bbot 2.3.0.5370rc0__py3-none-any.whl → 2.3.0.5382rc0__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 bbot might be problematic. Click here for more details.

Files changed (157) hide show
  1. bbot/__init__.py +1 -1
  2. bbot/cli.py +2 -6
  3. bbot/core/config/files.py +0 -1
  4. bbot/core/config/logger.py +1 -1
  5. bbot/core/core.py +1 -1
  6. bbot/core/event/base.py +13 -16
  7. bbot/core/helpers/command.py +4 -4
  8. bbot/core/helpers/depsinstaller/installer.py +5 -5
  9. bbot/core/helpers/diff.py +7 -7
  10. bbot/core/helpers/dns/brute.py +1 -1
  11. bbot/core/helpers/dns/dns.py +1 -2
  12. bbot/core/helpers/dns/engine.py +4 -6
  13. bbot/core/helpers/dns/mock.py +0 -1
  14. bbot/core/helpers/files.py +1 -1
  15. bbot/core/helpers/helper.py +3 -1
  16. bbot/core/helpers/interactsh.py +3 -3
  17. bbot/core/helpers/libmagic.py +0 -1
  18. bbot/core/helpers/misc.py +11 -11
  19. bbot/core/helpers/process.py +0 -2
  20. bbot/core/helpers/regex.py +1 -1
  21. bbot/core/helpers/regexes.py +3 -3
  22. bbot/core/helpers/validators.py +1 -2
  23. bbot/core/helpers/web/client.py +1 -1
  24. bbot/core/helpers/web/engine.py +1 -2
  25. bbot/core/helpers/web/web.py +2 -3
  26. bbot/core/helpers/wordcloud.py +5 -5
  27. bbot/core/modules.py +21 -22
  28. bbot/db/sql/models.py +0 -1
  29. bbot/modules/azure_tenant.py +2 -2
  30. bbot/modules/baddns.py +0 -2
  31. bbot/modules/baddns_direct.py +0 -1
  32. bbot/modules/base.py +16 -16
  33. bbot/modules/bypass403.py +5 -5
  34. bbot/modules/c99.py +1 -1
  35. bbot/modules/columbus.py +1 -1
  36. bbot/modules/deadly/ffuf.py +8 -8
  37. bbot/modules/deadly/nuclei.py +1 -1
  38. bbot/modules/deadly/vhost.py +3 -3
  39. bbot/modules/dnsbimi.py +1 -1
  40. bbot/modules/dnsdumpster.py +2 -2
  41. bbot/modules/dockerhub.py +1 -1
  42. bbot/modules/dotnetnuke.py +0 -2
  43. bbot/modules/extractous.py +1 -1
  44. bbot/modules/filedownload.py +1 -1
  45. bbot/modules/generic_ssrf.py +3 -3
  46. bbot/modules/github_workflows.py +1 -1
  47. bbot/modules/gowitness.py +7 -7
  48. bbot/modules/host_header.py +5 -5
  49. bbot/modules/httpx.py +1 -1
  50. bbot/modules/iis_shortnames.py +6 -6
  51. bbot/modules/internal/cloudcheck.py +5 -5
  52. bbot/modules/internal/dnsresolve.py +7 -7
  53. bbot/modules/internal/excavate.py +23 -26
  54. bbot/modules/internal/speculate.py +4 -4
  55. bbot/modules/ipneighbor.py +1 -1
  56. bbot/modules/jadx.py +1 -1
  57. bbot/modules/newsletters.py +2 -2
  58. bbot/modules/output/asset_inventory.py +6 -6
  59. bbot/modules/output/base.py +1 -1
  60. bbot/modules/output/csv.py +1 -1
  61. bbot/modules/output/stdout.py +2 -2
  62. bbot/modules/paramminer_headers.py +4 -7
  63. bbot/modules/portscan.py +3 -3
  64. bbot/modules/report/asn.py +11 -11
  65. bbot/modules/robots.py +3 -3
  66. bbot/modules/securitytxt.py +1 -1
  67. bbot/modules/sitedossier.py +1 -1
  68. bbot/modules/social.py +1 -1
  69. bbot/modules/subdomainradar.py +1 -1
  70. bbot/modules/telerik.py +7 -7
  71. bbot/modules/templates/bucket.py +1 -1
  72. bbot/modules/templates/github.py +1 -1
  73. bbot/modules/templates/shodan.py +1 -1
  74. bbot/modules/templates/subdomain_enum.py +1 -1
  75. bbot/modules/templates/webhook.py +1 -1
  76. bbot/modules/trufflehog.py +2 -2
  77. bbot/modules/url_manipulation.py +3 -3
  78. bbot/modules/urlscan.py +1 -1
  79. bbot/modules/viewdns.py +1 -1
  80. bbot/modules/wafw00f.py +1 -1
  81. bbot/scanner/preset/args.py +10 -11
  82. bbot/scanner/preset/environ.py +0 -1
  83. bbot/scanner/preset/preset.py +9 -9
  84. bbot/scanner/scanner.py +17 -17
  85. bbot/scanner/target.py +1 -1
  86. bbot/scripts/docs.py +1 -1
  87. bbot/test/bbot_fixtures.py +1 -1
  88. bbot/test/conftest.py +1 -1
  89. bbot/test/run_tests.sh +4 -4
  90. bbot/test/test_step_1/test_bbot_fastapi.py +2 -2
  91. bbot/test/test_step_1/test_cli.py +56 -56
  92. bbot/test/test_step_1/test_dns.py +15 -15
  93. bbot/test/test_step_1/test_engine.py +17 -17
  94. bbot/test/test_step_1/test_events.py +22 -22
  95. bbot/test/test_step_1/test_helpers.py +26 -26
  96. bbot/test/test_step_1/test_manager_scope_accuracy.py +306 -306
  97. bbot/test/test_step_1/test_modules_basic.py +52 -53
  98. bbot/test/test_step_1/test_presets.py +81 -81
  99. bbot/test/test_step_1/test_regexes.py +5 -5
  100. bbot/test/test_step_1/test_scan.py +4 -4
  101. bbot/test/test_step_1/test_target.py +25 -25
  102. bbot/test/test_step_1/test_web.py +5 -5
  103. bbot/test/test_step_2/module_tests/base.py +6 -6
  104. bbot/test/test_step_2/module_tests/test_module_anubisdb.py +1 -1
  105. bbot/test/test_step_2/module_tests/test_module_asset_inventory.py +0 -1
  106. bbot/test/test_step_2/module_tests/test_module_azure_realm.py +1 -1
  107. bbot/test/test_step_2/module_tests/test_module_baddns.py +6 -6
  108. bbot/test/test_step_2/module_tests/test_module_baddns_direct.py +2 -4
  109. bbot/test/test_step_2/module_tests/test_module_bevigil.py +4 -4
  110. bbot/test/test_step_2/module_tests/test_module_binaryedge.py +2 -2
  111. bbot/test/test_step_2/module_tests/test_module_bucket_amazon.py +2 -2
  112. bbot/test/test_step_2/module_tests/test_module_bucket_azure.py +1 -1
  113. bbot/test/test_step_2/module_tests/test_module_builtwith.py +2 -2
  114. bbot/test/test_step_2/module_tests/test_module_c99.py +9 -9
  115. bbot/test/test_step_2/module_tests/test_module_columbus.py +1 -1
  116. bbot/test/test_step_2/module_tests/test_module_credshed.py +2 -2
  117. bbot/test/test_step_2/module_tests/test_module_dehashed.py +1 -1
  118. bbot/test/test_step_2/module_tests/test_module_digitorus.py +1 -1
  119. bbot/test/test_step_2/module_tests/test_module_dnsbrute.py +8 -9
  120. bbot/test/test_step_2/module_tests/test_module_dnsbrute_mutations.py +0 -1
  121. bbot/test/test_step_2/module_tests/test_module_dnscommonsrv.py +0 -1
  122. bbot/test/test_step_2/module_tests/test_module_dnsdumpster.py +2 -2
  123. bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py +0 -2
  124. bbot/test/test_step_2/module_tests/test_module_excavate.py +10 -30
  125. bbot/test/test_step_2/module_tests/test_module_extractous.py +9 -9
  126. bbot/test/test_step_2/module_tests/test_module_filedownload.py +14 -14
  127. bbot/test/test_step_2/module_tests/test_module_git_clone.py +2 -2
  128. bbot/test/test_step_2/module_tests/test_module_gowitness.py +4 -4
  129. bbot/test/test_step_2/module_tests/test_module_host_header.py +1 -1
  130. bbot/test/test_step_2/module_tests/test_module_http.py +4 -4
  131. bbot/test/test_step_2/module_tests/test_module_httpx.py +7 -7
  132. bbot/test/test_step_2/module_tests/test_module_leakix.py +2 -2
  133. bbot/test/test_step_2/module_tests/test_module_myssl.py +1 -1
  134. bbot/test/test_step_2/module_tests/test_module_neo4j.py +1 -1
  135. bbot/test/test_step_2/module_tests/test_module_newsletters.py +6 -6
  136. bbot/test/test_step_2/module_tests/test_module_ntlm.py +7 -7
  137. bbot/test/test_step_2/module_tests/test_module_oauth.py +1 -1
  138. bbot/test/test_step_2/module_tests/test_module_otx.py +1 -1
  139. bbot/test/test_step_2/module_tests/test_module_paramminer_cookies.py +1 -2
  140. bbot/test/test_step_2/module_tests/test_module_paramminer_getparams.py +0 -6
  141. bbot/test/test_step_2/module_tests/test_module_paramminer_headers.py +2 -9
  142. bbot/test/test_step_2/module_tests/test_module_portscan.py +3 -4
  143. bbot/test/test_step_2/module_tests/test_module_postgres.py +1 -1
  144. bbot/test/test_step_2/module_tests/test_module_rapiddns.py +9 -9
  145. bbot/test/test_step_2/module_tests/test_module_sitedossier.py +2 -2
  146. bbot/test/test_step_2/module_tests/test_module_smuggler.py +1 -1
  147. bbot/test/test_step_2/module_tests/test_module_speculate.py +2 -6
  148. bbot/test/test_step_2/module_tests/test_module_splunk.py +4 -4
  149. bbot/test/test_step_2/module_tests/test_module_subdomaincenter.py +1 -1
  150. bbot/test/test_step_2/module_tests/test_module_subdomains.py +1 -1
  151. bbot/test/test_step_2/module_tests/test_module_trufflehog.py +2 -2
  152. bbot/test/test_step_2/module_tests/test_module_wayback.py +1 -1
  153. {bbot-2.3.0.5370rc0.dist-info → bbot-2.3.0.5382rc0.dist-info}/METADATA +2 -2
  154. {bbot-2.3.0.5370rc0.dist-info → bbot-2.3.0.5382rc0.dist-info}/RECORD +157 -157
  155. {bbot-2.3.0.5370rc0.dist-info → bbot-2.3.0.5382rc0.dist-info}/LICENSE +0 -0
  156. {bbot-2.3.0.5370rc0.dist-info → bbot-2.3.0.5382rc0.dist-info}/WHEEL +0 -0
  157. {bbot-2.3.0.5370rc0.dist-info → bbot-2.3.0.5382rc0.dist-info}/entry_points.txt +0 -0
bbot/modules/gowitness.py CHANGED
@@ -88,7 +88,7 @@ class gowitness(BaseModule):
88
88
  self.screenshot_path = self.base_path / "screenshots"
89
89
  self.command = self.construct_command()
90
90
  self.prepped = False
91
- self.screenshots_taken = dict()
91
+ self.screenshots_taken = {}
92
92
  self.connections_logged = set()
93
93
  self.technologies_found = set()
94
94
  return True
@@ -172,7 +172,7 @@ class gowitness(BaseModule):
172
172
 
173
173
  # emit technologies
174
174
  new_technologies = await self.get_new_technologies()
175
- for _, row in new_technologies.items():
175
+ for row in new_technologies.values():
176
176
  parent_id = row["url_id"]
177
177
  parent_url = self.screenshots_taken[parent_id]
178
178
  parent_event = event_dict[parent_url]
@@ -227,7 +227,7 @@ class gowitness(BaseModule):
227
227
  return screenshots
228
228
 
229
229
  async def get_new_network_logs(self):
230
- network_logs = dict()
230
+ network_logs = {}
231
231
  if self.db_path.is_file():
232
232
  async with aiosqlite.connect(str(self.db_path)) as con:
233
233
  con.row_factory = aiosqlite.Row
@@ -241,7 +241,7 @@ class gowitness(BaseModule):
241
241
  return network_logs
242
242
 
243
243
  async def get_new_technologies(self):
244
- technologies = dict()
244
+ technologies = {}
245
245
  if self.db_path.is_file():
246
246
  async with aiosqlite.connect(str(self.db_path)) as con:
247
247
  con.row_factory = aiosqlite.Row
@@ -264,8 +264,8 @@ class gowitness(BaseModule):
264
264
  async def report(self):
265
265
  if self.screenshots_taken:
266
266
  self.success(f"{len(self.screenshots_taken):,} web screenshots captured. To view:")
267
- self.success(f" - Start gowitness")
267
+ self.success(" - Start gowitness")
268
268
  self.success(f" - cd {self.base_path} && ./gowitness server")
269
- self.success(f" - Browse to http://localhost:7171")
269
+ self.success(" - Browse to http://localhost:7171")
270
270
  else:
271
- self.info(f"No web screenshots captured")
271
+ self.info("No web screenshots captured")
@@ -19,7 +19,7 @@ class host_header(BaseModule):
19
19
 
20
20
  async def setup(self):
21
21
  self.subdomain_tags = {}
22
- if self.scan.config.get("interactsh_disable", False) == False:
22
+ if self.scan.config.get("interactsh_disable", False) is False:
23
23
  try:
24
24
  self.interactsh_instance = self.helpers.interactsh()
25
25
  self.domain = await self.interactsh_instance.register(callback=self.interactsh_callback)
@@ -60,7 +60,7 @@ class host_header(BaseModule):
60
60
  self.debug("skipping results because subdomain tag was missing")
61
61
 
62
62
  async def finish(self):
63
- if self.scan.config.get("interactsh_disable", False) == False:
63
+ if self.scan.config.get("interactsh_disable", False) is False:
64
64
  await self.helpers.sleep(5)
65
65
  try:
66
66
  for r in await self.interactsh_instance.poll():
@@ -69,7 +69,7 @@ class host_header(BaseModule):
69
69
  self.debug(f"Error in interact.sh: {e}")
70
70
 
71
71
  async def cleanup(self):
72
- if self.scan.config.get("interactsh_disable", False) == False:
72
+ if self.scan.config.get("interactsh_disable", False) is False:
73
73
  try:
74
74
  await self.interactsh_instance.deregister()
75
75
  self.debug(
@@ -84,7 +84,7 @@ class host_header(BaseModule):
84
84
 
85
85
  added_cookies = {}
86
86
 
87
- for header, header_values in event.data["header-dict"].items():
87
+ for header_values in event.data["header-dict"].values():
88
88
  for header_value in header_values:
89
89
  if header_value.lower() == "set-cookie":
90
90
  header_split = header_value.split("=")
@@ -136,7 +136,7 @@ class host_header(BaseModule):
136
136
 
137
137
  split_output = output.split("\n")
138
138
  if " 4" in split_output:
139
- description = f"Duplicate Host Header Tolerated"
139
+ description = "Duplicate Host Header Tolerated"
140
140
  await self.emit_event(
141
141
  {
142
142
  "host": str(event.host),
bbot/modules/httpx.py CHANGED
@@ -90,7 +90,7 @@ class httpx(BaseModule):
90
90
  else:
91
91
  url = str(event.data)
92
92
  url_hash = hash((event.host, event.port, has_spider_max))
93
- if url_hash == None:
93
+ if url_hash is None:
94
94
  url_hash = hash((url, has_spider_max))
95
95
  return url, url_hash
96
96
 
@@ -39,7 +39,7 @@ class iis_shortnames(BaseModule):
39
39
  test_url = f"{target}*~1*/a.aspx"
40
40
 
41
41
  for method in ["GET", "POST", "OPTIONS", "DEBUG", "HEAD", "TRACE"]:
42
- kwargs = dict(method=method, allow_redirects=False, timeout=10)
42
+ kwargs = {"method": method, "allow_redirects": False, "timeout": 10}
43
43
  confirmations = 0
44
44
  iterations = 5 # one failed detection is tolerated, as long as its not the first run
45
45
  while iterations > 0:
@@ -128,7 +128,7 @@ class iis_shortnames(BaseModule):
128
128
  suffix = "/a.aspx"
129
129
 
130
130
  urls_and_kwargs = []
131
- kwargs = dict(method=method, allow_redirects=False, retries=2, timeout=10)
131
+ kwargs = {"method": method, "allow_redirects": False, "retries": 2, "timeout": 10}
132
132
  for c in valid_chars:
133
133
  for file_part in ("stem", "ext"):
134
134
  payload = encode_all(f"*{c}*~1*")
@@ -160,7 +160,7 @@ class iis_shortnames(BaseModule):
160
160
  url_hint_list = []
161
161
  found_results = False
162
162
 
163
- cl = ext_char_list if extension_mode == True else char_list
163
+ cl = ext_char_list if extension_mode is True else char_list
164
164
 
165
165
  urls_and_kwargs = []
166
166
 
@@ -169,7 +169,7 @@ class iis_shortnames(BaseModule):
169
169
  wildcard = "*" if extension_mode else "*~1*"
170
170
  payload = encode_all(f"{prefix}{c}{wildcard}")
171
171
  url = f"{target}{payload}{suffix}"
172
- kwargs = dict(method=method)
172
+ kwargs = {"method": method}
173
173
  urls_and_kwargs.append((url, kwargs, c))
174
174
 
175
175
  async for url, kwargs, c, response in self.helpers.request_custom_batch(urls_and_kwargs):
@@ -209,7 +209,7 @@ class iis_shortnames(BaseModule):
209
209
  extension_mode,
210
210
  node_count=node_count,
211
211
  )
212
- if len(prefix) > 0 and found_results == False:
212
+ if len(prefix) > 0 and found_results is False:
213
213
  url_hint_list.append(f"{prefix}")
214
214
  self.verbose(f"Found new (possibly partial) URL_HINT: {prefix} from node {target}")
215
215
  return url_hint_list
@@ -234,7 +234,7 @@ class iis_shortnames(BaseModule):
234
234
  {"severity": "LOW", "host": str(event.host), "url": normalized_url, "description": description},
235
235
  "VULNERABILITY",
236
236
  event,
237
- context=f"{{module}} detected low {{event.type}}: IIS shortname enumeration",
237
+ context="{module} detected low {event.type}: IIS shortname enumeration",
238
238
  )
239
239
  if not self.config.get("detect_only"):
240
240
  for detection in detections:
@@ -15,7 +15,7 @@ class CloudCheck(BaseInterceptModule):
15
15
 
16
16
  def make_dummy_modules(self):
17
17
  self.dummy_modules = {}
18
- for provider_name, provider in self.helpers.cloud.providers.items():
18
+ for provider_name in self.helpers.cloud.providers.keys():
19
19
  module = self.scan._make_dummy_module(f"cloud_{provider_name}", _type="scan")
20
20
  module.default_discovery_context = "{module} derived {event.type}: {event.host}"
21
21
  self.dummy_modules[provider_name] = module
@@ -56,9 +56,9 @@ class CloudCheck(BaseInterceptModule):
56
56
  # loop through each provider
57
57
  for provider in self.helpers.cloud.providers.values():
58
58
  provider_name = provider.name.lower()
59
- base_kwargs = dict(
60
- parent=event, tags=[f"{provider.provider_type}-{provider_name}"], _provider=provider_name
61
- )
59
+ base_kwargs = {
60
+ "parent": event, "tags": [f"{provider.provider_type}-{provider_name}"], "_provider": provider_name
61
+ }
62
62
  # loop through the provider's regex signatures, if any
63
63
  for event_type, sigs in provider.signatures.items():
64
64
  if event_type != "STORAGE_BUCKET":
@@ -74,7 +74,7 @@ class CloudCheck(BaseInterceptModule):
74
74
  if match:
75
75
  matches.append(match.groups())
76
76
  for match in matches:
77
- if not match in found:
77
+ if match not in found:
78
78
  found.add(match)
79
79
 
80
80
  _kwargs = dict(base_kwargs)
@@ -131,9 +131,9 @@ class DNSResolve(BaseInterceptModule):
131
131
  event.host, rdtypes=rdtypes, raw_dns_records=event.raw_dns_records
132
132
  )
133
133
  for rdtype, (is_wildcard, wildcard_host) in wildcard_rdtypes.items():
134
- if is_wildcard == False:
134
+ if is_wildcard is False:
135
135
  continue
136
- elif is_wildcard == True:
136
+ elif is_wildcard is True:
137
137
  event.add_tag("wildcard")
138
138
  wildcard_tag = "wildcard"
139
139
  else:
@@ -142,16 +142,16 @@ class DNSResolve(BaseInterceptModule):
142
142
  event.add_tag(f"{rdtype}-{wildcard_tag}")
143
143
 
144
144
  # wildcard event modification (www.evilcorp.com --> _wildcard.evilcorp.com)
145
- if wildcard_rdtypes and not "target" in event.tags:
145
+ if wildcard_rdtypes and "target" not in event.tags:
146
146
  # these are the rdtypes that have wildcards
147
147
  wildcard_rdtypes_set = set(wildcard_rdtypes)
148
148
  # consider the event a full wildcard if all its records are wildcards
149
149
  event_is_wildcard = False
150
150
  if wildcard_rdtypes_set:
151
- event_is_wildcard = all(r[0] == True for r in wildcard_rdtypes.values())
151
+ event_is_wildcard = all(r[0] is True for r in wildcard_rdtypes.values())
152
152
 
153
153
  if event_is_wildcard:
154
- if event.type in ("DNS_NAME",) and not "_wildcard" in event.data.split("."):
154
+ if event.type in ("DNS_NAME",) and "_wildcard" not in event.data.split("."):
155
155
  wildcard_parent = self.helpers.parent_domain(event.host)
156
156
  for rdtype, (_is_wildcard, _parent_domain) in wildcard_rdtypes.items():
157
157
  if _is_wildcard:
@@ -273,7 +273,7 @@ class DNSResolve(BaseInterceptModule):
273
273
  # tag event with errors
274
274
  for rdtype, errors in dns_errors.items():
275
275
  # only consider it an error if there weren't any results for that rdtype
276
- if errors and not rdtype in event.dns_children:
276
+ if errors and rdtype not in event.dns_children:
277
277
  event.add_tag(f"{rdtype}-error")
278
278
 
279
279
  def get_dns_parent(self, event):
@@ -307,7 +307,7 @@ class DNSResolve(BaseInterceptModule):
307
307
  def emit_raw_records(self):
308
308
  if self._emit_raw_records is None:
309
309
  watching_raw_records = any(
310
- ["RAW_DNS_RECORD" in m.get_watched_events() for m in self.scan.modules.values()]
310
+ "RAW_DNS_RECORD" in m.get_watched_events() for m in self.scan.modules.values()
311
311
  )
312
312
  omitted_event_types = self.scan.config.get("omit_event_types", [])
313
313
  omit_raw_records = "RAW_DNS_RECORD" in omitted_event_types
@@ -62,7 +62,6 @@ def _exclude_key(original_dict, key_to_exclude):
62
62
 
63
63
 
64
64
  def extract_params_url(parsed_url):
65
-
66
65
  params = parse_qs(parsed_url.query)
67
66
  flat_params = {k: v[0] for k, v in params.items()}
68
67
 
@@ -94,7 +93,6 @@ def extract_params_location(location_header_value, original_parsed_url):
94
93
 
95
94
 
96
95
  class YaraRuleSettings:
97
-
98
96
  def __init__(self, description, tags, emit_match):
99
97
  self.description = description
100
98
  self.tags = tags
@@ -155,7 +153,7 @@ class ExcavateRule:
155
153
  yara_results = {}
156
154
  for h in r.strings:
157
155
  yara_results[h.identifier.lstrip("$")] = sorted(
158
- set([i.matched_data.decode("utf-8", errors="ignore") for i in h.instances])
156
+ {i.matched_data.decode("utf-8", errors="ignore") for i in h.instances}
159
157
  )
160
158
  await self.process(yara_results, event, yara_rule_settings, discovery_context)
161
159
 
@@ -182,7 +180,7 @@ class ExcavateRule:
182
180
  Returns:
183
181
  None
184
182
  """
185
- for identifier, results in yara_results.items():
183
+ for results in yara_results.values():
186
184
  for result in results:
187
185
  event_data = {"description": f"{discovery_context} {yara_rule_settings.description}"}
188
186
  if yara_rule_settings.emit_match:
@@ -263,7 +261,6 @@ class ExcavateRule:
263
261
 
264
262
 
265
263
  class CustomExtractor(ExcavateRule):
266
-
267
264
  def __init__(self, excavate):
268
265
  super().__init__(excavate)
269
266
 
@@ -317,7 +314,7 @@ class excavate(BaseInternalModule, BaseInterceptModule):
317
314
 
318
315
  _module_threads = 8
319
316
 
320
- parameter_blacklist = set(
317
+ parameter_blacklist = {
321
318
  p.lower()
322
319
  for p in [
323
320
  "__VIEWSTATE",
@@ -332,7 +329,7 @@ class excavate(BaseInternalModule, BaseInterceptModule):
332
329
  "JSESSIONID",
333
330
  "PHPSESSID",
334
331
  ]
335
- )
332
+ }
336
333
 
337
334
  yara_rule_name_regex = re.compile(r"rule\s(\w+)\s{")
338
335
  yara_rule_regex = re.compile(r"(?s)((?:rule\s+\w+\s*{[^{}]*(?:{[^{}]*}[^{}]*)*[^{}]*(?:/\S*?}[^/]*?/)*)*})")
@@ -358,7 +355,6 @@ class excavate(BaseInternalModule, BaseInterceptModule):
358
355
  )
359
356
 
360
357
  class ParameterExtractor(ExcavateRule):
361
-
362
358
  yara_rules = {}
363
359
 
364
360
  class ParameterExtractorRule:
@@ -372,7 +368,6 @@ class excavate(BaseInternalModule, BaseInterceptModule):
372
368
  self.result = result
373
369
 
374
370
  class GetJquery(ParameterExtractorRule):
375
-
376
371
  name = "GET jquery"
377
372
  discovery_regex = r"/\$.get\([^\)].+\)/ nocase"
378
373
  extraction_regex = re.compile(r"\$.get\([\'\"](.+)[\'\"].+(\{.+\})\)")
@@ -393,8 +388,12 @@ class excavate(BaseInternalModule, BaseInterceptModule):
393
388
  for action, extracted_parameters in extracted_results:
394
389
  extracted_parameters_dict = self.convert_to_dict(extracted_parameters)
395
390
  for parameter_name, original_value in extracted_parameters_dict.items():
396
- yield self.output_type, parameter_name, original_value, action, _exclude_key(
397
- extracted_parameters_dict, parameter_name
391
+ yield (
392
+ self.output_type,
393
+ parameter_name,
394
+ original_value,
395
+ action,
396
+ _exclude_key(extracted_parameters_dict, parameter_name),
398
397
  )
399
398
 
400
399
  class PostJquery(GetJquery):
@@ -418,8 +417,12 @@ class excavate(BaseInternalModule, BaseInterceptModule):
418
417
  k: v[0] if isinstance(v, list) and len(v) == 1 else v for k, v in query_strings.items()
419
418
  }
420
419
  for parameter_name, original_value in query_strings_dict.items():
421
- yield self.output_type, parameter_name, original_value, url, _exclude_key(
422
- query_strings_dict, parameter_name
420
+ yield (
421
+ self.output_type,
422
+ parameter_name,
423
+ original_value,
424
+ url,
425
+ _exclude_key(query_strings_dict, parameter_name),
423
426
  )
424
427
 
425
428
  class GetForm(ParameterExtractorRule):
@@ -444,8 +447,12 @@ class excavate(BaseInternalModule, BaseInterceptModule):
444
447
  form_parameters[parameter_name] = original_value
445
448
 
446
449
  for parameter_name, original_value in form_parameters.items():
447
- yield self.output_type, parameter_name, original_value, form_action, _exclude_key(
448
- form_parameters, parameter_name
450
+ yield (
451
+ self.output_type,
452
+ parameter_name,
453
+ original_value,
454
+ form_action,
455
+ _exclude_key(form_parameters, parameter_name),
449
456
  )
450
457
 
451
458
  class PostForm(GetForm):
@@ -485,7 +492,6 @@ class excavate(BaseInternalModule, BaseInterceptModule):
485
492
  endpoint,
486
493
  additional_params,
487
494
  ) in extracted_params:
488
-
489
495
  self.excavate.debug(
490
496
  f"Found Parameter [{parameter_name}] in [{parameterExtractorSubModule.name}] ParameterExtractor Submodule"
491
497
  )
@@ -497,7 +503,6 @@ class excavate(BaseInternalModule, BaseInterceptModule):
497
503
  )
498
504
 
499
505
  if self.excavate.helpers.validate_parameter(parameter_name, parameter_type):
500
-
501
506
  if self.excavate.in_bl(parameter_name) == False:
502
507
  parsed_url = urlparse(url)
503
508
  description = f"HTTP Extracted Parameter [{parameter_name}] ({parameterExtractorSubModule.name} Submodule)"
@@ -532,7 +537,6 @@ class excavate(BaseInternalModule, BaseInterceptModule):
532
537
  await self.report(domain, event, yara_rule_settings, discovery_context, event_type="DNS_NAME")
533
538
 
534
539
  class EmailExtractor(ExcavateRule):
535
-
536
540
  yara_rules = {
537
541
  "email": 'rule email { meta: description = "contains email address" strings: $email = /[^\\W_][\\w\\-\\.\\+\']{0,100}@[a-zA-Z0-9\\-]{1,100}(\\.[a-zA-Z0-9\\-]{1,100})*\\.[a-zA-Z]{2,63}/ nocase fullword condition: $email }',
538
542
  }
@@ -551,7 +555,6 @@ class excavate(BaseInternalModule, BaseInterceptModule):
551
555
  }
552
556
 
553
557
  class ErrorExtractor(ExcavateRule):
554
-
555
558
  signatures = {
556
559
  "PHP_1": r"/\.php on line [0-9]+/",
557
560
  "PHP_2": r"/\.php<\/b> on line <b>[0-9]+/",
@@ -589,7 +592,6 @@ class excavate(BaseInternalModule, BaseInterceptModule):
589
592
  await self.report(event_data, event, yara_rule_settings, discovery_context, event_type="FINDING")
590
593
 
591
594
  class SerializationExtractor(ExcavateRule):
592
-
593
595
  regexes = {
594
596
  "Java": re.compile(r"[^a-zA-Z0-9\/+]rO0[a-zA-Z0-9+\/]+={0,2}"),
595
597
  "DOTNET": re.compile(r"[^a-zA-Z0-9\/+]AAEAAAD\/\/[a-zA-Z0-9\/+]+={0,2}"),
@@ -619,7 +621,6 @@ class excavate(BaseInternalModule, BaseInterceptModule):
619
621
  await self.report(event_data, event, yara_rule_settings, discovery_context, event_type="FINDING")
620
622
 
621
623
  class FunctionalityExtractor(ExcavateRule):
622
-
623
624
  yara_rules = {
624
625
  "File_Upload_Functionality": r'rule File_Upload_Functionality { meta: description = "contains file upload functionality" strings: $fileuploadfunc = /<input[^>]+type=["\']?file["\']?[^>]+>/ nocase condition: $fileuploadfunc }',
625
626
  "Web_Service_WSDL": r'rule Web_Service_WSDL { meta: emit_match = "True" description = "contains a web service WSDL URL" strings: $wsdl = /https?:\/\/[^\s]*\.(wsdl)/ nocase condition: $wsdl }',
@@ -633,7 +634,7 @@ class excavate(BaseInternalModule, BaseInterceptModule):
633
634
  scheme_blacklist = ["javascript", "mailto", "tel", "data", "vbscript", "about", "file"]
634
635
 
635
636
  async def process(self, yara_results, event, yara_rule_settings, discovery_context):
636
- for identifier, results in yara_results.items():
637
+ for results in yara_results.values():
637
638
  for url_str in results:
638
639
  scheme = url_str.split("://")[0]
639
640
  if scheme in self.scheme_blacklist:
@@ -704,7 +705,6 @@ class excavate(BaseInternalModule, BaseInterceptModule):
704
705
  tag_attribute_regex = bbot_regexes.tag_attribute_regex
705
706
 
706
707
  async def process(self, yara_results, event, yara_rule_settings, discovery_context):
707
-
708
708
  for identifier, results in yara_results.items():
709
709
  urls_found = 0
710
710
  final_url = ""
@@ -897,7 +897,6 @@ class excavate(BaseInternalModule, BaseInterceptModule):
897
897
  decoded_data = await self.helpers.re.recursive_decode(data)
898
898
 
899
899
  if self.parameter_extraction:
900
-
901
900
  content_type_lower = content_type.lower() if content_type else ""
902
901
  extraction_map = {
903
902
  "json": self.helpers.extract_params_json,
@@ -934,7 +933,6 @@ class excavate(BaseInternalModule, BaseInterceptModule):
934
933
  self.hugewarning(f"YARA Rule {rule_name} not found in pre-compiled rules")
935
934
 
936
935
  async def handle_event(self, event):
937
-
938
936
  if event.type == "HTTP_RESPONSE":
939
937
  # Harvest GET parameters from URL, if it came directly from the target, and parameter extraction is enabled
940
938
  if (
@@ -1023,7 +1021,6 @@ class excavate(BaseInternalModule, BaseInterceptModule):
1023
1021
 
1024
1022
  # Try to extract parameters from the redirect URL
1025
1023
  if self.parameter_extraction:
1026
-
1027
1024
  for (
1028
1025
  method,
1029
1026
  parsed_url,
@@ -45,10 +45,10 @@ class speculate(BaseInternalModule):
45
45
 
46
46
  async def setup(self):
47
47
  scan_modules = [m for m in self.scan.modules.values() if m._type == "scan"]
48
- self.open_port_consumers = any(["OPEN_TCP_PORT" in m.watched_events for m in scan_modules])
48
+ self.open_port_consumers = any("OPEN_TCP_PORT" in m.watched_events for m in scan_modules)
49
49
  # only consider active portscanners (still speculate if only passive ones are enabled)
50
50
  self.portscanner_enabled = any(
51
- ["portscan" in m.flags and "active" in m.flags for m in self.scan.modules.values()]
51
+ "portscan" in m.flags and "active" in m.flags for m in self.scan.modules.values()
52
52
  )
53
53
  self.emit_open_ports = self.open_port_consumers and not self.portscanner_enabled
54
54
  self.range_to_ip = True
@@ -71,7 +71,7 @@ class speculate(BaseInternalModule):
71
71
  self.hugewarning(
72
72
  f"Selected target ({target_len:,} hosts) is too large, skipping IP_RANGE --> IP_ADDRESS speculation"
73
73
  )
74
- self.hugewarning(f'Enabling the "portscan" module is highly recommended')
74
+ self.hugewarning('Enabling the "portscan" module is highly recommended')
75
75
  self.range_to_ip = False
76
76
 
77
77
  return True
@@ -126,7 +126,7 @@ class speculate(BaseInternalModule):
126
126
  parent = self.helpers.parent_domain(event.host_original)
127
127
  if parent != event.data:
128
128
  await self.emit_event(
129
- parent, "DNS_NAME", parent=event, context=f"speculated parent {{event.type}}: {{event.data}}"
129
+ parent, "DNS_NAME", parent=event, context="speculated parent {event.type}: {event.data}"
130
130
  )
131
131
 
132
132
  # URL --> OPEN_TCP_PORT
@@ -31,7 +31,7 @@ class ipneighbor(BaseModule):
31
31
  netmask = main_ip.max_prefixlen - min(main_ip.max_prefixlen, self.num_bits)
32
32
  network = ipaddress.ip_network(f"{main_ip}/{netmask}", strict=False)
33
33
  subnet_hash = hash(network)
34
- if not subnet_hash in self.processed:
34
+ if subnet_hash not in self.processed:
35
35
  self.processed.add(subnet_hash)
36
36
  for ip in network:
37
37
  if ip != main_ip:
bbot/modules/jadx.py CHANGED
@@ -43,7 +43,7 @@ class jadx(BaseModule):
43
43
 
44
44
  async def filter_event(self, event):
45
45
  if "file" in event.tags:
46
- if not event.data["magic_description"].lower() in self.allowed_file_types:
46
+ if event.data["magic_description"].lower() not in self.allowed_file_types:
47
47
  return False, f"Jadx is not able to decompile this file type: {event.data['magic_description']}"
48
48
  else:
49
49
  return False, "Event is not a file"
@@ -46,11 +46,11 @@ class newsletters(BaseModule):
46
46
  body = _event.data["body"]
47
47
  soup = self.helpers.beautifulsoup(body, "html.parser")
48
48
  if soup is False:
49
- self.debug(f"BeautifulSoup returned False")
49
+ self.debug("BeautifulSoup returned False")
50
50
  return
51
51
  result = self.find_type(soup)
52
52
  if result:
53
- description = f"Found a Newsletter Submission Form that could be used for email bombing attacks"
53
+ description = "Found a Newsletter Submission Form that could be used for email bombing attacks"
54
54
  data = {"host": str(_event.host), "description": description, "url": _event.data["url"]}
55
55
  await self.emit_event(
56
56
  data,
@@ -91,15 +91,15 @@ class asset_inventory(CSV):
91
91
  self.assets[hostkey].absorb_event(event)
92
92
 
93
93
  async def report(self):
94
- stats = dict()
95
- totals = dict()
94
+ stats = {}
95
+ totals = {}
96
96
 
97
97
  def increment_stat(stat, value):
98
98
  try:
99
99
  totals[stat] += 1
100
100
  except KeyError:
101
101
  totals[stat] = 1
102
- if not stat in stats:
102
+ if stat not in stats:
103
103
  stats[stat] = {}
104
104
  try:
105
105
  stats[stat][value] += 1
@@ -263,13 +263,13 @@ class Asset:
263
263
  if not self.recheck:
264
264
  # ports
265
265
  ports = [i.strip() for i in row.get("Open Ports", "").split(",")]
266
- self.ports.update(set(i for i in ports if i and is_port(i)))
266
+ self.ports.update({i for i in ports if i and is_port(i)})
267
267
  # findings
268
268
  findings = [i.strip() for i in row.get("Findings", "").splitlines()]
269
- self.findings.update(set(i for i in findings if i))
269
+ self.findings.update({i for i in findings if i})
270
270
  # technologies
271
271
  technologies = [i.strip() for i in row.get("Technologies", "").splitlines()]
272
- self.technologies.update(set(i for i in technologies if i))
272
+ self.technologies.update({i for i in technologies if i})
273
273
  # risk rating
274
274
  risk_rating = row.get("Risk Rating", "").strip()
275
275
  if risk_rating and risk_rating.isdigit() and int(risk_rating) > self.risk_rating:
@@ -24,7 +24,7 @@ class BaseOutputModule(BaseModule):
24
24
  if event.type in ("FINISHED",):
25
25
  return True, "its type is FINISHED"
26
26
  if self.errored:
27
- return False, f"module is in error state"
27
+ return False, "module is in error state"
28
28
  # exclude non-watched types
29
29
  if not any(t in self.get_watched_events() for t in ("*", event.type)):
30
30
  return False, "its type is not in watched_events"
@@ -64,7 +64,7 @@ class CSV(BaseOutputModule):
64
64
  ),
65
65
  "Source Module": str(getattr(event, "module_sequence", "")),
66
66
  "Scope Distance": str(getattr(event, "scope_distance", "")),
67
- "Event Tags": ",".join(sorted(list(getattr(event, "tags", [])))),
67
+ "Event Tags": ",".join(sorted(getattr(event, "tags", []))),
68
68
  "Discovery Path": " --> ".join(discovery_path),
69
69
  }
70
70
  )
@@ -20,7 +20,7 @@ class Stdout(BaseOutputModule):
20
20
 
21
21
  async def setup(self):
22
22
  self.text_format = self.config.get("format", "text").strip().lower()
23
- if not self.text_format in self.format_choices:
23
+ if self.text_format not in self.format_choices:
24
24
  return (
25
25
  False,
26
26
  f'Invalid text format choice, "{self.text_format}" (choices: {",".join(self.format_choices)})',
@@ -33,7 +33,7 @@ class Stdout(BaseOutputModule):
33
33
 
34
34
  async def filter_event(self, event):
35
35
  if self.accept_event_types:
36
- if not event.type in self.accept_event_types:
36
+ if event.type not in self.accept_event_types:
37
37
  return False, f'Event type "{event.type}" is not in the allowed event_types'
38
38
  return True
39
39
 
@@ -82,7 +82,6 @@ class paramminer_headers(BaseModule):
82
82
  header_regex = re.compile(r"^[!#$%&\'*+\-.^_`|~0-9a-zA-Z]+: [^\r\n]+$")
83
83
 
84
84
  async def setup(self):
85
-
86
85
  self.recycle_words = self.config.get("recycle_words", True)
87
86
  self.event_dict = {}
88
87
  self.already_checked = set()
@@ -90,11 +89,11 @@ class paramminer_headers(BaseModule):
90
89
  if not wordlist:
91
90
  wordlist = f"{self.helpers.wordlist_dir}/{self.default_wordlist}"
92
91
  self.debug(f"Using wordlist: [{wordlist}]")
93
- self.wl = set(
92
+ self.wl = {
94
93
  h.strip().lower()
95
94
  for h in self.helpers.read_file(await self.helpers.wordlist(wordlist))
96
95
  if len(h) > 0 and "%" not in h
97
- )
96
+ }
98
97
 
99
98
  # check against the boring list (if the option is set)
100
99
  if self.config.get("skip_boring_words", True):
@@ -157,7 +156,6 @@ class paramminer_headers(BaseModule):
157
156
  )
158
157
 
159
158
  async def handle_event(self, event):
160
-
161
159
  # If recycle words is enabled, we will collect WEB_PARAMETERS we find to build our list in finish()
162
160
  # We also collect any parameters of type "SPECULATIVE"
163
161
  if event.type == "WEB_PARAMETER":
@@ -201,7 +199,7 @@ class paramminer_headers(BaseModule):
201
199
  return
202
200
  for count, args, kwargs in self.gen_count_args(url):
203
201
  r = await self.helpers.request(*args, **kwargs)
204
- if r is not None and not ((str(r.status_code)[0] in ("4", "5"))):
202
+ if r is not None and not (str(r.status_code)[0] in ("4", "5")):
205
203
  return count
206
204
 
207
205
  def gen_count_args(self, url):
@@ -240,8 +238,7 @@ class paramminer_headers(BaseModule):
240
238
  return await compare_helper.compare(url, headers=test_headers, check_reflection=(len(header_list) == 1))
241
239
 
242
240
  async def finish(self):
243
-
244
- untested_matches = sorted(list(self.extracted_words_master.copy()))
241
+ untested_matches = sorted(self.extracted_words_master.copy())
245
242
  for url, (event, batch_size) in list(self.event_dict.items()):
246
243
  try:
247
244
  compare_helper = self.helpers.http_compare(url)