1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
|
"""
The module `core.module` defines the `Module` class.
The `Module` class has to be inherited to create a new weevely module.
Normally, the following methods have to be overridden:
* `init()`: This defines the basic module initialization. The `init()` method normally calls `register_info()`, `register_vectors()` and `register_arguments()`.
* `check()`: This is called at the first run. Check and set the module status.
* `run()`: This function is called on module run.
"""
import argparse
import shlex
import utils
from core import argparsers
from core import messages
from core import modules
from core.loggers import log
from core.vectorlist import VectorList
from core.weexceptions import DevException, ArgparseError
class Formatter(argparse.ArgumentDefaultsHelpFormatter):
def _format_description(self, desc):
lines = desc.split('\n')
head = lines[0]
n = len(head)
return head + '\n' + '=' * n + '\n' + '\n'.join([' ' + l for l in lines[1:]]) + '\n'
def add_text(self, text):
if text is not argparse.SUPPRESS and text is not None:
self._add_item(self._format_description, [text])
class Status:
"""Represent the module statuses.
Is stored in session[module][status] and is set by `setup()` call at the first run.
* Status.IDLE: The module is inactive. This state is set if the module has been never been setup, of if it needs a new setup. If a module is run in this state, the `Module.setup()` function is automatically called.
* Status.RUN: The module is properly running and can be call.
* Status.FAIL: The module setup failed. The execution of this module is automatically skipped.
"""
IDLE = 0
RUN = 1
FAIL = 2
class Module:
aliases = []
def __init__(self, session, name, folder):
"""Module object constructor.
This call the overridable `init()` method.
Normally does not need to be overridden.
"""
self.session = session
self.name = name
self.folder = folder
self.vectors = VectorList(session, name)
# init session db for current session
if name not in self.session:
self.session[self.name] = {
'stored_args': {},
'results': {},
'status': Status.IDLE
}
# HelpParser is a slightly changed `ArgumentParser`
self.argparser = argparsers.HelpParser(
prog=self.name,
description=self.__doc__,
formatter_class=Formatter,
)
# Arguments dictionary is initially empty
self.args = {}
self.init()
def run_cmdline(self, line, cmd = ''):
"""Execute the module from command line.
Get command line string as argument. Called from terminal.
Normally does not need to be overridden.
Args:
line (str): the module arguments.
cmd (str): the executed command
Return:
Object. The result of the module execution.
"""
# Split the command line
try:
command = shlex.split(line)
except Exception as e:
import traceback; log.debug(traceback.format_exc())
log.warn(messages.generic.error_parsing_command_s % str(e))
return
# Execute the command, catching Ctrl-c, Ctrl-d, argparse exit,
# and other exceptions
try:
result = self.run_argv(command)
except (KeyboardInterrupt, EOFError):
log.info(messages.module.module_s_exec_terminated % self.name)
return
except ArgparseError:
return
except Exception as e:
import traceback; log.debug(traceback.format_exc())
log.warn(messages.module.error_module_exec_error_s % str(e))
return
self.print_result(
result[:-1] if (
isinstance(result, str) and
result.endswith('\n')
) else result
)
# Data is returned for the testing of _cmdline calls
return result
def run_argv(self, argv, catch_errors=True):
"""Execute the module.
Get arguments list as argument. The arguments are parsed with getopt,
and validated. Then calls setup() and run() of module.
Normally does not need to be overridden.
Args:
argv (list of str): The list of arguments.
catch_errors (bool): Whether to catch remote errors or not. Passed to the base channel
Returns:
Object. The result of the module execution.
"""
# Merge stored arguments with line arguments
stored_args = self.session[self.name]['stored_args']
self.args = {}
try:
user_args = self.argparser.parse_args(argv)
except SystemExit as e:
raise ArgparseError(e)
# The new arg must win over the stored one if:
# new arg is not none and the value of the old one
# is not just the default value
for newarg_key, newarg_value in user_args.__dict__.items():
# Pick the default argument of the current arg
default_value = next((action.default for action in self.argparser._actions if action.dest == newarg_key), None)
stored_value = stored_args.get(newarg_key)
if newarg_value != None and newarg_value != default_value:
self.args[newarg_key] = newarg_value
elif stored_value != None:
self.args[newarg_key] = stored_value
else:
self.args[newarg_key] = default_value
# If module status is IDLE, launch setup()
if self.session[self.name]['status'] == Status.IDLE:
self.session[self.name]['status'] = self.setup()
# If setup still not set the status to RUN, return
if self.session[self.name]['status'] != Status.RUN:
return
# If module status is FAIL, return
if self.session[self.name]['status'] == Status.FAIL:
log.debug(messages.module.module_s_inactive % self.name)
return
# Setup() could has been stored additional args, so all the updated
# stored arguments are applied to args
stored_args = self.session[self.name]['stored_args']
for stored_arg_key, stored_arg_value in stored_args.items():
if stored_arg_key != None and stored_arg_value != self.args.get(stored_arg_key):
self.args[stored_arg_key] = stored_arg_value
return self.run(catch_errors=catch_errors)
def run_alias(self, args, cmd):
"""Execute the module to replace a missing terminal command.
This runs the module if the direct shell command can't
be run due to the shell_sh failing.
It is called when some alias defined in `Module.alias` list
is executed from the command line.
Normally does not need to be overridden.
Args:
args (str): string containing the module arguments.
Return:
Object. The result of the module execution.
"""
if self.session['default_shell'] != 'shell_sh':
log.debug(messages.module.running_the_alias_s % self.name)
return self.run_cmdline(args)
else:
modules.loaded['shell_sh'].run_cmdline(
'%s -- %s' % (cmd, args)
)
def init(self):
"""Module initialization.
Called at boot.
Must be overriden to set the basic Module data.
This normally calls `register_info()`, `register_vectors()` and `register_arguments()`.
"""
raise DevException(messages.module.error_init_method_required)
def setup(self):
"""Module first setup.
Called at the first module run per session.
Override this to implement the module setup.
This should perform the basic check of the module compatibility
with the remote enviroinment, and return the module Status value.
Current execution arguments are in self.args.
If not overridden, always returns Status.RUN.
Returns:
Status value. Must be Status.RUN, Status.FAIL, or Status.IDLE.
"""
return Status.RUN
def run(self, catch_errors=True):
"""Module execution.
Called at every module executions.
Override this to implement the module behaviour.
Current execution arguments are in self.args.
Returns:
Object containing the execution result.
"""
return
def help(self):
"""Function called on terminal help command.
This is binded with the terminal `help_module()` method.
Normally does not need to be overridden.
"""
self.run_argv([ '-h' ])
def register_info(self, info):
"""Register the module basic information.
The human-readable description is automatically read from the object
docstring. With no description set, raise an exception.
Arbitrary fields can be used.
Args:
info (dict): Module information.
Raises:
DevException: Missing description
"""
self.info = info
self.info['description'] = (
info.get('description')
if info.get('description')
else self.__doc__.strip()
)
self.argparser.description = self.info.get('description')
if not self.argparser.description:
raise DevException(messages.module.error_module_missing_description)
def register_arguments(self, arguments = []):
"""Register the module arguments.
Register arguments to be added to the argparse parser.
Args:
arguments (list of dict): List of dictionaries in the form
`[{ 'name' : 'arg1', 'opt' : '', .. }, {'name' : 'arg2', 'opt' : '', .. }]`
to be passed to the `ArgumentParser.add_argument()` method.
"""
try:
for arg_opts in arguments:
# Handle if the argument registration is done before
# The vector registration. This should at least warn
if arg_opts.get('choices') == []:
log.warn(messages.module.error_choices_s_s_empty % (self.name,
arg_name))
self.argparser.add_argument(
arg_opts['name'],
**dict((k, v) for k, v in arg_opts.items() if k != 'name')
)
except Exception as e:
raise DevException(messages.module.error_setting_arguments_s % (e))
def register_vectors(self, vectors):
"""Register the module vectors.
The passed vectors are stored in `self.vectors`, a VectorList object.
Args:
vectors (list of vectors objects): List of ShellCmd, PhpCode, and
ModuleExec to use as module vectors.
"""
self.vectors.extend(vectors)
def print_result(self, result, header=False):
"""Format and print execution result.
Called at every executions from command line.
Override this to implement a different result print format.
Args:
result (Object): The result to format and print.
:param result:
:param header:
"""
if result not in (None, ''):
log.info(utils.prettify.tablify(result, header=header))
def _store_result(self, field, value):
"""Store persistent module result.
Store data in the module session structure as result.
Args:
field (string): The key name to label the result.
value (obj): The result to store.
"""
self.session[self.name]['results'][field] = value
def _get_stored_result(self, field, module = None, default=None):
"""Get stored module result.
Get the modle result stored in the session structure.
Args:
field (string): The key name which contains the result.
module (string): The module name. If not set, the current
module is used.
default: The value to be returned in case key does not exist.
"""
if module is not None:
return self.session[module][
'results'].get(field, default)
else:
return self.session.get(field, default)
|