secator 0.13.0__py3-none-any.whl → 0.15.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of secator might be problematic. Click here for more details.
- secator/cli.py +34 -10
- secator/configs/profiles/aggressive.yaml +6 -5
- secator/configs/profiles/default.yaml +6 -7
- secator/configs/profiles/insane.yaml +8 -0
- secator/configs/profiles/paranoid.yaml +8 -0
- secator/configs/profiles/polite.yaml +8 -0
- secator/configs/profiles/sneaky.yaml +8 -0
- secator/configs/profiles/tor.yaml +5 -0
- secator/decorators.py +17 -10
- secator/definitions.py +5 -0
- secator/installer.py +10 -3
- secator/output_types/stat.py +3 -0
- secator/report.py +2 -2
- secator/runners/_base.py +32 -2
- secator/runners/command.py +2 -0
- secator/runners/scan.py +1 -0
- secator/runners/task.py +1 -0
- secator/tasks/_categories.py +11 -11
- secator/tasks/arjun.py +2 -1
- secator/tasks/bbot.py +3 -0
- secator/tasks/bup.py +2 -1
- secator/tasks/cariddi.py +2 -1
- secator/tasks/dalfox.py +2 -1
- secator/tasks/dirsearch.py +3 -1
- secator/tasks/dnsx.py +3 -1
- secator/tasks/dnsxbrute.py +2 -0
- secator/tasks/feroxbuster.py +3 -1
- secator/tasks/ffuf.py +3 -1
- secator/tasks/fping.py +3 -1
- secator/tasks/gau.py +3 -1
- secator/tasks/gf.py +2 -1
- secator/tasks/gitleaks.py +3 -1
- secator/tasks/gospider.py +2 -0
- secator/tasks/grype.py +3 -1
- secator/tasks/h8mail.py +2 -1
- secator/tasks/httpx.py +3 -1
- secator/tasks/katana.py +2 -0
- secator/tasks/maigret.py +3 -1
- secator/tasks/mapcidr.py +2 -1
- secator/tasks/msfconsole.py +4 -3
- secator/tasks/naabu.py +3 -1
- secator/tasks/nmap.py +2 -0
- secator/tasks/nuclei.py +3 -1
- secator/tasks/searchsploit.py +3 -1
- secator/tasks/subfinder.py +3 -1
- secator/tasks/testssl.py +2 -1
- secator/tasks/trivy.py +4 -1
- secator/tasks/wafw00f.py +2 -1
- secator/tasks/wpprobe.py +2 -1
- secator/tasks/wpscan.py +2 -1
- secator/template.py +1 -1
- secator/utils.py +15 -11
- secator/utils_test.py +9 -3
- {secator-0.13.0.dist-info → secator-0.15.0.dist-info}/METADATA +10 -3
- {secator-0.13.0.dist-info → secator-0.15.0.dist-info}/RECORD +58 -55
- secator/configs/profiles/stealth.yaml +0 -7
- secator/configs/workflows/port_scan.yaml +0 -39
- {secator-0.13.0.dist-info → secator-0.15.0.dist-info}/WHEEL +0 -0
- {secator-0.13.0.dist-info → secator-0.15.0.dist-info}/entry_points.txt +0 -0
- {secator-0.13.0.dist-info → secator-0.15.0.dist-info}/licenses/LICENSE +0 -0
secator/cli.py
CHANGED
|
@@ -36,6 +36,7 @@ click.rich_click.USE_RICH_MARKUP = True
|
|
|
36
36
|
ALL_TASKS = discover_tasks()
|
|
37
37
|
ALL_WORKFLOWS = [t for t in TEMPLATES if t.type == 'workflow']
|
|
38
38
|
ALL_SCANS = [t for t in TEMPLATES if t.type == 'scan']
|
|
39
|
+
ALL_PROFILES = [t for t in TEMPLATES if t.type == 'profile']
|
|
39
40
|
FINDING_TYPES_LOWER = [c.__name__.lower() for c in FINDING_TYPES]
|
|
40
41
|
CONTEXT_SETTINGS = dict(help_option_names=['-h', '-help', '--help'])
|
|
41
42
|
|
|
@@ -125,6 +126,25 @@ for config in sorted(ALL_SCANS, key=lambda x: x['name']):
|
|
|
125
126
|
register_runner(scan, config)
|
|
126
127
|
|
|
127
128
|
|
|
129
|
+
@cli.group(aliases=['p'])
|
|
130
|
+
@click.pass_context
|
|
131
|
+
def profile(ctx):
|
|
132
|
+
"""Show profiles"""
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@profile.command('list')
|
|
137
|
+
def profile_list():
|
|
138
|
+
table = Table()
|
|
139
|
+
table.add_column("Profile name", style="bold gold3")
|
|
140
|
+
table.add_column("Description", overflow='fold')
|
|
141
|
+
table.add_column("Options", overflow='fold')
|
|
142
|
+
for profile in ALL_PROFILES:
|
|
143
|
+
opts_str = ','.join(f'{k}={v}' for k, v in profile.opts.items())
|
|
144
|
+
table.add_row(profile.name, profile.description, opts_str)
|
|
145
|
+
console.print(table)
|
|
146
|
+
|
|
147
|
+
|
|
128
148
|
#--------#
|
|
129
149
|
# WORKER #
|
|
130
150
|
#--------#
|
|
@@ -685,7 +705,7 @@ def report_show(report_query, output, runner_type, time_delta, type, query, work
|
|
|
685
705
|
all_results.extend(runner.results)
|
|
686
706
|
continue
|
|
687
707
|
report = Report(runner, title=f"Consolidated report - {current}", exporters=exporters)
|
|
688
|
-
report.build(extractors=extractors if not unified else [])
|
|
708
|
+
report.build(extractors=extractors if not unified else [], dedupe=unified)
|
|
689
709
|
file_date = get_file_date(path)
|
|
690
710
|
runner_name = data['info']['name']
|
|
691
711
|
console.print(
|
|
@@ -763,7 +783,7 @@ def report_list(workspace, runner_type, time_delta):
|
|
|
763
783
|
@report.command('export')
|
|
764
784
|
@click.argument('json_path', type=str)
|
|
765
785
|
@click.option('--output-folder', '-of', type=str)
|
|
766
|
-
@click.option('-output', '-o', type=str)
|
|
786
|
+
@click.option('-output', '-o', type=str, required=True)
|
|
767
787
|
def report_export(json_path, output_folder, output):
|
|
768
788
|
with open(json_path, 'r') as f:
|
|
769
789
|
data = loads_dataclass(f.read())
|
|
@@ -1423,6 +1443,16 @@ def task(name, verbose, check):
|
|
|
1423
1443
|
task = task[0]
|
|
1424
1444
|
task_name = task.__name__
|
|
1425
1445
|
|
|
1446
|
+
# Check task command is set
|
|
1447
|
+
check_test(
|
|
1448
|
+
task.cmd,
|
|
1449
|
+
'Check task command is set (cls.cmd)',
|
|
1450
|
+
'Task has no cmd attribute.',
|
|
1451
|
+
errors
|
|
1452
|
+
)
|
|
1453
|
+
if errors:
|
|
1454
|
+
sys.exit(0)
|
|
1455
|
+
|
|
1426
1456
|
# Run install
|
|
1427
1457
|
cmd = f'secator install tools {task_name}'
|
|
1428
1458
|
ret_code = Command.execute(cmd, name='install', quiet=not verbose, cwd=ROOT_FOLDER)
|
|
@@ -1437,7 +1467,7 @@ def task(name, verbose, check):
|
|
|
1437
1467
|
errors
|
|
1438
1468
|
)
|
|
1439
1469
|
check_test(
|
|
1440
|
-
any(cmd for cmd in [task.install_cmd, task.install_github_handle]),
|
|
1470
|
+
any(cmd for cmd in [task.install_pre, task.install_cmd, task.install_github_handle]),
|
|
1441
1471
|
'Check task installation command is defined',
|
|
1442
1472
|
'Task has no installation command. Please define one or more of the following class attributes: `install_pre`, `install_cmd`, `install_post`, `install_github_handle`.', # noqa: E501
|
|
1443
1473
|
errors
|
|
@@ -1472,13 +1502,7 @@ def task(name, verbose, check):
|
|
|
1472
1502
|
errors
|
|
1473
1503
|
)
|
|
1474
1504
|
check_test(
|
|
1475
|
-
task.
|
|
1476
|
-
'Check task command is set (cls.cmd)',
|
|
1477
|
-
'Task has no cmd attribute.',
|
|
1478
|
-
errors
|
|
1479
|
-
)
|
|
1480
|
-
check_test(
|
|
1481
|
-
task.input_type,
|
|
1505
|
+
task.input_types,
|
|
1482
1506
|
'Check task input type is set (cls.input_type)',
|
|
1483
1507
|
'Task has no input_type attribute.',
|
|
1484
1508
|
warnings,
|
secator/decorators.py
CHANGED
|
@@ -13,6 +13,7 @@ from secator.utils import (deduplicate, expand_input, get_command_category)
|
|
|
13
13
|
|
|
14
14
|
RUNNER_OPTS = {
|
|
15
15
|
'output': {'type': str, 'default': None, 'help': 'Output options (-o table,json,csv,gdrive)', 'short': 'o'},
|
|
16
|
+
'profiles': {'type': str, 'default': 'default', 'help': 'Profiles', 'short': 'pf'},
|
|
16
17
|
'workspace': {'type': str, 'default': 'default', 'help': 'Workspace', 'short': 'ws'},
|
|
17
18
|
'print_json': {'is_flag': True, 'short': 'json', 'default': False, 'help': 'Print items as JSON lines'},
|
|
18
19
|
'print_raw': {'is_flag': True, 'short': 'raw', 'default': False, 'help': 'Print items in raw format'},
|
|
@@ -270,7 +271,6 @@ def generate_cli_subcommand(cli_endpoint, func, **opts):
|
|
|
270
271
|
def register_runner(cli_endpoint, config):
|
|
271
272
|
name = config.name
|
|
272
273
|
input_required = True
|
|
273
|
-
input_type = 'targets'
|
|
274
274
|
command_opts = {
|
|
275
275
|
'no_args_is_help': True,
|
|
276
276
|
'context_settings': {
|
|
@@ -281,37 +281,44 @@ def register_runner(cli_endpoint, config):
|
|
|
281
281
|
|
|
282
282
|
if cli_endpoint.name == 'scan':
|
|
283
283
|
runner_cls = Scan
|
|
284
|
+
input_required = False # allow targets from stdin
|
|
284
285
|
short_help = config.description or ''
|
|
285
286
|
short_help += f' [dim]alias: {config.alias}' if config.alias else ''
|
|
286
287
|
command_opts.update({
|
|
287
288
|
'name': name,
|
|
288
|
-
'short_help': short_help
|
|
289
|
+
'short_help': short_help,
|
|
290
|
+
'no_args_is_help': False
|
|
289
291
|
})
|
|
292
|
+
input_types = config.input_types
|
|
290
293
|
|
|
291
294
|
elif cli_endpoint.name == 'workflow':
|
|
292
295
|
runner_cls = Workflow
|
|
296
|
+
input_required = False # allow targets from stdin
|
|
293
297
|
short_help = config.description or ''
|
|
294
298
|
short_help = f'{short_help:<55} [dim](alias)[/][bold cyan] {config.alias}' if config.alias else ''
|
|
295
299
|
command_opts.update({
|
|
296
300
|
'name': name,
|
|
297
|
-
'short_help': short_help
|
|
301
|
+
'short_help': short_help,
|
|
302
|
+
'no_args_is_help': False
|
|
298
303
|
})
|
|
304
|
+
input_types = config.input_types
|
|
299
305
|
|
|
300
306
|
elif cli_endpoint.name == 'task':
|
|
301
307
|
runner_cls = Task
|
|
302
308
|
input_required = False # allow targets from stdin
|
|
303
309
|
task_cls = Task.get_task_class(config.name)
|
|
304
310
|
task_category = get_command_category(task_cls)
|
|
305
|
-
|
|
306
|
-
short_help = f'[magenta]{task_category:<15}[/]{task_cls.__doc__}'
|
|
311
|
+
short_help = f'[magenta]{task_category:<25}[/] {task_cls.__doc__}'
|
|
307
312
|
command_opts.update({
|
|
308
313
|
'name': name,
|
|
309
314
|
'short_help': short_help,
|
|
310
315
|
'no_args_is_help': False
|
|
311
316
|
})
|
|
317
|
+
input_types = task_cls.input_types
|
|
312
318
|
|
|
313
319
|
else:
|
|
314
320
|
raise ValueError(f"Unrecognized runner endpoint name {cli_endpoint.name}")
|
|
321
|
+
input_types_str = '|'.join(input_types) if input_types else 'targets'
|
|
315
322
|
options = get_command_options(config)
|
|
316
323
|
|
|
317
324
|
# TODO: maybe allow this in the future
|
|
@@ -323,7 +330,7 @@ def register_runner(cli_endpoint, config):
|
|
|
323
330
|
# for i in range(0, len(ctx.args), 2)
|
|
324
331
|
# }
|
|
325
332
|
|
|
326
|
-
@click.argument(
|
|
333
|
+
@click.argument(input_types_str, required=input_required)
|
|
327
334
|
@decorate_command_options(options)
|
|
328
335
|
@click.pass_context
|
|
329
336
|
def func(ctx, **opts):
|
|
@@ -352,7 +359,7 @@ def register_runner(cli_endpoint, config):
|
|
|
352
359
|
# opts.update(unknown_opts)
|
|
353
360
|
|
|
354
361
|
# Expand input
|
|
355
|
-
inputs = opts.pop(
|
|
362
|
+
inputs = opts.pop(input_types_str)
|
|
356
363
|
inputs = expand_input(inputs, ctx)
|
|
357
364
|
|
|
358
365
|
# Build hooks from driver name
|
|
@@ -419,10 +426,10 @@ def register_runner(cli_endpoint, config):
|
|
|
419
426
|
runner.run()
|
|
420
427
|
|
|
421
428
|
generate_cli_subcommand(cli_endpoint, func, **command_opts)
|
|
422
|
-
generate_rich_click_opt_groups(cli_endpoint, name,
|
|
429
|
+
generate_rich_click_opt_groups(cli_endpoint, name, input_types, options)
|
|
423
430
|
|
|
424
431
|
|
|
425
|
-
def generate_rich_click_opt_groups(cli_endpoint, name,
|
|
432
|
+
def generate_rich_click_opt_groups(cli_endpoint, name, input_types, options):
|
|
426
433
|
sortorder = {
|
|
427
434
|
'Execution': 0,
|
|
428
435
|
'Output': 1,
|
|
@@ -433,7 +440,7 @@ def generate_rich_click_opt_groups(cli_endpoint, name, input_type, options):
|
|
|
433
440
|
opt_group = [
|
|
434
441
|
{
|
|
435
442
|
'name': 'Targets',
|
|
436
|
-
'options':
|
|
443
|
+
'options': input_types,
|
|
437
444
|
},
|
|
438
445
|
]
|
|
439
446
|
for prefix in prefixes:
|
secator/definitions.py
CHANGED
|
@@ -40,6 +40,9 @@ AUTO_CALIBRATION = 'auto_calibration'
|
|
|
40
40
|
CONTENT_TYPE = 'content_type'
|
|
41
41
|
CONTENT_LENGTH = 'content_length'
|
|
42
42
|
CIDR_RANGE = 'cidr_range'
|
|
43
|
+
DOCKER_IMAGE = 'docker_image'
|
|
44
|
+
FILENAME = 'filename'
|
|
45
|
+
GIT_REPOSITORY = 'git_repository'
|
|
43
46
|
CPES = 'cpes'
|
|
44
47
|
CVES = 'cves'
|
|
45
48
|
DELAY = 'delay'
|
|
@@ -62,6 +65,7 @@ MATCH_CODES = 'match_codes'
|
|
|
62
65
|
MATCH_REGEX = 'match_regex'
|
|
63
66
|
MATCH_SIZE = 'match_size'
|
|
64
67
|
MATCH_WORDS = 'match_words'
|
|
68
|
+
ORG_NAME = 'org_name'
|
|
65
69
|
OUTPUT_PATH = 'output_path'
|
|
66
70
|
PATH = 'path'
|
|
67
71
|
PERCENT = 'percent'
|
|
@@ -100,6 +104,7 @@ REFERENCE = 'reference'
|
|
|
100
104
|
REFERENCES = 'references'
|
|
101
105
|
SEVERITY = 'severity'
|
|
102
106
|
TAGS = 'tags'
|
|
107
|
+
TECHNOLOGY = 'technology'
|
|
103
108
|
WEBSERVER = 'webserver'
|
|
104
109
|
WORDLIST = 'wordlist'
|
|
105
110
|
WORDS = 'words'
|
secator/installer.py
CHANGED
|
@@ -47,6 +47,7 @@ class InstallerStatus(Enum):
|
|
|
47
47
|
@dataclass
|
|
48
48
|
class Distribution:
|
|
49
49
|
name: str
|
|
50
|
+
system: str
|
|
50
51
|
pm_name: str
|
|
51
52
|
pm_installer: str
|
|
52
53
|
pm_finalizer: str
|
|
@@ -186,6 +187,8 @@ class SourceInstaller:
|
|
|
186
187
|
install_cmd = config
|
|
187
188
|
else:
|
|
188
189
|
distribution = get_distro_config()
|
|
190
|
+
if not distribution.pm_installer:
|
|
191
|
+
return InstallerStatus.UNKNOWN_DISTRIBUTION
|
|
189
192
|
for distros, command in config.items():
|
|
190
193
|
if distribution.name in distros.split("|") or distros == '*':
|
|
191
194
|
install_cmd = command
|
|
@@ -543,7 +546,7 @@ def get_distro_config():
|
|
|
543
546
|
distrib = system
|
|
544
547
|
|
|
545
548
|
if system == "Linux":
|
|
546
|
-
distrib = distro.
|
|
549
|
+
distrib = distro.like() or distro.id()
|
|
547
550
|
|
|
548
551
|
if distrib in ["ubuntu", "debian", "linuxmint", "popos", "kali"]:
|
|
549
552
|
installer = "apt install -y --no-install-recommends"
|
|
@@ -573,12 +576,16 @@ def get_distro_config():
|
|
|
573
576
|
else:
|
|
574
577
|
installer = "scoop" # Alternative package manager for Windows
|
|
575
578
|
|
|
576
|
-
|
|
579
|
+
if not installer:
|
|
580
|
+
console.print(Error(message=f'Could not find installer for your distribution (system: {system}, distrib: {distrib})')) # noqa: E501
|
|
581
|
+
|
|
582
|
+
manager = installer.split(' ')[0] if installer else ''
|
|
577
583
|
config = Distribution(
|
|
578
584
|
pm_installer=installer,
|
|
579
585
|
pm_finalizer=finalizer,
|
|
580
586
|
pm_name=manager,
|
|
581
|
-
name=distrib
|
|
587
|
+
name=distrib,
|
|
588
|
+
system=system
|
|
582
589
|
)
|
|
583
590
|
return config
|
|
584
591
|
|
secator/output_types/stat.py
CHANGED
|
@@ -25,6 +25,9 @@ class Stat(OutputType):
|
|
|
25
25
|
_table_fields = ['name', 'pid', 'cpu', 'memory']
|
|
26
26
|
_sort_by = ('name', 'pid')
|
|
27
27
|
|
|
28
|
+
def __str__(self) -> str:
|
|
29
|
+
return f'{self.name} [pid={self.pid}] [cpu={self.cpu:.2f}%] [memory={self.memory:.2f}%]'
|
|
30
|
+
|
|
28
31
|
def __repr__(self) -> str:
|
|
29
32
|
s = rf'[dim yellow3]📊 {self.name} \[pid={self.pid}] \[cpu={self.cpu:.2f}%] \[memory={self.memory:.2f}%]'
|
|
30
33
|
if self.net_conns:
|
secator/report.py
CHANGED
|
@@ -55,7 +55,7 @@ class Report:
|
|
|
55
55
|
f'{str(e)}[/]\n[dim]{traceback_as_string(e)}[/]',
|
|
56
56
|
)
|
|
57
57
|
|
|
58
|
-
def build(self, extractors=[], dedupe=
|
|
58
|
+
def build(self, extractors=[], dedupe=CONFIG.runners.remove_duplicates):
|
|
59
59
|
# Trim options
|
|
60
60
|
from secator.decorators import DEFAULT_CLI_OPTIONS
|
|
61
61
|
opts = merge_opts(self.runner.config.options, self.runner.run_opts)
|
|
@@ -97,7 +97,7 @@ class Report:
|
|
|
97
97
|
if items:
|
|
98
98
|
if sort_by and all(sort_by):
|
|
99
99
|
items = sorted(items, key=operator.attrgetter(*sort_by))
|
|
100
|
-
if dedupe
|
|
100
|
+
if dedupe:
|
|
101
101
|
items = remove_duplicates(items)
|
|
102
102
|
# items = [item for item in items if not item._duplicate and item not in dedupe_from]
|
|
103
103
|
for extractor in extractors:
|
secator/runners/_base.py
CHANGED
|
@@ -53,7 +53,7 @@ class Runner:
|
|
|
53
53
|
"""
|
|
54
54
|
|
|
55
55
|
# Input field (mostly for tests and CLI)
|
|
56
|
-
|
|
56
|
+
input_types = []
|
|
57
57
|
|
|
58
58
|
# Output types
|
|
59
59
|
output_types = []
|
|
@@ -138,6 +138,10 @@ class Runner:
|
|
|
138
138
|
self.debug('Run opts', obj={k: v for k, v in self.run_opts.items() if v is not None}, sub='init')
|
|
139
139
|
self.debug('Print opts', obj={k: v for k, v in self.print_opts.items() if v is not None}, sub='init')
|
|
140
140
|
|
|
141
|
+
# Load profiles
|
|
142
|
+
profiles_str = run_opts.get('profiles', [])
|
|
143
|
+
self.load_profiles(profiles_str)
|
|
144
|
+
|
|
141
145
|
# Determine exporters
|
|
142
146
|
exporters_str = self.run_opts.get('output') or self.default_exporters
|
|
143
147
|
self.exporters = self.resolve_exporters(exporters_str)
|
|
@@ -827,7 +831,7 @@ class Runner:
|
|
|
827
831
|
if isinstance(data, (OutputType, dict)):
|
|
828
832
|
if getattr(data, 'toDict', None):
|
|
829
833
|
data = data.toDict()
|
|
830
|
-
data = json.dumps(data)
|
|
834
|
+
data = json.dumps(data, default=str)
|
|
831
835
|
print(data, file=out)
|
|
832
836
|
|
|
833
837
|
def _get_findings_count(self):
|
|
@@ -953,6 +957,32 @@ class Runner:
|
|
|
953
957
|
]
|
|
954
958
|
return [cls for cls in classes if cls]
|
|
955
959
|
|
|
960
|
+
def load_profiles(self, profiles):
|
|
961
|
+
"""Load profiles and update run options.
|
|
962
|
+
|
|
963
|
+
Args:
|
|
964
|
+
profiles (list[str]): List of profile names to resolve.
|
|
965
|
+
|
|
966
|
+
Returns:
|
|
967
|
+
list: List of profiles.
|
|
968
|
+
"""
|
|
969
|
+
from secator.cli import ALL_PROFILES
|
|
970
|
+
if isinstance(profiles, str):
|
|
971
|
+
profiles = profiles.split(',')
|
|
972
|
+
templates = []
|
|
973
|
+
for pname in profiles:
|
|
974
|
+
matches = [p for p in ALL_PROFILES if p.name == pname]
|
|
975
|
+
if not matches:
|
|
976
|
+
self._print(Warning(message=f'Profile "{pname}" was not found'), rich=True)
|
|
977
|
+
else:
|
|
978
|
+
templates.append(matches[0])
|
|
979
|
+
opts = {}
|
|
980
|
+
for profile in templates:
|
|
981
|
+
self._print(Info(message=f'Loaded profile {profile.name} ({profile.description})'), rich=True)
|
|
982
|
+
opts.update(profile.opts)
|
|
983
|
+
opts = {k: v for k, v in opts.items() if k not in self.run_opts}
|
|
984
|
+
self.run_opts.update(opts)
|
|
985
|
+
|
|
956
986
|
@classmethod
|
|
957
987
|
def get_func_path(cls, func):
|
|
958
988
|
"""Get the full symbolic path of a function or method, including staticmethods, using function and method
|
secator/runners/command.py
CHANGED
secator/runners/scan.py
CHANGED
|
@@ -33,6 +33,7 @@ class Scan(Runner):
|
|
|
33
33
|
sigs = []
|
|
34
34
|
for name, workflow_opts in self.config.workflows.items():
|
|
35
35
|
run_opts = self.run_opts.copy()
|
|
36
|
+
run_opts.pop('profiles', None)
|
|
36
37
|
run_opts['no_poll'] = True
|
|
37
38
|
run_opts['caller'] = 'Scan'
|
|
38
39
|
opts = merge_opts(scan_opts, workflow_opts, run_opts)
|
secator/runners/task.py
CHANGED
secator/tasks/_categories.py
CHANGED
|
@@ -73,19 +73,19 @@ OPTS_VULN = [
|
|
|
73
73
|
|
|
74
74
|
class Http(Command):
|
|
75
75
|
meta_opts = {k: OPTS[k] for k in OPTS_HTTP_CRAWLERS}
|
|
76
|
-
|
|
76
|
+
input_types = [URL]
|
|
77
77
|
output_types = [Url]
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
class HttpCrawler(Command):
|
|
81
81
|
meta_opts = {k: OPTS[k] for k in OPTS_HTTP_CRAWLERS}
|
|
82
|
-
|
|
82
|
+
input_types = [URL]
|
|
83
83
|
output_types = [Url]
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
class HttpFuzzer(Command):
|
|
87
87
|
meta_opts = {k: OPTS[k] for k in OPTS_HTTP_FUZZERS}
|
|
88
|
-
|
|
88
|
+
input_types = [URL]
|
|
89
89
|
output_types = [Url]
|
|
90
90
|
|
|
91
91
|
|
|
@@ -99,22 +99,22 @@ class Recon(Command):
|
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
class ReconDns(Recon):
|
|
102
|
-
|
|
102
|
+
input_types = [HOST]
|
|
103
103
|
output_types = [Subdomain]
|
|
104
104
|
|
|
105
105
|
|
|
106
106
|
class ReconUser(Recon):
|
|
107
|
-
|
|
107
|
+
input_types = [USERNAME]
|
|
108
108
|
output_types = [UserAccount]
|
|
109
109
|
|
|
110
110
|
|
|
111
111
|
class ReconIp(Recon):
|
|
112
|
-
|
|
112
|
+
input_types = [CIDR_RANGE]
|
|
113
113
|
output_types = [Ip]
|
|
114
114
|
|
|
115
115
|
|
|
116
116
|
class ReconPort(Recon):
|
|
117
|
-
|
|
117
|
+
input_types = [IP]
|
|
118
118
|
output_types = [Port]
|
|
119
119
|
|
|
120
120
|
|
|
@@ -434,15 +434,15 @@ class Vuln(Command):
|
|
|
434
434
|
|
|
435
435
|
|
|
436
436
|
class VulnHttp(Vuln):
|
|
437
|
-
|
|
437
|
+
input_types = [HOST]
|
|
438
438
|
|
|
439
439
|
|
|
440
440
|
class VulnCode(Vuln):
|
|
441
|
-
|
|
441
|
+
input_types = [PATH]
|
|
442
442
|
|
|
443
443
|
|
|
444
444
|
class VulnMulti(Vuln):
|
|
445
|
-
|
|
445
|
+
input_types = [HOST]
|
|
446
446
|
output_types = [Vulnerability]
|
|
447
447
|
|
|
448
448
|
|
|
@@ -451,7 +451,7 @@ class VulnMulti(Vuln):
|
|
|
451
451
|
#--------------#
|
|
452
452
|
|
|
453
453
|
class Tagger(Command):
|
|
454
|
-
|
|
454
|
+
input_types = [URL]
|
|
455
455
|
output_types = [Tag]
|
|
456
456
|
|
|
457
457
|
#----------------#
|
secator/tasks/arjun.py
CHANGED
|
@@ -14,8 +14,9 @@ from secator.utils import process_wordlist
|
|
|
14
14
|
class arjun(Command):
|
|
15
15
|
"""HTTP Parameter Discovery Suite."""
|
|
16
16
|
cmd = 'arjun'
|
|
17
|
+
tags = ['url', 'fuzz', 'params']
|
|
17
18
|
input_flag = '-u'
|
|
18
|
-
|
|
19
|
+
input_types = [URL]
|
|
19
20
|
version_flag = ' '
|
|
20
21
|
opts = {
|
|
21
22
|
'chunk_size': {'type': int, 'help': 'Control query/chunk size'},
|
secator/tasks/bbot.py
CHANGED
|
@@ -2,6 +2,7 @@ import shutil
|
|
|
2
2
|
|
|
3
3
|
from secator.config import CONFIG
|
|
4
4
|
from secator.decorators import task
|
|
5
|
+
from secator.definitions import FILENAME, HOST, IP, ORG_NAME, PORT, URL, USERNAME
|
|
5
6
|
from secator.runners import Command
|
|
6
7
|
from secator.serializers import RegexSerializer
|
|
7
8
|
from secator.output_types import Vulnerability, Port, Url, Record, Ip, Tag, Info, Error
|
|
@@ -177,8 +178,10 @@ def output_discriminator(self, item):
|
|
|
177
178
|
class bbot(Command):
|
|
178
179
|
"""Multipurpose scanner."""
|
|
179
180
|
cmd = 'bbot -y --allow-deadly --force'
|
|
181
|
+
tags = ['vuln', 'scan']
|
|
180
182
|
json_flag = '--json'
|
|
181
183
|
input_flag = '-t'
|
|
184
|
+
input_types = [HOST, IP, URL, PORT, ORG_NAME, USERNAME, FILENAME]
|
|
182
185
|
file_flag = None
|
|
183
186
|
version_flag = '--help'
|
|
184
187
|
opts = {
|
secator/tasks/bup.py
CHANGED
secator/tasks/cariddi.py
CHANGED
|
@@ -14,7 +14,8 @@ from secator.tasks._categories import HttpCrawler
|
|
|
14
14
|
class cariddi(HttpCrawler):
|
|
15
15
|
"""Crawl endpoints, secrets, api keys, extensions, tokens..."""
|
|
16
16
|
cmd = 'cariddi'
|
|
17
|
-
|
|
17
|
+
tags = ['url', 'crawl']
|
|
18
|
+
input_types = [URL]
|
|
18
19
|
input_flag = OPT_PIPE_INPUT
|
|
19
20
|
output_types = [Url, Tag]
|
|
20
21
|
file_flag = OPT_PIPE_INPUT
|
secator/tasks/dalfox.py
CHANGED
secator/tasks/dirsearch.py
CHANGED
|
@@ -9,7 +9,7 @@ from secator.definitions import (CONTENT_LENGTH, CONTENT_TYPE, DELAY, DEPTH,
|
|
|
9
9
|
MATCH_CODES, MATCH_REGEX, MATCH_SIZE,
|
|
10
10
|
MATCH_WORDS, METHOD, OPT_NOT_SUPPORTED, OUTPUT_PATH, PROXY,
|
|
11
11
|
RATE_LIMIT, RETRIES, STATUS_CODE,
|
|
12
|
-
THREADS, TIMEOUT, USER_AGENT, WORDLIST)
|
|
12
|
+
THREADS, TIMEOUT, USER_AGENT, WORDLIST, URL)
|
|
13
13
|
from secator.output_types import Url, Info, Error
|
|
14
14
|
from secator.tasks._categories import HttpFuzzer
|
|
15
15
|
|
|
@@ -18,6 +18,8 @@ from secator.tasks._categories import HttpFuzzer
|
|
|
18
18
|
class dirsearch(HttpFuzzer):
|
|
19
19
|
"""Advanced web path brute-forcer."""
|
|
20
20
|
cmd = 'dirsearch'
|
|
21
|
+
tags = ['url', 'fuzz']
|
|
22
|
+
input_types = [URL]
|
|
21
23
|
input_flag = '-u'
|
|
22
24
|
file_flag = '-l'
|
|
23
25
|
json_flag = '-O json'
|
secator/tasks/dnsx.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from secator.decorators import task
|
|
2
|
-
from secator.definitions import (OPT_PIPE_INPUT, RATE_LIMIT, RETRIES, THREADS)
|
|
2
|
+
from secator.definitions import (HOST, OPT_PIPE_INPUT, RATE_LIMIT, RETRIES, THREADS)
|
|
3
3
|
from secator.output_types import Record, Ip, Subdomain
|
|
4
4
|
from secator.output_types.ip import IpProtocol
|
|
5
5
|
from secator.tasks._categories import ReconDns
|
|
@@ -11,8 +11,10 @@ from secator.utils import extract_domain_info
|
|
|
11
11
|
class dnsx(ReconDns):
|
|
12
12
|
"""dnsx is a fast and multi-purpose DNS toolkit designed for running various retryabledns library."""
|
|
13
13
|
cmd = 'dnsx -resp -recon'
|
|
14
|
+
tags = ['dns', 'fuzz']
|
|
14
15
|
json_flag = '-json'
|
|
15
16
|
input_flag = OPT_PIPE_INPUT
|
|
17
|
+
input_types = [HOST]
|
|
16
18
|
file_flag = OPT_PIPE_INPUT
|
|
17
19
|
output_types = [Record, Ip, Subdomain]
|
|
18
20
|
opt_key_map = {
|
secator/tasks/dnsxbrute.py
CHANGED
|
@@ -11,8 +11,10 @@ from secator.utils import process_wordlist
|
|
|
11
11
|
class dnsxbrute(ReconDns):
|
|
12
12
|
"""dnsx is a fast and multi-purpose DNS toolkit designed for running various library."""
|
|
13
13
|
cmd = 'dnsx'
|
|
14
|
+
tags = ['dns', 'fuzz']
|
|
14
15
|
json_flag = '-json'
|
|
15
16
|
input_flag = '-domain'
|
|
17
|
+
input_types = [HOST]
|
|
16
18
|
file_flag = '-domain'
|
|
17
19
|
opt_key_map = {
|
|
18
20
|
RATE_LIMIT: 'rate-limit',
|
secator/tasks/feroxbuster.py
CHANGED
|
@@ -6,7 +6,7 @@ from secator.definitions import (CONTENT_TYPE, DELAY, DEPTH, FILTER_CODES,
|
|
|
6
6
|
MATCH_REGEX, MATCH_SIZE, MATCH_WORDS, METHOD,
|
|
7
7
|
OPT_NOT_SUPPORTED, OPT_PIPE_INPUT, PROXY,
|
|
8
8
|
RATE_LIMIT, RETRIES, STATUS_CODE,
|
|
9
|
-
THREADS, TIMEOUT, USER_AGENT, WORDLIST, WORDS)
|
|
9
|
+
THREADS, TIMEOUT, USER_AGENT, WORDLIST, WORDS, URL)
|
|
10
10
|
from secator.output_types import Url
|
|
11
11
|
from secator.serializers import JSONSerializer
|
|
12
12
|
from secator.tasks._categories import HttpFuzzer
|
|
@@ -16,6 +16,8 @@ from secator.tasks._categories import HttpFuzzer
|
|
|
16
16
|
class feroxbuster(HttpFuzzer):
|
|
17
17
|
"""Simple, fast, recursive content discovery tool written in Rust"""
|
|
18
18
|
cmd = 'feroxbuster --auto-bail --no-state'
|
|
19
|
+
tags = ['url', 'fuzz']
|
|
20
|
+
input_types = [URL]
|
|
19
21
|
input_flag = '--url'
|
|
20
22
|
input_chunk_size = 1
|
|
21
23
|
file_flag = OPT_PIPE_INPUT
|