secator 0.3.5__py3-none-any.whl → 0.4.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/celery.py +16 -21
- secator/cli.py +163 -81
- secator/config.py +555 -122
- secator/decorators.py +17 -10
- secator/definitions.py +4 -77
- secator/exporters/gdrive.py +10 -10
- secator/hooks/mongodb.py +3 -4
- secator/installer.py +10 -6
- secator/output_types/vulnerability.py +3 -1
- secator/runners/_base.py +12 -11
- secator/runners/_helpers.py +52 -34
- secator/runners/command.py +26 -30
- secator/runners/scan.py +4 -8
- secator/runners/task.py +2 -2
- secator/runners/workflow.py +3 -7
- secator/tasks/_categories.py +95 -44
- secator/tasks/dnsxbrute.py +3 -2
- secator/tasks/ffuf.py +2 -2
- secator/tasks/httpx.py +4 -4
- secator/tasks/katana.py +5 -4
- secator/tasks/msfconsole.py +3 -4
- secator/tasks/nmap.py +95 -48
- secator/tasks/nuclei.py +4 -0
- secator/tasks/searchsploit.py +0 -1
- secator/template.py +137 -0
- secator/utils.py +3 -7
- {secator-0.3.5.dist-info → secator-0.4.0.dist-info}/METADATA +12 -6
- {secator-0.3.5.dist-info → secator-0.4.0.dist-info}/RECORD +31 -30
- {secator-0.3.5.dist-info → secator-0.4.0.dist-info}/WHEEL +1 -1
- {secator-0.3.5.dist-info → secator-0.4.0.dist-info}/entry_points.txt +0 -0
- {secator-0.3.5.dist-info → secator-0.4.0.dist-info}/licenses/LICENSE +0 -0
secator/tasks/nmap.py
CHANGED
|
@@ -4,16 +4,19 @@ import re
|
|
|
4
4
|
|
|
5
5
|
import xmltodict
|
|
6
6
|
|
|
7
|
+
from secator.config import CONFIG
|
|
7
8
|
from secator.decorators import task
|
|
8
9
|
from secator.definitions import (CONFIDENCE, CVSS_SCORE, DELAY,
|
|
9
10
|
DESCRIPTION, EXTRA_DATA, FOLLOW_REDIRECT,
|
|
10
11
|
HEADER, HOST, ID, IP, MATCHED_AT, NAME,
|
|
11
12
|
OPT_NOT_SUPPORTED, OUTPUT_PATH, PORT, PORTS, PROVIDER,
|
|
12
13
|
PROXY, RATE_LIMIT, REFERENCE, REFERENCES,
|
|
13
|
-
RETRIES, SCRIPT, SERVICE_NAME, STATE, TAGS,
|
|
14
|
-
THREADS, TIMEOUT, USER_AGENT)
|
|
14
|
+
RETRIES, SCRIPT, SERVICE_NAME, SEVERITY, STATE, TAGS,
|
|
15
|
+
THREADS, TIMEOUT, TOP_PORTS, USER_AGENT)
|
|
15
16
|
from secator.output_types import Exploit, Port, Vulnerability
|
|
17
|
+
from secator.rich import console
|
|
16
18
|
from secator.tasks._categories import VulnMulti
|
|
19
|
+
from secator.utils import debug
|
|
17
20
|
|
|
18
21
|
logger = logging.getLogger(__name__)
|
|
19
22
|
|
|
@@ -28,11 +31,12 @@ class nmap(VulnMulti):
|
|
|
28
31
|
opt_prefix = '--'
|
|
29
32
|
output_types = [Port, Vulnerability, Exploit]
|
|
30
33
|
opts = {
|
|
31
|
-
PORTS: {'type': str, '
|
|
34
|
+
PORTS: {'type': str, 'short': 'p', 'help': 'Ports to scan'},
|
|
35
|
+
TOP_PORTS: {'type': int, 'short': 'tp', 'help': 'Top ports to scan [full, 100, 1000]'},
|
|
32
36
|
SCRIPT: {'type': str, 'default': 'vulners', 'help': 'NSE scripts'},
|
|
33
37
|
# 'tcp_connect': {'type': bool, 'short': 'sT', 'default': False, 'help': 'TCP Connect scan'},
|
|
34
38
|
'tcp_syn_stealth': {'is_flag': True, 'short': 'sS', 'default': False, 'help': 'TCP SYN Stealth'},
|
|
35
|
-
'output_path': {'type': str, 'short': 'oX', 'default': None, 'help': 'Output XML file path'}
|
|
39
|
+
'output_path': {'type': str, 'short': 'oX', 'default': None, 'help': 'Output XML file path'},
|
|
36
40
|
}
|
|
37
41
|
opt_key_map = {
|
|
38
42
|
HEADER: OPT_NOT_SUPPORTED,
|
|
@@ -114,20 +118,18 @@ class nmapData(dict):
|
|
|
114
118
|
|
|
115
119
|
# Get extra data
|
|
116
120
|
extra_data = self._get_extra_data(port)
|
|
121
|
+
service_name = extra_data['service_name']
|
|
122
|
+
version_exact = extra_data.get('version_exact', False)
|
|
123
|
+
conf = extra_data.get('confidence')
|
|
124
|
+
if not version_exact:
|
|
125
|
+
console.print(
|
|
126
|
+
f'[bold orange1]nmap could not identify an exact version for {service_name} '
|
|
127
|
+
f'(detection confidence is {conf}): do not blindy trust the results ![/]'
|
|
128
|
+
)
|
|
117
129
|
|
|
118
130
|
# Grab CPEs
|
|
119
131
|
cpes = extra_data.get('cpe', [])
|
|
120
132
|
|
|
121
|
-
# Grab service name
|
|
122
|
-
service_name = ''
|
|
123
|
-
if 'product' in extra_data:
|
|
124
|
-
service_name = extra_data['product']
|
|
125
|
-
elif 'name' in extra_data:
|
|
126
|
-
service_name = extra_data['name']
|
|
127
|
-
if 'version' in extra_data:
|
|
128
|
-
version = extra_data['version']
|
|
129
|
-
service_name += f'/{version}'
|
|
130
|
-
|
|
131
133
|
# Get script output
|
|
132
134
|
scripts = self._get_scripts(port)
|
|
133
135
|
|
|
@@ -160,10 +162,17 @@ class nmapData(dict):
|
|
|
160
162
|
EXTRA_DATA: extra_data,
|
|
161
163
|
}
|
|
162
164
|
if not func:
|
|
163
|
-
|
|
165
|
+
debug(f'Script output parser for "{script_id}" is not supported YET.', sub='cve')
|
|
164
166
|
continue
|
|
165
167
|
for vuln in func(output, cpes=cpes):
|
|
166
168
|
vuln.update(metadata)
|
|
169
|
+
confidence = 'low'
|
|
170
|
+
if 'cpe-match' in vuln[TAGS]:
|
|
171
|
+
confidence = 'high' if version_exact else 'medium'
|
|
172
|
+
vuln[CONFIDENCE] = confidence
|
|
173
|
+
if (CONFIG.runners.skip_cve_low_confidence and vuln[CONFIDENCE] == 'low'):
|
|
174
|
+
debug(f'{vuln[ID]}: ignored (low confidence).', sub='cve')
|
|
175
|
+
continue
|
|
167
176
|
yield vuln
|
|
168
177
|
|
|
169
178
|
#---------------------#
|
|
@@ -198,40 +207,74 @@ class nmapData(dict):
|
|
|
198
207
|
return host_cfg.get('address', {}).get('@addr', None)
|
|
199
208
|
|
|
200
209
|
def _get_extra_data(self, port_cfg):
|
|
201
|
-
|
|
210
|
+
extra_data = {
|
|
202
211
|
k.lstrip('@'): v
|
|
203
212
|
for k, v in port_cfg.get('service', {}).items()
|
|
204
213
|
}
|
|
205
214
|
|
|
206
215
|
# Strip product / version strings
|
|
207
|
-
if 'product' in
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
216
|
+
if 'product' in extra_data:
|
|
217
|
+
extra_data['product'] = extra_data['product'].lower()
|
|
218
|
+
|
|
219
|
+
# Get version and post-process it
|
|
220
|
+
version = None
|
|
221
|
+
if 'version' in extra_data:
|
|
222
|
+
vsplit = extra_data['version'].split(' ')
|
|
223
|
+
version_exact = True
|
|
213
224
|
os = None
|
|
214
|
-
if len(
|
|
215
|
-
version, os, extra_version = tuple(
|
|
225
|
+
if len(vsplit) == 3:
|
|
226
|
+
version, os, extra_version = tuple(vsplit)
|
|
227
|
+
if os == 'or' and extra_version == 'later':
|
|
228
|
+
version_exact = False
|
|
229
|
+
os = None
|
|
216
230
|
version = f'{version}-{extra_version}'
|
|
217
|
-
elif len(
|
|
218
|
-
version, os = tuple(
|
|
219
|
-
elif len(
|
|
220
|
-
version =
|
|
231
|
+
elif len(vsplit) == 2:
|
|
232
|
+
version, os = tuple(vsplit)
|
|
233
|
+
elif len(vsplit) == 1:
|
|
234
|
+
version = vsplit[0]
|
|
221
235
|
else:
|
|
222
|
-
version =
|
|
236
|
+
version = extra_data['version']
|
|
223
237
|
if os:
|
|
224
|
-
|
|
238
|
+
extra_data['os'] = os
|
|
239
|
+
if version:
|
|
240
|
+
extra_data['version'] = version
|
|
241
|
+
extra_data['version_exact'] = version_exact
|
|
242
|
+
|
|
243
|
+
# Grap service name
|
|
244
|
+
product = extra_data.get('name', None) or extra_data.get('product', None)
|
|
245
|
+
if product:
|
|
246
|
+
service_name = product
|
|
225
247
|
if version:
|
|
226
|
-
|
|
248
|
+
service_name += f'/{version}'
|
|
249
|
+
extra_data['service_name'] = service_name
|
|
227
250
|
|
|
228
251
|
# Grab CPEs
|
|
229
|
-
cpes =
|
|
252
|
+
cpes = extra_data.get('cpe', [])
|
|
230
253
|
if not isinstance(cpes, list):
|
|
231
254
|
cpes = [cpes]
|
|
232
|
-
|
|
255
|
+
extra_data['cpe'] = cpes
|
|
256
|
+
debug(f'Found CPEs: {",".join(cpes)}', sub='cve')
|
|
257
|
+
|
|
258
|
+
# Grab confidence
|
|
259
|
+
conf = int(extra_data.get('conf', 0))
|
|
260
|
+
if conf > 7:
|
|
261
|
+
confidence = 'high'
|
|
262
|
+
elif conf > 4:
|
|
263
|
+
confidence = 'medium'
|
|
264
|
+
else:
|
|
265
|
+
confidence = 'low'
|
|
266
|
+
extra_data['confidence'] = confidence
|
|
267
|
+
|
|
268
|
+
# Build custom CPE
|
|
269
|
+
if product and version:
|
|
270
|
+
vsplit = version.split('-')
|
|
271
|
+
version_cpe = vsplit[0] if not version_exact else version
|
|
272
|
+
cpe = VulnMulti.create_cpe_string(product, version_cpe)
|
|
273
|
+
if cpe not in cpes:
|
|
274
|
+
cpes.append(cpe)
|
|
275
|
+
debug(f'Added new CPE from identified product and version: {cpe}', sub='cve')
|
|
233
276
|
|
|
234
|
-
return
|
|
277
|
+
return extra_data
|
|
235
278
|
|
|
236
279
|
def _get_scripts(self, port_cfg):
|
|
237
280
|
scripts = port_cfg.get('script', [])
|
|
@@ -276,23 +319,23 @@ class nmapData(dict):
|
|
|
276
319
|
TAGS: [vuln_id, provider_name]
|
|
277
320
|
}
|
|
278
321
|
if provider_name == 'MITRE CVE':
|
|
279
|
-
|
|
280
|
-
if
|
|
281
|
-
vuln.update(
|
|
322
|
+
data = VulnMulti.lookup_cve(vuln['id'], cpes=cpes)
|
|
323
|
+
if data:
|
|
324
|
+
vuln.update(data)
|
|
282
325
|
yield vuln
|
|
283
326
|
else:
|
|
284
|
-
|
|
327
|
+
debug(f'Vulscan provider {provider_name} is not supported YET.', sub='cve')
|
|
285
328
|
continue
|
|
286
329
|
|
|
287
330
|
def _parse_vulners_output(self, out, **kwargs):
|
|
288
|
-
cpes = []
|
|
331
|
+
cpes = kwargs.get('cpes', [])
|
|
289
332
|
provider_name = 'vulners'
|
|
290
333
|
for line in out.splitlines():
|
|
291
334
|
if not line:
|
|
292
335
|
continue
|
|
293
336
|
line = line.strip()
|
|
294
337
|
if line.startswith('cpe:'):
|
|
295
|
-
cpes.append(line)
|
|
338
|
+
cpes.append(line.rstrip(':'))
|
|
296
339
|
continue
|
|
297
340
|
elems = tuple(line.split('\t'))
|
|
298
341
|
vuln = {}
|
|
@@ -307,7 +350,8 @@ class nmapData(dict):
|
|
|
307
350
|
NAME: name,
|
|
308
351
|
PROVIDER: provider_name,
|
|
309
352
|
REFERENCE: reference_url,
|
|
310
|
-
'_type': 'exploit'
|
|
353
|
+
'_type': 'exploit',
|
|
354
|
+
TAGS: [exploit_id, provider_name]
|
|
311
355
|
# CVSS_SCORE: cvss_score,
|
|
312
356
|
# CONFIDENCE: 'low'
|
|
313
357
|
}
|
|
@@ -319,23 +363,26 @@ class nmapData(dict):
|
|
|
319
363
|
|
|
320
364
|
elif len(elems) == 3: # vuln
|
|
321
365
|
vuln_id, vuln_cvss, reference_url = tuple(line.split('\t'))
|
|
366
|
+
vuln_cvss = float(vuln_cvss)
|
|
367
|
+
vuln_id = vuln_id.split(':')[-1]
|
|
322
368
|
vuln_type = vuln_id.split('-')[0]
|
|
323
369
|
vuln = {
|
|
324
370
|
ID: vuln_id,
|
|
325
371
|
NAME: vuln_id,
|
|
326
372
|
PROVIDER: provider_name,
|
|
327
373
|
CVSS_SCORE: vuln_cvss,
|
|
374
|
+
SEVERITY: VulnMulti.cvss_to_severity(vuln_cvss),
|
|
328
375
|
REFERENCES: [reference_url],
|
|
329
|
-
TAGS: [],
|
|
376
|
+
TAGS: [vuln_id, provider_name],
|
|
330
377
|
CONFIDENCE: 'low'
|
|
331
378
|
}
|
|
332
|
-
if vuln_type == 'CVE':
|
|
379
|
+
if vuln_type == 'CVE' or vuln_type == 'PRION:CVE':
|
|
333
380
|
vuln[TAGS].append('cve')
|
|
334
|
-
|
|
335
|
-
if
|
|
336
|
-
vuln.update(
|
|
381
|
+
data = VulnMulti.lookup_cve(vuln_id, cpes=cpes)
|
|
382
|
+
if data:
|
|
383
|
+
vuln.update(data)
|
|
337
384
|
yield vuln
|
|
338
385
|
else:
|
|
339
|
-
|
|
386
|
+
debug(f'Vulners parser for "{vuln_type}" is not implemented YET.', sub='cve')
|
|
340
387
|
else:
|
|
341
|
-
|
|
388
|
+
debug(f'Unrecognized vulners output: {elems}', sub='cve')
|
secator/tasks/nuclei.py
CHANGED
|
@@ -85,8 +85,12 @@ class nuclei(VulnMulti):
|
|
|
85
85
|
def extra_data_extractor(item):
|
|
86
86
|
data = {}
|
|
87
87
|
data['data'] = item.get('extracted-results', [])
|
|
88
|
+
data['type'] = item.get('type', '')
|
|
88
89
|
data['template_id'] = item['template-id']
|
|
89
90
|
data['template_url'] = item.get('template-url', '')
|
|
91
|
+
for k, v in item.get('meta', {}).items():
|
|
92
|
+
data['data'].append(f'{k}: {v}')
|
|
93
|
+
data['metadata'] = item.get('metadata', {})
|
|
90
94
|
return data
|
|
91
95
|
|
|
92
96
|
@staticmethod
|
secator/tasks/searchsploit.py
CHANGED
|
@@ -28,7 +28,6 @@ class searchsploit(Command):
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
install_cmd = 'sudo git clone https://gitlab.com/exploit-database/exploitdb.git /opt/exploitdb || true && sudo ln -sf /opt/exploitdb/searchsploit /usr/local/bin/searchsploit' # noqa: E501
|
|
31
|
-
install_github_handle = 'rad10/SearchSploit.py'
|
|
32
31
|
proxychains = False
|
|
33
32
|
proxy_socks5 = False
|
|
34
33
|
proxy_http = False
|
secator/template.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import glob
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
from dotmap import DotMap
|
|
6
|
+
|
|
7
|
+
from secator.rich import console
|
|
8
|
+
from secator.config import CONFIG, CONFIGS_FOLDER
|
|
9
|
+
|
|
10
|
+
TEMPLATES_DIR_KEYS = ['workflow', 'scan', 'profile']
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def load_template(name):
|
|
14
|
+
"""Load a config by name.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
name: Name of the config, for instances profiles/aggressive or workflows/domain_scan.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
dict: Loaded config.
|
|
21
|
+
"""
|
|
22
|
+
path = CONFIGS_FOLDER / f'{name}.yaml'
|
|
23
|
+
if not path.exists():
|
|
24
|
+
console.log(f'Config "{name}" could not be loaded.')
|
|
25
|
+
return
|
|
26
|
+
with path.open('r') as f:
|
|
27
|
+
return yaml.load(f.read(), Loader=yaml.Loader)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def find_templates():
|
|
31
|
+
results = {'scan': [], 'workflow': [], 'profile': []}
|
|
32
|
+
dirs_type = [CONFIGS_FOLDER]
|
|
33
|
+
if CONFIG.dirs.templates:
|
|
34
|
+
dirs_type.append(CONFIG.dirs.templates)
|
|
35
|
+
paths = []
|
|
36
|
+
for dir in dirs_type:
|
|
37
|
+
dir_paths = [
|
|
38
|
+
Path(path)
|
|
39
|
+
for path in glob.glob(str(dir).rstrip('/') + '/**/*.y*ml', recursive=True)
|
|
40
|
+
]
|
|
41
|
+
paths.extend(dir_paths)
|
|
42
|
+
for path in paths:
|
|
43
|
+
with path.open('r') as f:
|
|
44
|
+
try:
|
|
45
|
+
config = yaml.load(f.read(), yaml.Loader)
|
|
46
|
+
type = config.get('type')
|
|
47
|
+
if type:
|
|
48
|
+
results[type].append(path)
|
|
49
|
+
except yaml.YAMLError as exc:
|
|
50
|
+
console.log(f'Unable to load config at {path}')
|
|
51
|
+
console.log(str(exc))
|
|
52
|
+
return results
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class TemplateLoader(DotMap):
|
|
56
|
+
|
|
57
|
+
def __init__(self, input={}, name=None, **kwargs):
|
|
58
|
+
if name:
|
|
59
|
+
name = name.replace('-', '_') # so that workflows have a nice '-' in CLI
|
|
60
|
+
config = self._load_from_name(name)
|
|
61
|
+
elif isinstance(input, str) or isinstance(input, Path):
|
|
62
|
+
config = self._load_from_file(input)
|
|
63
|
+
else:
|
|
64
|
+
config = input
|
|
65
|
+
super().__init__(config)
|
|
66
|
+
|
|
67
|
+
def _load_from_file(self, path):
|
|
68
|
+
if isinstance(path, str):
|
|
69
|
+
path = Path(path)
|
|
70
|
+
if not path.exists():
|
|
71
|
+
console.log(f'Config path {path} does not exists', style='bold red')
|
|
72
|
+
return
|
|
73
|
+
with path.open('r') as f:
|
|
74
|
+
return yaml.load(f.read(), Loader=yaml.Loader)
|
|
75
|
+
|
|
76
|
+
def _load_from_name(self, name):
|
|
77
|
+
return load_template(name)
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def load_all(cls):
|
|
81
|
+
configs = find_templates()
|
|
82
|
+
return TemplateLoader({
|
|
83
|
+
key: [TemplateLoader(path) for path in configs[key]]
|
|
84
|
+
for key in TEMPLATES_DIR_KEYS
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
def get_tasks_class(self):
|
|
88
|
+
from secator.runners import Task
|
|
89
|
+
tasks = []
|
|
90
|
+
for name, conf in self.tasks.items():
|
|
91
|
+
if name == '_group':
|
|
92
|
+
group_conf = TemplateLoader(input={'tasks': conf})
|
|
93
|
+
tasks.extend(group_conf.get_tasks_class())
|
|
94
|
+
else:
|
|
95
|
+
tasks.append(Task.get_task_class(name))
|
|
96
|
+
return tasks
|
|
97
|
+
|
|
98
|
+
def get_workflows(self):
|
|
99
|
+
return [TemplateLoader(name=f'workflows/{name}') for name, _ in self.workflows.items()]
|
|
100
|
+
|
|
101
|
+
def get_workflow_supported_opts(self):
|
|
102
|
+
opts = {}
|
|
103
|
+
tasks = self.get_tasks_class()
|
|
104
|
+
for task_cls in tasks:
|
|
105
|
+
task_opts = task_cls.get_supported_opts()
|
|
106
|
+
for name, conf in task_opts.items():
|
|
107
|
+
supported = opts.get(name, {}).get('supported', False)
|
|
108
|
+
opts[name] = conf
|
|
109
|
+
opts[name]['supported'] = conf['supported'] or supported
|
|
110
|
+
return opts
|
|
111
|
+
|
|
112
|
+
def get_scan_supported_opts(self):
|
|
113
|
+
opts = {}
|
|
114
|
+
workflows = self.get_workflows()
|
|
115
|
+
for workflow in workflows:
|
|
116
|
+
workflow_opts = workflow.get_workflow_supported_opts()
|
|
117
|
+
for name, conf in workflow_opts.items():
|
|
118
|
+
supported = opts.get(name, {}).get('supported', False)
|
|
119
|
+
opts[name] = conf
|
|
120
|
+
opts[name]['supported'] = conf['supported'] or supported
|
|
121
|
+
return opts
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def supported_opts(self):
|
|
125
|
+
return self.get_supported_opts()
|
|
126
|
+
|
|
127
|
+
def get_supported_opts(self):
|
|
128
|
+
opts = {}
|
|
129
|
+
if self.type == 'workflow':
|
|
130
|
+
opts = self.get_workflow_supported_opts()
|
|
131
|
+
elif self.type == 'scan':
|
|
132
|
+
opts = self.get_scan_supported_opts()
|
|
133
|
+
elif self.type == 'task':
|
|
134
|
+
tasks = self.get_tasks_class()
|
|
135
|
+
if tasks:
|
|
136
|
+
opts = tasks[0].get_supported_opts()
|
|
137
|
+
return dict(sorted(opts.items()))
|
secator/utils.py
CHANGED
|
@@ -19,8 +19,8 @@ import ifaddr
|
|
|
19
19
|
import yaml
|
|
20
20
|
from rich.markdown import Markdown
|
|
21
21
|
|
|
22
|
-
from secator.definitions import (DEBUG, DEBUG_COMPONENT,
|
|
23
|
-
|
|
22
|
+
from secator.definitions import (DEBUG, DEBUG_COMPONENT, VERSION, DEV_PACKAGE)
|
|
23
|
+
from secator.config import CONFIG, ROOT_FOLDER, LIB_FOLDER
|
|
24
24
|
from secator.rich import console
|
|
25
25
|
|
|
26
26
|
logger = logging.getLogger(__name__)
|
|
@@ -65,7 +65,7 @@ def expand_input(input):
|
|
|
65
65
|
"""
|
|
66
66
|
if input is None: # read from stdin
|
|
67
67
|
console.print('Waiting for input on stdin ...', style='bold yellow')
|
|
68
|
-
rlist, _, _ = select.select([sys.stdin], [], [],
|
|
68
|
+
rlist, _, _ = select.select([sys.stdin], [], [], CONFIG.cli.stdin_timeout)
|
|
69
69
|
if rlist:
|
|
70
70
|
data = sys.stdin.read().splitlines()
|
|
71
71
|
else:
|
|
@@ -312,10 +312,6 @@ def detect_host(interface=None):
|
|
|
312
312
|
return None
|
|
313
313
|
|
|
314
314
|
|
|
315
|
-
def find_list_item(array, val, key='id', default=None):
|
|
316
|
-
return next((item for item in array if item[key] == val), default)
|
|
317
|
-
|
|
318
|
-
|
|
319
315
|
def print_results_table(results, title=None, exclude_fields=[], log=False):
|
|
320
316
|
from secator.output_types import OUTPUT_TYPES
|
|
321
317
|
from secator.rich import build_table
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: secator
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: The pentester's swiss knife.
|
|
5
5
|
Project-URL: Homepage, https://github.com/freelabz/secator
|
|
6
6
|
Project-URL: Issues, https://github.com/freelabz/secator/issues
|
|
@@ -29,11 +29,13 @@ Requires-Dist: humanize<5
|
|
|
29
29
|
Requires-Dist: ifaddr<1
|
|
30
30
|
Requires-Dist: jinja2<4
|
|
31
31
|
Requires-Dist: packaging<25
|
|
32
|
+
Requires-Dist: pydantic<3
|
|
32
33
|
Requires-Dist: python-dotenv<2
|
|
33
34
|
Requires-Dist: pyyaml<7
|
|
34
35
|
Requires-Dist: requests<3
|
|
35
36
|
Requires-Dist: rich-click<1.7
|
|
36
37
|
Requires-Dist: rich<14
|
|
38
|
+
Requires-Dist: typing-extensions<5
|
|
37
39
|
Requires-Dist: validators<1
|
|
38
40
|
Requires-Dist: xmltodict<1
|
|
39
41
|
Provides-Extra: build
|
|
@@ -90,7 +92,7 @@ and it is designed to improve productivity for pentesters and security researche
|
|
|
90
92
|
|
|
91
93
|
# Features
|
|
92
94
|
|
|
93
|
-

|
|
94
96
|
|
|
95
97
|
* **Curated list of commands**
|
|
96
98
|
|
|
@@ -177,14 +179,18 @@ wget -O - https://raw.githubusercontent.com/freelabz/secator/main/scripts/instal
|
|
|
177
179
|
<summary>Docker</summary>
|
|
178
180
|
|
|
179
181
|
```sh
|
|
180
|
-
docker run -it --rm --net=host -v
|
|
182
|
+
docker run -it --rm --net=host -v ~/.secator:/root/.secator freelabz/secator --help
|
|
181
183
|
```
|
|
182
184
|
|
|
183
|
-
The volume mount
|
|
185
|
+
The volume mount -v is necessary to save all secator reports to your host machine, and--net=host is recommended to grant full access to the host network.
|
|
184
186
|
|
|
185
|
-
You can
|
|
187
|
+
You can alias this command to run it easier:
|
|
186
188
|
```sh
|
|
187
|
-
alias secator="docker run -it --rm --net=host -v
|
|
189
|
+
alias secator="docker run -it --rm --net=host -v ~/.secator:/root/.secator freelabz/secator"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Now you can run secator like if it was installed on baremetal:
|
|
193
|
+
```
|
|
188
194
|
secator --help
|
|
189
195
|
```
|
|
190
196
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
secator/.gitignore,sha256=da8MUc3hdb6Mo0WjZu2upn5uZMbXcBGvhdhTQ1L89HI,3093
|
|
2
2
|
secator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
secator/celery.py,sha256=
|
|
4
|
-
secator/cli.py,sha256
|
|
5
|
-
secator/config.py,sha256
|
|
6
|
-
secator/decorators.py,sha256=
|
|
7
|
-
secator/definitions.py,sha256=
|
|
8
|
-
secator/installer.py,sha256=
|
|
3
|
+
secator/celery.py,sha256=5Raua1rDFJACdmP4b1HLS15kx3ObcUrVConQ0UcopTc,12135
|
|
4
|
+
secator/cli.py,sha256=-B3Hjy8I1z7Jc6fiPYQPCt0DX6TVrNZ98aU-8oN_RQM,34150
|
|
5
|
+
secator/config.py,sha256=-PdJgIesb7T1N2s38iHHeIquxBHoW1Cmd04gQr9PnnY,17403
|
|
6
|
+
secator/decorators.py,sha256=SIS_32SbeN9iTx82mvy9F9vLpjofRYspOOLCXytIO2g,10764
|
|
7
|
+
secator/definitions.py,sha256=lGhuBw2oBvSjtpxCPm0EwoAZLxcf2rwDEpcU8yfxVUA,3689
|
|
8
|
+
secator/installer.py,sha256=fpjf5fbA5M5zDQVP4Fdr51sLoMwtoGZTe3mXh7DvD6s,9466
|
|
9
9
|
secator/report.py,sha256=g0stVCcx9klbUS01uKvWcxNE9MJfNFMexYA2SoDIWJU,2596
|
|
10
10
|
secator/rich.py,sha256=W4PipeZfIVnERfW3ySeWSvnZ90jhCFiABBoERYy_6kM,3177
|
|
11
|
-
secator/
|
|
11
|
+
secator/template.py,sha256=MRCzvn8FJ7D4n8V4ceBwAjPsdLhTWjDRu5VLefmLb6M,3705
|
|
12
|
+
secator/utils.py,sha256=_npGVl85skBAMdGDzrhvnNwg4-aRhbisl8oxsB9Q1q8,10824
|
|
12
13
|
secator/utils_test.py,sha256=xVF9RH1-p3X0TdmODJi4k62H7Xth96Ib7qnUZ4vAJs8,5043
|
|
13
14
|
secator/configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
15
|
secator/configs/profiles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -37,12 +38,12 @@ secator/configs/workflows/wordpress.yaml,sha256=QgBUNi8Gav_efbmczUGfzlByWsmogTmG
|
|
|
37
38
|
secator/exporters/__init__.py,sha256=2nBPOOas9Fp4nmo9pjSw3mvklZNHL8BmH88w_i-eaJc,356
|
|
38
39
|
secator/exporters/_base.py,sha256=-RrrwO_qp0ETLLHSta4T-zKtMbWdiEmz1Cw5mNo6USU,77
|
|
39
40
|
secator/exporters/csv.py,sha256=xsPMljzJhoTc8lcfxWBIKH2niK6KeYL7Bx2NzpdsYw0,982
|
|
40
|
-
secator/exporters/gdrive.py,sha256=
|
|
41
|
+
secator/exporters/gdrive.py,sha256=hq6PtCVejeqp9cipcAkH5ZsijBWB_SXIcW9uctYJAYM,4143
|
|
41
42
|
secator/exporters/json.py,sha256=cWkDugUdy-lbcPFKNgBrRFxHspiFhjVbJfdDABjJ9uk,431
|
|
42
43
|
secator/exporters/table.py,sha256=RHQoaFeeyeoBGNucJgrlk2KtmVqe9BGNtAAYee7xJ8Y,210
|
|
43
44
|
secator/exporters/txt.py,sha256=QbiwWYGgHpITGw1sL2TX-S3AfmBdJ-VOWkPJzuBvOu4,785
|
|
44
45
|
secator/hooks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
-
secator/hooks/mongodb.py,sha256=
|
|
46
|
+
secator/hooks/mongodb.py,sha256=PGcM6hkTl4bt46-cPlFgcY-PfNWmHpz_eiv77XeV0-A,7036
|
|
46
47
|
secator/output_types/__init__.py,sha256=uj6AXDeorECPwhwekNVGjQbGv41jHG_8udkuoc4XzW0,854
|
|
47
48
|
secator/output_types/_base.py,sha256=bld1ED0pN1hOvwBV2canrlKrfBCgawzWKPDH6F3jVQE,2469
|
|
48
49
|
secator/output_types/exploit.py,sha256=NIa0mbhm3ZTyV5kyjEvrI5QK2swMpdMCj3f1gIWcsro,1581
|
|
@@ -55,46 +56,46 @@ secator/output_types/tag.py,sha256=8AlT0VigsYP04GN8sPCTM07IlL5uMUmFgsNa9IDCoyY,1
|
|
|
55
56
|
secator/output_types/target.py,sha256=gJWzzqhal34Cnl9oAKf0m1MSaGxRtUGdA2XbkhD_yd0,848
|
|
56
57
|
secator/output_types/url.py,sha256=yDozBXCuPfuybH1iX_xGmbCJPXO6Ei14C8Hp5CnzNbE,2535
|
|
57
58
|
secator/output_types/user_account.py,sha256=EiT2BFl2LTCdqHF1meoMEKVhjKGroyf8-JoWHPuBOTc,1378
|
|
58
|
-
secator/output_types/vulnerability.py,sha256=
|
|
59
|
+
secator/output_types/vulnerability.py,sha256=hV6gsCFLdZ9IQV1ZSVCnsUms8H3md2_XZqp3wJDYDn4,2896
|
|
59
60
|
secator/runners/__init__.py,sha256=EBbOk37vkBy9p8Hhrbi-2VtM_rTwQ3b-0ggTyiD22cE,290
|
|
60
|
-
secator/runners/_base.py,sha256=
|
|
61
|
-
secator/runners/_helpers.py,sha256=
|
|
62
|
-
secator/runners/command.py,sha256=
|
|
63
|
-
secator/runners/scan.py,sha256=
|
|
64
|
-
secator/runners/task.py,sha256=
|
|
65
|
-
secator/runners/workflow.py,sha256=
|
|
61
|
+
secator/runners/_base.py,sha256=RHEQq-OvoC_rnpws30LmXDaO907pcblPe_WbWRcXQqk,28370
|
|
62
|
+
secator/runners/_helpers.py,sha256=kxXfxP4LOCz49p5Y-OKuUqvVAmRPtAoK2O9PHoUxCX0,3947
|
|
63
|
+
secator/runners/command.py,sha256=F6Eg5hLisk6KLXKSBgNIURu2A67heNzmkVF_OYF_Xdo,18688
|
|
64
|
+
secator/runners/scan.py,sha256=ZN6bgb3yqu5wemq_VVqul5VTU64TtYUl_0wkcW1aXRU,1647
|
|
65
|
+
secator/runners/task.py,sha256=c038lkkQ2H1GpLcHp46PEmSSXsIIRA5jnDgw6x8rEAY,2826
|
|
66
|
+
secator/runners/workflow.py,sha256=i2s-lJYIMJfHrxdVwglG9bkCGKu90yUNAM2lfwT6RIA,3689
|
|
66
67
|
secator/serializers/__init__.py,sha256=OP5cmFl77ovgSCW_IDcZ21St2mUt5UK4QHfrsK2KvH8,248
|
|
67
68
|
secator/serializers/dataclass.py,sha256=g5gMT4NwndjhGcGbFuYEs07AZW_Q_m9orov_edVEGlI,792
|
|
68
69
|
secator/serializers/json.py,sha256=XwuSQOBwrOAs16F5HtY-Q-rAGAxfNvlq3z-Nb2gwigE,304
|
|
69
70
|
secator/serializers/regex.py,sha256=hGJ_1JSOv9xPtfn_umHlsjnR_alnsDFv-UmjYCC3vwU,314
|
|
70
71
|
secator/tasks/__init__.py,sha256=Wp2QF5QS2e_BlVygsIEFbmYPTfTg7v_Vd3LQJeXTC7I,344
|
|
71
|
-
secator/tasks/_categories.py,sha256=
|
|
72
|
+
secator/tasks/_categories.py,sha256=2cUsZOdYHA-YXJwryU2FTTT4Y4xXzmDJ92F8ud-MDJQ,10402
|
|
72
73
|
secator/tasks/cariddi.py,sha256=GKVJ8nWtJu9fB_FhAVYA2TX3fMdKYdbMpH2IhCkj_no,3155
|
|
73
74
|
secator/tasks/dalfox.py,sha256=nrLkIbTNz_J7LgUy_3kBgzhTUbQi3RmiSJhc9HWa05c,1744
|
|
74
75
|
secator/tasks/dirsearch.py,sha256=2hJeJZJwaAl3-UAjBwlmjW1w9bxjVWxxwfcaTTxqClc,2387
|
|
75
76
|
secator/tasks/dnsx.py,sha256=H_3z87KAK-ndAQgCwS8TRWaUX_Hh54qEeuKQCS4rjBw,1771
|
|
76
|
-
secator/tasks/dnsxbrute.py,sha256=
|
|
77
|
+
secator/tasks/dnsxbrute.py,sha256=TT1wEU6BSS0VVwk7c5cFRKEnPji3HeK_tLrWtLJBSvk,1245
|
|
77
78
|
secator/tasks/feroxbuster.py,sha256=9QQpd8T0CSMfXf_BMmCX4LeIogyvsc_ccXFJnEocxVo,3011
|
|
78
|
-
secator/tasks/ffuf.py,sha256=
|
|
79
|
+
secator/tasks/ffuf.py,sha256=eJyXY912hg8e_8sibzo7X7704QM4vI4C0o317bcTV-c,2428
|
|
79
80
|
secator/tasks/fping.py,sha256=P2EAPUGgwEC4Geh2zUbBPKF9bdqrlrdDg-R_TYLTFng,1127
|
|
80
81
|
secator/tasks/gau.py,sha256=Sq5l277cGxpT2bB5s1RqrggP804RKbC6xxgLDZZzLFs,1391
|
|
81
82
|
secator/tasks/gf.py,sha256=WlhoEyL6xE79w6nE5XNSXHs-jVeO10njqJxBF8w20sA,945
|
|
82
83
|
secator/tasks/gospider.py,sha256=_UlTb9G5Ss8D68NT53s0_rI6TnG00Ph0yxWyHic7cKs,2172
|
|
83
84
|
secator/tasks/grype.py,sha256=n60Zs9d1NWJFHQ0DwIZib5wu3xH-tV2RzgLYwuQSTo4,2413
|
|
84
85
|
secator/tasks/h8mail.py,sha256=hZBpfV6M1mbpD_PbDHxLI5HMvqAvTeY_W0lbkq3Hugo,2037
|
|
85
|
-
secator/tasks/httpx.py,sha256=
|
|
86
|
-
secator/tasks/katana.py,sha256=
|
|
86
|
+
secator/tasks/httpx.py,sha256=ugk4AOIqhvT5-HVhlRsQw_oF2BGovgHlS-_D9wav8wo,3972
|
|
87
|
+
secator/tasks/katana.py,sha256=3JHtvxoSxJtju9xqvQptVaYXKnLLlCkOn6oY0A8h7wM,4371
|
|
87
88
|
secator/tasks/maigret.py,sha256=PZDTICJ4LZF3joKe-dXu2alffakD_1sxBuNEUBtJDm4,2098
|
|
88
89
|
secator/tasks/mapcidr.py,sha256=7aa2WXQATWgIQo5oA12URjAg80L6MFMGdxScxls8DuA,980
|
|
89
|
-
secator/tasks/msfconsole.py,sha256=
|
|
90
|
+
secator/tasks/msfconsole.py,sha256=Cm0vzOFff17C4M1YjkgU6T7Jc5-ClBK0Qi_529qVRb0,6065
|
|
90
91
|
secator/tasks/naabu.py,sha256=RNs4NCZXgKhPqzR78l6l61tau0mGHuj6C3If7fimpgs,1594
|
|
91
|
-
secator/tasks/nmap.py,sha256=
|
|
92
|
-
secator/tasks/nuclei.py,sha256=
|
|
93
|
-
secator/tasks/searchsploit.py,sha256=
|
|
92
|
+
secator/tasks/nmap.py,sha256=WS071HX7dnb2bP-M4Ah0yNYWva-WTVaj9oVzMMFUvRY,11239
|
|
93
|
+
secator/tasks/nuclei.py,sha256=lKZYPVcnCYomd830-ZCOz4fyc8xAKjNDuKayyz0BPek,3507
|
|
94
|
+
secator/tasks/searchsploit.py,sha256=RD2uv3GFI3Eb-DiTzJp59jyXnvAZRACq-WjDI1NgFM0,1664
|
|
94
95
|
secator/tasks/subfinder.py,sha256=cpFyFCpVaDZ3QAjNId26ezOwntn3CA5Uk-AC2l0mo0E,1087
|
|
95
96
|
secator/tasks/wpscan.py,sha256=UVWnBPOQ1RDB2wzMswWR6vc6cucYgHtuJ8pLZoqCM40,5434
|
|
96
|
-
secator-0.
|
|
97
|
-
secator-0.
|
|
98
|
-
secator-0.
|
|
99
|
-
secator-0.
|
|
100
|
-
secator-0.
|
|
97
|
+
secator-0.4.0.dist-info/METADATA,sha256=sG5y-7z5TSaCkO2_BehY8co5v_ieePZfv2EV9k9K8pc,14095
|
|
98
|
+
secator-0.4.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
|
|
99
|
+
secator-0.4.0.dist-info/entry_points.txt,sha256=lPgsqqUXWgiuGSfKy-se5gHdQlAXIwS_A46NYq7Acic,44
|
|
100
|
+
secator-0.4.0.dist-info/licenses/LICENSE,sha256=19W5Jsy4WTctNkqmZIqLRV1gTDOp01S3LDj9iSgWaJ0,2867
|
|
101
|
+
secator-0.4.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|