mirror of
https://github.com/calebstewart/pwncat.git
synced 2024-11-27 19:04:15 +01:00
bcb1f77606
- Added some last touches to module system. - Modified connect syntax to allow more flexible parameters - Still need to update enumeration API docs and connect command docs
136 lines
7.1 KiB
ReStructuredText
136 lines
7.1 KiB
ReStructuredText
Modules
|
|
=======
|
|
|
|
``pwncat`` is extended primarily by implementing modules. This concept is similar in theory to Metasploit modules. Modules are organized by their purpose, so modules for persistence are under the ``persist`` package while modules for enumeration are under the ``enumerate`` package.
|
|
|
|
At their core, modules are simply classes which implement a ``run`` function to perform some task. Modules can have arguments which are passed as normal keyword arguments to the ``run`` method. Argument names, types and documentation is stored in the class property ``ARGUMENTS`` which is a dictionary mapping argument names to ``Argument`` instances.
|
|
|
|
When a user executes the following commands at the local prompt:
|
|
|
|
.. code-block::
|
|
|
|
use persist.cron_reverse
|
|
set lhost 10.0.0.1
|
|
set lport 4444
|
|
run
|
|
|
|
``pwncat`` will first lookup the Python module ``pwncat.modules.persist.cron_reverse``. This module must implement a class named ``Module`` which inherits from the ``BaseModule`` class. Next, each ``set`` call will cross-reference the parameter name with the module arguments and ensure type-checking is performed. Lastly, the ``run`` method will be executed.
|
|
|
|
Return values from modules are displayed in two ways. First, if the return value conforms to the ``Result`` interface, it is formatted and displayed with a title and description under the appropriate category. Otherwise, a list of uncategorized results will be displayed. If no return value is found, a simple "Module completed successfully" message will be displayed.
|
|
|
|
Creating Base Modules
|
|
---------------------
|
|
|
|
Specilized categories such as ``enumerate``, ``persist``, and ``escalate`` have their own module base classes which all inherit from the ``BaseModule`` class. If you'd like to create a generic module which does not fit into these categories, you can subclass the ``BaseModule`` class itself.
|
|
|
|
A basic module is placed anywhere under the ``pwncat/modules/`` package. It will be automatically loaded upon opening ``pwncat``. A basic module named "random_string.py" could be placed under "pwncat/modules" and may look like this:
|
|
|
|
.. code-block:: python
|
|
|
|
class Module(BaseModule):
|
|
"""
|
|
Module documentation. This is shown with the `info` command.
|
|
"""
|
|
|
|
ARGUMENTS = {
|
|
"length": Argument(type=int, default=10, help="How long to make the string"),
|
|
"alphabet": Argument(type=str, default=string.ascii_printable, help="The characters to choose from")
|
|
}
|
|
|
|
def run(self, length, alphabet):
|
|
return "".join([random.choice(alphabet) for _ in range(length)])
|
|
|
|
This module simply generates a random string of a given length and can be executed in a few different ways at the pwncat prompt:
|
|
|
|
.. code-block::
|
|
|
|
use random_string
|
|
set length 5
|
|
set alphabet 0123456789abcdef
|
|
run
|
|
# Or, more succinctly
|
|
run random_string length=5 alphabet=0123456789abcdef
|
|
|
|
This module can also be used from within ``pwncat`` by using the ``pwncat.modules`` helper functions to locate and run modules by name:
|
|
|
|
.. code-block:: python
|
|
|
|
import pwncat.modules
|
|
result = pwncat.modules.run("random_string", length=5, alphabet="0123456789abcdef")
|
|
result = list(pwncat.modules.match("random_.*"))[0].run(length=2, alphabet="hello")
|
|
result = pwncat.modules.find("random_string").run(length=2, alphabet="hello")
|
|
|
|
Module Results
|
|
--------------
|
|
|
|
Module `run` methods should return result objects which are compatible with the `pwncat.modules.Result` object. How those values are returned can happen in a few ways. For simple modules, the `run` method can simply return the result with the `return` statement. In this case, no progress bar will be created and until the module finishes there will be no status output. Alternatively, if the `run` method is a generator, a progress bar is automatically created using `rich` and the status is updated with each of the `yield`'d values. The results should always have a `__str__` operator defined so that status results can be printed properly. The result of the `run` method will never be a generator when called externally, however. The base module class wraps the subclass `run` method, creates a progress bar, collects the output and returns an interable object containing all of the results.
|
|
|
|
If a module would like to update the current progress status without returning any data, it can do so using the `pwncat.modules.Status` type. If an object of this type is `yield`'d, it will not be added to the resulting return value of `run`, and will only update the progress bar. The `Status` class is simply a subclass of `str`, therefore issuing `yield Status("new module status")` will update the progress bar accordingly.
|
|
|
|
If you need to implement a custom result class to encapsulate your results, you can do so either by directly inheriting from the `Result` class or by simply implementing the required methods. The only strictly required methods are either the `title` or `__str__` methods. By default, the `title` method will simply return `str(self)`, so overriding `__str__` is normally enough. This controls the single-line output of this result on the terminal.
|
|
|
|
For objects which require larger output, you can utilize the `description` method. This method returns a string with a long-description of your object. For example, the `private_key` enumeration data implements this method to show the entire content of the private key while the title just indicates that it *is* a private key and where it was found.
|
|
|
|
Lastly, there is a `category` method which specifies how to categorize this result. The affects how the data is separated and displayed when run from the prompt.
|
|
|
|
Other methods or properties can be added at will to this object. The above methods are not meant to obstruct the programmatic use of the data returned by a module so that you can organically return results from modules while still having properly formatted output from the prompt.
|
|
|
|
Recursively Calling Modules
|
|
---------------------------
|
|
|
|
If your new module needs to call another module (through any of the interfaces above), you should be sure to pass the current progress bar down to the sub-modules. This is done like so:
|
|
|
|
.. code-block:: python
|
|
|
|
class Module(BaseModule):
|
|
def run(self):
|
|
result = pwncat.modules.run("some.other.module", progress=self.progress)
|
|
|
|
This ensures that multiple progress bars are not created and fighting over the terminal lock.
|
|
|
|
Exceptions
|
|
----------
|
|
|
|
.. autoclass:: pwncat.modules.ModuleNotFound
|
|
|
|
.. autoclass:: pwncat.modules.ArgumentFormatError
|
|
|
|
.. autoclass:: pwncat.modules.MissingArgument
|
|
|
|
.. autoclass:: pwncat.modules.InvalidArgument
|
|
|
|
.. autoclass:: pwncat.modules.ModuleFailed
|
|
|
|
Module Helper Classes
|
|
---------------------
|
|
|
|
.. autoclass:: pwncat.modules.Argument
|
|
:members:
|
|
|
|
.. autofunction:: pwncat.modules.List
|
|
|
|
.. autofunction:: pwncat.modules.Bool
|
|
|
|
.. autoclass:: pwncat.modules.Status
|
|
|
|
.. autoclass:: pwncat.modules.Result
|
|
:members:
|
|
|
|
Locating and Using Modules
|
|
--------------------------
|
|
|
|
.. autofunction:: pwncat.modules.reload
|
|
|
|
.. autofunction:: pwncat.modules.find
|
|
|
|
.. autofunction:: pwncat.modules.match
|
|
|
|
.. autofunction:: pwncat.modules.run
|
|
|
|
Base Module Class
|
|
-----------------
|
|
|
|
.. autoclass:: pwncat.modules.BaseModule
|
|
:members:
|
|
:undoc-members:
|