Integrating an external command
... or how to turn a command that you use daily into an overpowered machine.
Creating a task file
Imagine we have a tool named mytool
that we want to integrate with secator
.
Start by creating a file named mytool.py
:
from secator.decorators import task # required for `secator` to recognize tasks
from secator.runners import Command # the `secator` runner to use
@task()
class mytool(Command): # make sure class name is lowercase and matches the filename.
cmd = 'mytool' # ... or whatever the name of your external command is.
Move this file over to:
~/.secator/templates/
(or whatever yourdirs.templates
in Configuration points to)
OR
secator/tasks/
if you have a Development setup and want to contribute your task implementation to the officialsecator
repository.
Adding an input flag [optional]
If your tool requires an input flag or a list flag to take its targets, for instance:
mytool -u TARGET
mytool -l TXT_FILE
You need to set the input_flag
and file_flag
class options:
from secator.decorators import task
from secator.runners import Command
@task()
class mytool(Command):
cmd = 'mytool'
input_flag = '-u'
file_flag = '-l'
Setting these attributes allows us to run mytool
with secator
like:
secator x mytool TARGET # will run mytool -u TARGET
secator x mytool TXT_FILE # will run mytool -l TXT_FILE
Parsing a command's output
Now that you have a basic implementation working, you need to convert your command's output into structured output (JSON).
Find out what your command's output looks like and pick the corresponding guide:
Read Parsing JSON lines if your tool has an option to stream JSON lines (preferred).
Read Parsing output files if your tool has an option to output to a file (e.g JSON or CSV).
Read Parsing raw standard output if your tools only outputs to
stdout
.
Adding more options [optional]
To support more options, you can use the opt_prefix
, opts
, opt_key_map
and opt_value_map
attributes.
Assuming mytool
has the --delay
, --debug
and --include-tags
options, we would support them this way:
@task()
class mytool(Command):
# ...
opt_prefix = '--' # default is '-'
opts = {
'tags': {'type': str, 'short': 't', 'help': 'Tags'},
'delay': {'type': int, 'short': 'dbg', 'help': 'Delay'},
'debug': {'is_flag': True, 'short': 'd', 'help': 'Debug mode'},
}
opt_key_map = { # to map input options
'tags': 'include-tags',
}
opt_value_map = { # to transform options values
'delay': lambda x: x * 1000 # convert seconds to milliseconds
}
With this config, running either of:
secator x mytool --tags tag1,tag2 --debug --delay 5 TARGET # long option format
secator x mytool -t tag1,tag2 -dbg -d 5 TARGET # short option format
will result in running mytool
like:
mytool --include-tags tag1,tag2 --debug --delay 5000 -u TARGET
Adding an install command [optional]
To support installing your tool with secator, you can set the install_cmd
, and / or install_github_handle
attributes:
@task()
class mytool(Command):
# ...
install_cmd = "sudo apt install -y mytool"
install_github_handle = "myorg/mytool"
Now you can install mytool
using:
secator install tools mytool
Using a category [optional]
If your tool fits into one of secator
's built-in command categories, you can inherit from it's option set:
Http
: A tool that makes HTTP requests.HttpCrawler
: A command that crawls URLs (subset ofHttp
).HttpFuzzer
: A command that fuzzes URLs (subset ofHttp
).
You can inherit from these categories and map their options to your command.
For instance, if mytool
is an HTTP fuzzer, we would change it's implementation like:
from secator.tasks._categories import HTTPFuzzer
from secator.definitions import OPT_NOT_SUPPORTED
@task()
class mytool(Command, HTTPFuzzer):
# ...
opt_key_map = {
# HTTPFuzzer options mapping
HEADER: 'header',
DELAY: 'delay',
DEPTH: OPT_NOT_SUPPORTED,
FILTER_CODES: OPT_NOT_SUPPORTED,
FILTER_REGEX: OPT_NOT_SUPPORTED,
FILTER_SIZE: OPT_NOT_SUPPORTED,
FILTER_WORDS: OPT_NOT_SUPPORTED,
FOLLOW_REDIRECT: OPT_NOT_SUPPORTED,
MATCH_CODES: OPT_NOT_SUPPORTED,
MATCH_REGEX: OPT_NOT_SUPPORTED,
MATCH_SIZE: OPT_NOT_SUPPORTED,
MATCH_WORDS: OPT_NOT_SUPPORTED,
METHOD: OPT_NOT_SUPPORTED,
PROXY: OPT_NOT_SUPPORTED,
RATE_LIMIT: OPT_NOT_SUPPORTED,
RETRIES: OPT_NOT_SUPPORTED,
THREADS: OPT_NOT_SUPPORTED,
TIMEOUT: 'timeout',
USER_AGENT: 'user-agent',
# my tool specific options
'tags': 'include-tags',
}
opt_value_map = {
'delay': lambda x: x * 1000 # convert seconds to milliseconds
}
With this config, running:
secator x mytool --help
would list:
The
meta
options in theHTTPFuzzer
category that are supported bymytool
.The options only usable by
mytool
.
For instance, running:
secator x mytool \
-header "Authorization: Bearer MYTOKEN" \
-delay 1 \
-ua "secator/0.6 (Debian)" \
-timeout 5 \
-t tag1,tag2 \
TARGET
would result in running mytool
like:
mytool \
--header "Authorization: Bearer MYTOKEN" \
--delay 1000 \
--user-agent "secator/0.6 (Debian)" \
--timeout 5 \
--include-tags tag1,tag2
-u TARGET
Supporting proxies [optional]
If your tool supports proxies, secator
has first-class support for proxychains
, HTTP
and SOCKS5
proxies, and can dynamically choose the type of proxy to use based on the following attributes:
proxy_socks5
: boolean indicating if your command supportsSOCKS5
proxies.proxy_http
: boolean indicating if your command supportsHTTP
/HTTPS
proxies.proxychains
: boolean indicating if your command supports being run withproxychains
.
If your proxy supports SOCKS5
or HTTP
proxies, make sure to have an option called proxy
in your opts
definition or it won't be picked up.
If your proxy supports proxychains
, secator
will use the local proxychains
binary and proxychains.conf
configuration, so make sure those are functional.
Example:
Assuming mytool
does not support HTTP or SOCKS5 proxies, but works with proxychains
, you can update your task definition like:
@task()
class mytool(Command):
# ...
proxychains = True
proxy_socks5 = False
proxy_http = True
With the above configuration, running with -proxy <VALUE>
would result in the following behaviour:
secator x mytool -proxy proxychains TARGET
secator x mytool -proxy auto TARGET # auto-pick from proxychains > socks5 > http
becomes:
proxychains mytool -u TARGET
Hooking onto runner lifecycle
You can hook onto any part of the runner lifecycle by override the hooks methods (read Lifecyle hooks to know more).
Example:
@task()
class mytool(Command):
# ...
@staticmethod
def on_line(self, line):
return line.rstrip(',') # strip trailing comma of stdout lines
@staticmethod
def on_item_pre_convert(self, item):
item['extra_data'] = {
'version': '2.0' # add extra data to items
}
return item
Chunking
secator
allows to chunk a task into multiple children tasks when the length of the input grows, or some other specific requirements (e.g: your command only takes one target at a time).
Chunking only works when Distributed runs with Celery are enabled.
You can specify the chunk size using the input_chunk_size
attribute:
@task()
class mytool(Command):
# ...
input_chunk_size = 10 # additional tasks will be spawned every 10 targets
With this config, running:
secator x mytool tasks.txt # tasks.txt contains 20 targets
would result in:
mytool -l /tmp/task_0_9.txt
mytool -l /tmp/task_10_19.txt
Last updated
Was this helpful?