secator 0.7.0__py3-none-any.whl → 0.8.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.

Files changed (49) hide show
  1. secator/celery.py +3 -3
  2. secator/cli.py +106 -76
  3. secator/config.py +88 -58
  4. secator/configs/workflows/subdomain_recon.yaml +2 -2
  5. secator/configs/workflows/url_dirsearch.yaml +1 -1
  6. secator/decorators.py +1 -0
  7. secator/definitions.py +1 -1
  8. secator/installer.py +277 -60
  9. secator/output_types/error.py +3 -3
  10. secator/output_types/exploit.py +11 -7
  11. secator/output_types/info.py +2 -2
  12. secator/output_types/ip.py +1 -1
  13. secator/output_types/port.py +3 -3
  14. secator/output_types/record.py +4 -4
  15. secator/output_types/stat.py +2 -2
  16. secator/output_types/subdomain.py +1 -1
  17. secator/output_types/tag.py +3 -3
  18. secator/output_types/target.py +2 -2
  19. secator/output_types/url.py +11 -11
  20. secator/output_types/user_account.py +6 -6
  21. secator/output_types/vulnerability.py +5 -4
  22. secator/output_types/warning.py +2 -2
  23. secator/report.py +1 -0
  24. secator/runners/_base.py +17 -13
  25. secator/runners/command.py +44 -7
  26. secator/tasks/_categories.py +145 -43
  27. secator/tasks/bbot.py +2 -0
  28. secator/tasks/bup.py +1 -0
  29. secator/tasks/dirsearch.py +2 -2
  30. secator/tasks/dnsxbrute.py +2 -1
  31. secator/tasks/feroxbuster.py +2 -3
  32. secator/tasks/fping.py +1 -1
  33. secator/tasks/grype.py +2 -4
  34. secator/tasks/h8mail.py +1 -1
  35. secator/tasks/katana.py +1 -1
  36. secator/tasks/maigret.py +1 -1
  37. secator/tasks/msfconsole.py +18 -3
  38. secator/tasks/naabu.py +15 -1
  39. secator/tasks/nmap.py +32 -20
  40. secator/tasks/nuclei.py +4 -1
  41. secator/tasks/searchsploit.py +9 -2
  42. secator/tasks/wpscan.py +12 -1
  43. secator/template.py +1 -1
  44. secator/utils.py +151 -62
  45. {secator-0.7.0.dist-info → secator-0.8.0.dist-info}/METADATA +50 -45
  46. {secator-0.7.0.dist-info → secator-0.8.0.dist-info}/RECORD +49 -49
  47. {secator-0.7.0.dist-info → secator-0.8.0.dist-info}/WHEEL +1 -1
  48. {secator-0.7.0.dist-info → secator-0.8.0.dist-info}/entry_points.txt +0 -0
  49. {secator-0.7.0.dist-info → secator-0.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -4,6 +4,7 @@ from secator.config import CONFIG
4
4
  from secator.output_types import Subdomain
5
5
  from secator.serializers import JSONSerializer
6
6
  from secator.tasks._categories import ReconDns
7
+ from secator.utils import process_wordlist
7
8
 
8
9
 
9
10
  @task()
@@ -19,7 +20,7 @@ class dnsxbrute(ReconDns):
19
20
  THREADS: 'threads',
20
21
  }
21
22
  opts = {
22
- WORDLIST: {'type': str, 'short': 'w', 'default': CONFIG.wordlists.defaults.dns, 'help': 'Wordlist'},
23
+ WORDLIST: {'type': str, 'short': 'w', 'default': CONFIG.wordlists.defaults.dns, 'process': process_wordlist, 'help': 'Wordlist to use'}, # noqa: E501
23
24
  'trace': {'is_flag': True, 'default': False, 'help': 'Perform dns tracing'},
24
25
  }
25
26
  item_loaders = [JSONSerializer()]
@@ -1,3 +1,4 @@
1
+ from secator.config import CONFIG
1
2
  from secator.decorators import task
2
3
  from secator.definitions import (CONTENT_TYPE, DELAY, DEPTH, FILTER_CODES,
3
4
  FILTER_REGEX, FILTER_SIZE, FILTER_WORDS,
@@ -59,9 +60,7 @@ class feroxbuster(HttpFuzzer):
59
60
  }
60
61
  }
61
62
  install_cmd = (
62
- 'sudo apt install -y unzip curl && '
63
- 'curl -sL https://raw.githubusercontent.com/epi052/feroxbuster/master/install-nix.sh | '
64
- 'bash && sudo mv feroxbuster /usr/local/bin'
63
+ f'cd /tmp && curl -sL https://raw.githubusercontent.com/epi052/feroxbuster/master/install-nix.sh | bash -s {CONFIG.dirs.bin}' # noqa: E501
65
64
  )
66
65
  install_github_handle = 'epi052/feroxbuster'
67
66
  proxychains = False
secator/tasks/fping.py CHANGED
@@ -29,7 +29,7 @@ class fping(ReconIp):
29
29
  }
30
30
  input_type = IP
31
31
  output_types = [Ip]
32
- install_cmd = 'sudo apt install -y fping'
32
+ install_pre = {'*': ['fping']}
33
33
 
34
34
  @staticmethod
35
35
  def item_loader(self, line):
secator/tasks/grype.py CHANGED
@@ -1,4 +1,4 @@
1
-
1
+ from secator.config import CONFIG
2
2
  from secator.decorators import task
3
3
  from secator.definitions import (DELAY, FOLLOW_REDIRECT, HEADER,
4
4
  OPT_NOT_SUPPORTED, PROXY, RATE_LIMIT, RETRIES,
@@ -28,7 +28,7 @@ class grype(VulnCode):
28
28
  }
29
29
  output_types = [Vulnerability]
30
30
  install_cmd = (
31
- 'curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sudo sh -s -- -b /usr/local/bin'
31
+ f'curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b {CONFIG.dirs.bin}'
32
32
  )
33
33
  install_github_handle = 'anchore/grype'
34
34
 
@@ -63,7 +63,6 @@ class grype(VulnCode):
63
63
  if vuln_id.startswith('GHSA'):
64
64
  data['provider'] = 'github.com'
65
65
  data['references'] = [f'https://github.com/advisories/{vuln_id}']
66
- data['tags'].extend(['cve', 'ghsa'])
67
66
  vuln = VulnCode.lookup_ghsa(vuln_id)
68
67
  if vuln:
69
68
  data.update(vuln)
@@ -72,7 +71,6 @@ class grype(VulnCode):
72
71
  elif vuln_id.startswith('CVE'):
73
72
  vuln = VulnCode.lookup_cve(vuln_id)
74
73
  if vuln:
75
- vuln['tags'].append('cve')
76
74
  data.update(vuln)
77
75
  data['severity'] = data['severity'] or severity.lower()
78
76
  data['extra_data'] = extra_data
secator/tasks/h8mail.py CHANGED
@@ -21,7 +21,7 @@ class h8mail(OSInt):
21
21
  'config': {'type': str, 'help': 'Configuration file for API keys'},
22
22
  'local_breach': {'type': str, 'short': 'lb', 'help': 'Local breach file'}
23
23
  }
24
- install_cmd = 'pipx install h8mail'
24
+ install_cmd = 'pipx install h8mail && pipx upgrade h8mail'
25
25
 
26
26
  @staticmethod
27
27
  def on_start(self):
secator/tasks/katana.py CHANGED
@@ -74,7 +74,7 @@ class katana(HttpCrawler):
74
74
  # TAGS: lambda x: x['response'].get('server')
75
75
  }
76
76
  }
77
- install_cmd = 'sudo apt install build-essential && go install -v github.com/projectdiscovery/katana/cmd/katana@latest'
77
+ install_cmd = 'go install -v github.com/projectdiscovery/katana/cmd/katana@latest'
78
78
  install_github_handle = 'projectdiscovery/katana'
79
79
  proxychains = False
80
80
  proxy_socks5 = True
secator/tasks/maigret.py CHANGED
@@ -41,7 +41,7 @@ class maigret(ReconUser):
41
41
  EXTRA_DATA: lambda x: x['status'].get('ids', {})
42
42
  }
43
43
  }
44
- install_cmd = 'pipx install git+https://github.com/soxoj/maigret@6be2f409e58056b1ca8571a8151e53bef107dedc'
44
+ install_cmd = 'pipx install git+https://github.com/soxoj/maigret'
45
45
  socks5_proxy = True
46
46
  profile = 'io'
47
47
 
@@ -4,6 +4,7 @@ import logging
4
4
 
5
5
  from rich.panel import Panel
6
6
 
7
+ from secator.config import CONFIG
7
8
  from secator.decorators import task
8
9
  from secator.definitions import (DELAY, FOLLOW_REDIRECT, HEADER, HOST, OPT_NOT_SUPPORTED, PROXY, RATE_LIMIT, RETRIES,
9
10
  THREADS, TIMEOUT, USER_AGENT)
@@ -42,7 +43,21 @@ class msfconsole(VulnMulti):
42
43
  }
43
44
  encoding = 'ansi'
44
45
  ignore_return_code = True
45
- # install_cmd = 'wget -O - https://raw.githubusercontent.com/freelabz/secator/main/scripts/msfinstall.sh | sh'
46
+ install_pre = {
47
+ 'apt|apk': ['libpq-dev', 'libpcap-dev', 'libffi-dev'],
48
+ 'pacman': ['ruby-erb', 'postgresql-libs'],
49
+ 'yum|zypper': ['postgresql-devel'],
50
+ }
51
+ install_cmd = (
52
+ f'git clone https://github.com/rapid7/metasploit-framework.git {CONFIG.dirs.share}/metasploit-framework || true && '
53
+ f'cd {CONFIG.dirs.share}/metasploit-framework && '
54
+ f'gem install bundler --user-install -n {CONFIG.dirs.bin} && '
55
+ f'gem install xmlrpc --user-install -n {CONFIG.dirs.bin} && '
56
+ f'bundle config set --local path "{CONFIG.dirs.share}" && '
57
+ 'bundle update --bundler && '
58
+ 'bundle install && '
59
+ f'ln -sf $HOME/.local/share/metasploit-framework/msfconsole {CONFIG.dirs.bin}/msfconsole'
60
+ )
46
61
 
47
62
  @staticmethod
48
63
  def on_init(self):
@@ -83,14 +98,14 @@ class msfconsole(VulnMulti):
83
98
  f.write(content)
84
99
 
85
100
  script_name = script_path.split('/')[-1]
86
- self._print(Panel(content, title=f'[bold magenta]{script_name}', expand=False))
101
+ self._print(Panel(content, title=f'[bold magenta]{script_name}', expand=False), rich=True)
87
102
 
88
103
  # Override original command with new resource script
89
104
  self.run_opts['msfconsole.resource'] = out_path
90
105
 
91
106
  # Nothing passed, error out
92
107
  else:
93
- raise ValueError('At least one of "inline_script" or "resource_script" must be passed.')
108
+ raise ValueError('At least one of "execute_command" or "resource" must be passed.')
94
109
 
95
110
 
96
111
  # TODO: This is better as it goes through an RPC API to communicate with
secator/tasks/naabu.py CHANGED
@@ -47,9 +47,23 @@ class naabu(ReconPort):
47
47
  }
48
48
  }
49
49
  output_types = [Port]
50
- install_cmd = 'sudo apt install -y build-essential libpcap-dev && go install -v github.com/projectdiscovery/naabu/v2/cmd/naabu@latest' # noqa: E501
50
+ install_cmd = 'go install -v github.com/projectdiscovery/naabu/v2/cmd/naabu@latest'
51
51
  install_github_handle = 'projectdiscovery/naabu'
52
+ install_pre = {'apt|apk': ['libpcap-dev'], 'pacman|brew': ['libpcap']}
53
+ install_post = {'arch|alpine': 'sudo ln -s /usr/lib/libpcap.so /usr/lib/libpcap.so.0.8'}
52
54
  proxychains = False
53
55
  proxy_socks5 = True
54
56
  proxy_http = False
55
57
  profile = 'io'
58
+
59
+ @staticmethod
60
+ def before_init(self):
61
+ for ix, input in enumerate(self.inputs):
62
+ if input == 'localhost':
63
+ self.inputs[ix] = '127.0.0.1'
64
+
65
+ @staticmethod
66
+ def on_item(self, item):
67
+ if item.host == '127.0.0.1':
68
+ item.host = 'localhost'
69
+ return item
secator/tasks/nmap.py CHANGED
@@ -63,8 +63,12 @@ class nmap(VulnMulti):
63
63
  opt_value_map = {
64
64
  PORTS: lambda x: ','.join([str(p) for p in x]) if isinstance(x, list) else x
65
65
  }
66
+ install_pre = {
67
+ 'apt|pacman|brew': ['nmap'],
68
+ 'apk': ['nmap', 'nmap-scripts'],
69
+ }
66
70
  install_cmd = (
67
- 'sudo apt install -y nmap && sudo git clone https://github.com/scipag/vulscan /opt/scipag_vulscan || true && '
71
+ 'sudo git clone https://github.com/scipag/vulscan /opt/scipag_vulscan || true && '
68
72
  'sudo ln -s /opt/scipag_vulscan /usr/share/nmap/scripts/vulscan || true'
69
73
  )
70
74
  proxychains = True
@@ -116,6 +120,7 @@ class nmap(VulnMulti):
116
120
  class nmapData(dict):
117
121
 
118
122
  def __iter__(self):
123
+ datas = []
119
124
  for host in self._get_hosts():
120
125
  hostname = self._get_hostname(host)
121
126
  ip = self._get_ip(host)
@@ -177,16 +182,19 @@ class nmapData(dict):
177
182
  if not func:
178
183
  debug(f'Script output parser for "{script_id}" is not supported YET.', sub='cve')
179
184
  continue
180
- for vuln in func(output, cpes=cpes):
181
- vuln.update(metadata)
185
+ for data in func(output, cpes=cpes):
186
+ data.update(metadata)
182
187
  confidence = 'low'
183
- if 'cpe-match' in vuln[TAGS]:
188
+ if 'cpe-match' in data[TAGS]:
184
189
  confidence = 'high' if version_exact else 'medium'
185
- vuln[CONFIDENCE] = confidence
186
- if (CONFIG.runners.skip_cve_low_confidence and vuln[CONFIDENCE] == 'low'):
187
- debug(f'{vuln[ID]}: ignored (low confidence).', sub='cve')
190
+ data[CONFIDENCE] = confidence
191
+ if (CONFIG.runners.skip_cve_low_confidence and data[CONFIDENCE] == 'low'):
192
+ debug(f'{data[ID]}: ignored (low confidence).', sub='cve')
193
+ continue
194
+ if data in datas:
188
195
  continue
189
- yield vuln
196
+ yield data
197
+ datas.append(data)
190
198
 
191
199
  #---------------------#
192
200
  # XML FILE EXTRACTORS #
@@ -261,7 +269,7 @@ class nmapData(dict):
261
269
  extra_data['version_exact'] = version_exact
262
270
 
263
271
  # Grap service name
264
- product = extra_data.get('name', None) or extra_data.get('product', None)
272
+ product = extra_data.get('product', None) or extra_data.get('name', None)
265
273
  if product:
266
274
  service_name = product
267
275
  if version:
@@ -339,12 +347,12 @@ class nmapData(dict):
339
347
  TAGS: [vuln_id, provider_name]
340
348
  }
341
349
  if provider_name == 'MITRE CVE':
342
- data = VulnMulti.lookup_cve(vuln['id'], cpes=cpes)
350
+ data = VulnMulti.lookup_cve(vuln['id'], *cpes)
343
351
  if data:
344
352
  vuln.update(data)
345
353
  yield vuln
346
354
  else:
347
- debug(f'Vulscan provider {provider_name} is not supported YET.', sub='cve')
355
+ debug(f'Vulscan provider {provider_name} is not supported YET.', sub='cve.provider', verbose=True)
348
356
  continue
349
357
 
350
358
  def _parse_vulners_output(self, out, **kwargs):
@@ -358,30 +366,35 @@ class nmapData(dict):
358
366
  cpes.append(line.rstrip(':'))
359
367
  continue
360
368
  elems = tuple(line.split('\t'))
361
- vuln = {}
362
369
 
363
370
  if len(elems) == 4: # exploit
364
371
  # TODO: Implement exploit processing
365
372
  exploit_id, cvss_score, reference_url, _ = elems
366
373
  name = exploit_id
367
374
  # edb_id = name.split(':')[-1] if 'EDB-ID' in name else None
368
- vuln = {
375
+ exploit = {
369
376
  ID: exploit_id,
370
377
  NAME: name,
371
378
  PROVIDER: provider_name,
372
379
  REFERENCE: reference_url,
380
+ TAGS: [exploit_id, provider_name],
381
+ CVSS_SCORE: cvss_score,
382
+ CONFIDENCE: 'low',
373
383
  '_type': 'exploit',
374
- TAGS: [exploit_id, provider_name]
375
- # CVSS_SCORE: cvss_score,
376
- # CONFIDENCE: 'low'
377
384
  }
378
385
  # TODO: lookup exploit in ExploitDB to find related CVEs
379
386
  # if edb_id:
380
387
  # print(edb_id)
381
- # vuln_data = VulnMulti.lookup_exploitdb(edb_id)
382
- yield vuln
388
+ # exploit_data = VulnMulti.lookup_exploitdb(edb_id)
389
+ vuln = VulnMulti.lookup_cve_from_vulners_exploit(exploit_id, *cpes)
390
+ if vuln:
391
+ yield vuln
392
+ exploit[TAGS].extend(vuln[TAGS])
393
+ exploit[CONFIDENCE] = vuln[CONFIDENCE]
394
+ yield exploit
383
395
 
384
396
  elif len(elems) == 3: # vuln
397
+ vuln = {}
385
398
  vuln_id, vuln_cvss, reference_url = tuple(line.split('\t'))
386
399
  vuln_cvss = float(vuln_cvss)
387
400
  vuln_id = vuln_id.split(':')[-1]
@@ -397,8 +410,7 @@ class nmapData(dict):
397
410
  CONFIDENCE: 'low'
398
411
  }
399
412
  if vuln_type == 'CVE' or vuln_type == 'PRION:CVE':
400
- vuln[TAGS].append('cve')
401
- data = VulnMulti.lookup_cve(vuln_id, cpes=cpes)
413
+ data = VulnMulti.lookup_cve(vuln_id, *cpes)
402
414
  if data:
403
415
  vuln.update(data)
404
416
  yield vuln
secator/tasks/nuclei.py CHANGED
@@ -74,8 +74,11 @@ class nuclei(VulnMulti):
74
74
  }
75
75
  }
76
76
  ignore_return_code = True
77
- install_cmd = 'go install -v github.com/projectdiscovery/nuclei/v2/cmd/nuclei@latest && nuclei update-templates'
77
+ install_cmd = 'go install -v github.com/projectdiscovery/nuclei/v2/cmd/nuclei@latest'
78
78
  install_github_handle = 'projectdiscovery/nuclei'
79
+ install_post = {
80
+ '*': 'nuclei -ut'
81
+ }
79
82
  proxychains = False
80
83
  proxy_socks5 = True # kind of, leaks data when running network / dns templates
81
84
  proxy_http = True # same
@@ -1,5 +1,6 @@
1
1
  import re
2
2
 
3
+ from secator.config import CONFIG
3
4
  from secator.decorators import task
4
5
  from secator.definitions import (CVES, EXTRA_DATA, ID, MATCHED_AT, NAME,
5
6
  PROVIDER, REFERENCE, TAGS, OPT_NOT_SUPPORTED)
@@ -13,7 +14,7 @@ SEARCHSPLOIT_TITLE_REGEX = re.compile(r'^((?:[a-zA-Z\-_!\.()]+\d?\s?)+)\.?\s*(.*
13
14
 
14
15
  @task()
15
16
  class searchsploit(Command):
16
- """Exploit-DB command line search tool."""
17
+ """Exploit searcher based on ExploitDB."""
17
18
  cmd = 'searchsploit'
18
19
  input_flag = None
19
20
  json_flag = '--json'
@@ -37,7 +38,13 @@ class searchsploit(Command):
37
38
  }
38
39
  }
39
40
  }
40
- 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
41
+ install_pre = {
42
+ 'apk': ['ncurses']
43
+ }
44
+ install_cmd = (
45
+ f'git clone https://gitlab.com/exploit-database/exploitdb.git {CONFIG.dirs.share}/exploitdb || true && '
46
+ f'ln -sf $HOME/.local/share/exploitdb/searchsploit {CONFIG.dirs.bin}/searchsploit'
47
+ )
41
48
  proxychains = False
42
49
  proxy_socks5 = False
43
50
  proxy_http = False
secator/tasks/wpscan.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  import os
3
3
 
4
+ from secator.config import CONFIG
4
5
  from secator.decorators import task
5
6
  from secator.definitions import (CONFIDENCE, CVSS_SCORE, DELAY, DESCRIPTION,
6
7
  EXTRA_DATA, FOLLOW_REDIRECT, HEADER, ID,
@@ -66,7 +67,17 @@ class wpscan(VulnHttp):
66
67
  },
67
68
  }
68
69
  output_types = [Vulnerability, Tag]
69
- install_cmd = 'sudo apt install -y build-essential ruby-dev rubygems && sudo gem install wpscan'
70
+ install_pre = {
71
+ 'apt': ['kali:libcurl4t64', 'libffi-dev'],
72
+ 'pacman': ['ruby-erb'],
73
+ }
74
+ install_cmd = f'gem install wpscan --user-install -n {CONFIG.dirs.bin}'
75
+ install_post = {
76
+ 'kali': (
77
+ f'gem uninstall nokogiri --user-install -n {CONFIG.dirs.bin} --force --executables && '
78
+ f'gem install nokogiri --user-install -n {CONFIG.dirs.bin} --platform=ruby'
79
+ )
80
+ }
70
81
  proxychains = False
71
82
  proxy_http = True
72
83
  proxy_socks5 = False
secator/template.py CHANGED
@@ -120,7 +120,7 @@ class TemplateLoader(DotMap):
120
120
 
121
121
  def parse_config(config, prefix=''):
122
122
  for key, value in config.items():
123
- if key == '_group':
123
+ if key.startswith('_group'):
124
124
  parse_config(value, prefix)
125
125
  elif value:
126
126
  task_name = f'{prefix}/{key}' if prefix else key