Modules
On this page, we will discuss primarily modules creation. If you want to contribute to the official project repository, make sure you follow the instructions in the development section first.
Modules are namespace packages in the cryton.modules
namespace.
Once Worker receives an execution request, it:
- imports the specified module (in case it exists)
- validates the input using JSON Schema 2020-12 (specified in the
Module.SCHEMA
parameter) - checks requirements (using the
Module.check_requirements
method) - runs the module with the supplied arguments (using the
Module.execute
method) - saves the module output
In order to achieve this, there are some rules:
- module is a namespace package
- the namespace package contains
module.py
file - the
module.py
file contains class calledModule
- the
Module
class inherits fromcryton.lib.utility.module.ModuleBase
- the
Module
class implements all abstract methods from theModuleBase
- the
Module
class overrides theSCHEMA
class variable (see JSON Schema) - the
Module
class overrides theModuleBase.execute
method - it is the entry point for running the module
Creating a new module¶
Let's say we want to create a module that just prints and returns Hello World!
. In case the user specifies a name
parameter, it will use it instead.
Want to create your own project with modules?
In case you want to keep your modules private, or version the modules yourself, you can create your own repository and install them later as mentioned here.
Projects and Python packages should follow the convention of having the cryton-modules-
prefix.
We will be using Poetry for this example.
Create new poetry project for a module called hello_world
for the cryton.modules
namespace:
poetry new --name cryton.modules.hello_world cryton-modules-my-collection
Go into the project directory:
cd cryton-modules-my-collection
Add Cryton (with worker extras) as a dependency.
poetry add "cryton[worker]>=2"
You're all set. Follow the rest of the guide, but don't forget you already have the module directory (Python package).
In the directory cryton/modules/
create a new Python package (directory with __init__.py
file) and give it appropriate name (hello_world
in our case).
mkdir cryton/modules/hello_world
touch cryton/modules/hello_world/__init__.py
Now create module.py
file in the new directory.
touch cryton/modules/hello_world/module.py
We should have the following structure:
├── cryton
│ ├── modules
│ │ ├── hello_world
│ │ │ ├── __init__.py
│ │ │ ├── module.py
Copy the following code into the cryton/modules/hello_world/module.py
file:
from cryton.lib.utility.module import ModuleBase, ModuleOutput, Result
# Module implementation
class Module(ModuleBase):
# The SCHEMA variable is used for input arguments validation (it uses JSON Schema)
_SCHEMA = {
"type": "object",
"description": "Arguments for the `hello_world` module.",
"properties": {
"name": {"type": "string", "minLength": 1, "description": "Name used in the greeting."}
},
"additionalProperties": False
}
# In case our module has any system requirements, we can check for them here
def check_requirements(self) -> None:
pass
# This is the entrypoint to the module execution.
# Write the code you want to run here. Also add parsing and evaluation of results
def execute(self) -> ModuleOutput:
# Arguments can be accessed using the `self._arguments` parameter
name = self._arguments.get("name", "World")
to_print = f"Hello {name}!"
self._data.result = Result.OK # The module finished successfully
self._data.output = to_print # Output we want to send
return self._data
You just created a module that checks if the input parameter name
is a string and has at least one character, in case it is defined. Once the module is executed it returns the greeting.
Tip
- For more information, see the implementation of the
cryton.lib.utility.module.ModuleBase
class - Since the modules are installed alongside Cryton, they have access to its features/code (Metasploit, Empire, ...)
- Do not forget to add tests
- Feel free to check other modules for inspiration