bbot 2.0.1.4654rc0__py3-none-any.whl → 2.3.0.5397rc0__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.
- bbot/__init__.py +1 -1
- bbot/cli.py +3 -7
- bbot/core/config/files.py +0 -1
- bbot/core/config/logger.py +34 -4
- bbot/core/core.py +21 -6
- bbot/core/engine.py +9 -8
- bbot/core/event/base.py +162 -63
- bbot/core/helpers/bloom.py +10 -3
- bbot/core/helpers/command.py +9 -8
- bbot/core/helpers/depsinstaller/installer.py +89 -32
- bbot/core/helpers/depsinstaller/sudo_askpass.py +38 -2
- bbot/core/helpers/diff.py +10 -10
- bbot/core/helpers/dns/brute.py +18 -14
- bbot/core/helpers/dns/dns.py +16 -15
- bbot/core/helpers/dns/engine.py +159 -132
- bbot/core/helpers/dns/helpers.py +2 -2
- bbot/core/helpers/dns/mock.py +26 -8
- bbot/core/helpers/files.py +1 -1
- bbot/core/helpers/helper.py +7 -4
- bbot/core/helpers/interactsh.py +3 -3
- bbot/core/helpers/libmagic.py +65 -0
- bbot/core/helpers/misc.py +65 -22
- bbot/core/helpers/names_generator.py +17 -3
- bbot/core/helpers/process.py +0 -20
- bbot/core/helpers/regex.py +1 -1
- bbot/core/helpers/regexes.py +12 -6
- bbot/core/helpers/validators.py +1 -2
- bbot/core/helpers/web/client.py +1 -1
- bbot/core/helpers/web/engine.py +18 -13
- bbot/core/helpers/web/web.py +25 -116
- bbot/core/helpers/wordcloud.py +5 -5
- bbot/core/modules.py +36 -27
- bbot/core/multiprocess.py +58 -0
- bbot/core/shared_deps.py +46 -3
- bbot/db/sql/models.py +147 -0
- bbot/defaults.yml +15 -10
- bbot/errors.py +0 -8
- bbot/modules/anubisdb.py +2 -2
- bbot/modules/apkpure.py +63 -0
- bbot/modules/azure_tenant.py +2 -2
- bbot/modules/baddns.py +35 -19
- bbot/modules/baddns_direct.py +92 -0
- bbot/modules/baddns_zone.py +3 -8
- bbot/modules/badsecrets.py +4 -3
- bbot/modules/base.py +195 -51
- bbot/modules/bevigil.py +7 -7
- bbot/modules/binaryedge.py +7 -4
- bbot/modules/bufferoverrun.py +47 -0
- bbot/modules/builtwith.py +6 -10
- bbot/modules/bypass403.py +5 -5
- bbot/modules/c99.py +10 -7
- bbot/modules/censys.py +9 -13
- bbot/modules/certspotter.py +5 -3
- bbot/modules/chaos.py +9 -7
- bbot/modules/code_repository.py +1 -0
- bbot/modules/columbus.py +3 -3
- bbot/modules/crt.py +5 -3
- bbot/modules/deadly/dastardly.py +1 -1
- bbot/modules/deadly/ffuf.py +9 -9
- bbot/modules/deadly/nuclei.py +3 -3
- bbot/modules/deadly/vhost.py +4 -3
- bbot/modules/dehashed.py +1 -1
- bbot/modules/digitorus.py +1 -1
- bbot/modules/dnsbimi.py +145 -0
- bbot/modules/dnscaa.py +3 -3
- bbot/modules/dnsdumpster.py +4 -4
- bbot/modules/dnstlsrpt.py +144 -0
- bbot/modules/docker_pull.py +7 -5
- bbot/modules/dockerhub.py +2 -2
- bbot/modules/dotnetnuke.py +18 -19
- bbot/modules/emailformat.py +1 -1
- bbot/modules/extractous.py +122 -0
- bbot/modules/filedownload.py +9 -7
- bbot/modules/fullhunt.py +7 -4
- bbot/modules/generic_ssrf.py +5 -5
- bbot/modules/github_codesearch.py +3 -2
- bbot/modules/github_org.py +4 -4
- bbot/modules/github_workflows.py +4 -4
- bbot/modules/gitlab.py +2 -5
- bbot/modules/google_playstore.py +93 -0
- bbot/modules/gowitness.py +48 -50
- bbot/modules/hackertarget.py +5 -3
- bbot/modules/host_header.py +5 -5
- bbot/modules/httpx.py +1 -4
- bbot/modules/hunterio.py +3 -9
- bbot/modules/iis_shortnames.py +19 -30
- bbot/modules/internal/cloudcheck.py +27 -12
- bbot/modules/internal/dnsresolve.py +250 -276
- bbot/modules/internal/excavate.py +100 -64
- bbot/modules/internal/speculate.py +42 -33
- bbot/modules/internetdb.py +4 -2
- bbot/modules/ip2location.py +3 -5
- bbot/modules/ipneighbor.py +1 -1
- bbot/modules/ipstack.py +3 -8
- bbot/modules/jadx.py +87 -0
- bbot/modules/leakix.py +11 -10
- bbot/modules/myssl.py +2 -2
- bbot/modules/newsletters.py +2 -2
- bbot/modules/otx.py +5 -3
- bbot/modules/output/asset_inventory.py +7 -7
- bbot/modules/output/base.py +1 -1
- bbot/modules/output/csv.py +1 -2
- bbot/modules/output/http.py +20 -14
- bbot/modules/output/mysql.py +51 -0
- bbot/modules/output/neo4j.py +7 -2
- bbot/modules/output/postgres.py +49 -0
- bbot/modules/output/slack.py +0 -1
- bbot/modules/output/sqlite.py +29 -0
- bbot/modules/output/stdout.py +2 -2
- bbot/modules/output/teams.py +107 -6
- bbot/modules/paramminer_headers.py +5 -8
- bbot/modules/passivetotal.py +13 -13
- bbot/modules/portscan.py +32 -6
- bbot/modules/postman.py +50 -126
- bbot/modules/postman_download.py +220 -0
- bbot/modules/rapiddns.py +3 -8
- bbot/modules/report/asn.py +11 -11
- bbot/modules/robots.py +3 -3
- bbot/modules/securitytrails.py +7 -10
- bbot/modules/securitytxt.py +128 -0
- bbot/modules/shodan_dns.py +7 -9
- bbot/modules/sitedossier.py +1 -1
- bbot/modules/skymem.py +2 -2
- bbot/modules/social.py +2 -1
- bbot/modules/subdomaincenter.py +1 -1
- bbot/modules/subdomainradar.py +160 -0
- bbot/modules/telerik.py +8 -8
- bbot/modules/templates/bucket.py +1 -1
- bbot/modules/templates/github.py +22 -14
- bbot/modules/templates/postman.py +21 -0
- bbot/modules/templates/shodan.py +14 -13
- bbot/modules/templates/sql.py +95 -0
- bbot/modules/templates/subdomain_enum.py +53 -17
- bbot/modules/templates/webhook.py +2 -4
- bbot/modules/trickest.py +8 -37
- bbot/modules/trufflehog.py +18 -3
- bbot/modules/url_manipulation.py +3 -3
- bbot/modules/urlscan.py +1 -1
- bbot/modules/viewdns.py +1 -1
- bbot/modules/virustotal.py +8 -30
- bbot/modules/wafw00f.py +1 -1
- bbot/modules/wayback.py +1 -1
- bbot/modules/wpscan.py +17 -11
- bbot/modules/zoomeye.py +11 -6
- bbot/presets/baddns-thorough.yml +12 -0
- bbot/presets/fast.yml +16 -0
- bbot/presets/kitchen-sink.yml +1 -0
- bbot/presets/spider.yml +4 -0
- bbot/presets/subdomain-enum.yml +7 -7
- bbot/scanner/manager.py +5 -16
- bbot/scanner/preset/args.py +44 -26
- bbot/scanner/preset/environ.py +7 -2
- bbot/scanner/preset/path.py +7 -4
- bbot/scanner/preset/preset.py +36 -23
- bbot/scanner/scanner.py +176 -63
- bbot/scanner/target.py +236 -434
- bbot/scripts/docs.py +1 -1
- bbot/test/bbot_fixtures.py +22 -3
- bbot/test/conftest.py +132 -100
- bbot/test/fastapi_test.py +17 -0
- bbot/test/owasp_mastg.apk +0 -0
- bbot/test/run_tests.sh +4 -4
- bbot/test/test.conf +2 -0
- bbot/test/test_step_1/test_bbot_fastapi.py +82 -0
- bbot/test/test_step_1/test_bloom_filter.py +2 -0
- bbot/test/test_step_1/test_cli.py +138 -64
- bbot/test/test_step_1/test_dns.py +392 -70
- bbot/test/test_step_1/test_engine.py +17 -17
- bbot/test/test_step_1/test_events.py +203 -37
- bbot/test/test_step_1/test_helpers.py +64 -28
- bbot/test/test_step_1/test_manager_deduplication.py +1 -1
- bbot/test/test_step_1/test_manager_scope_accuracy.py +336 -338
- bbot/test/test_step_1/test_modules_basic.py +69 -71
- bbot/test/test_step_1/test_presets.py +184 -96
- bbot/test/test_step_1/test_python_api.py +7 -2
- bbot/test/test_step_1/test_regexes.py +35 -5
- bbot/test/test_step_1/test_scan.py +39 -5
- bbot/test/test_step_1/test_scope.py +5 -4
- bbot/test/test_step_1/test_target.py +243 -145
- bbot/test/test_step_1/test_web.py +48 -10
- bbot/test/test_step_2/module_tests/base.py +17 -20
- bbot/test/test_step_2/module_tests/test_module_anubisdb.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_apkpure.py +71 -0
- bbot/test/test_step_2/module_tests/test_module_asset_inventory.py +0 -1
- bbot/test/test_step_2/module_tests/test_module_azure_realm.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_baddns.py +6 -6
- bbot/test/test_step_2/module_tests/test_module_baddns_direct.py +62 -0
- bbot/test/test_step_2/module_tests/test_module_bevigil.py +29 -2
- bbot/test/test_step_2/module_tests/test_module_binaryedge.py +4 -2
- bbot/test/test_step_2/module_tests/test_module_bucket_amazon.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_bucket_azure.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_bufferoverrun.py +35 -0
- bbot/test/test_step_2/module_tests/test_module_builtwith.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_bypass403.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_c99.py +126 -0
- bbot/test/test_step_2/module_tests/test_module_censys.py +4 -1
- bbot/test/test_step_2/module_tests/test_module_cloudcheck.py +4 -0
- bbot/test/test_step_2/module_tests/test_module_code_repository.py +11 -1
- bbot/test/test_step_2/module_tests/test_module_columbus.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_credshed.py +3 -3
- bbot/test/test_step_2/module_tests/test_module_dastardly.py +2 -1
- bbot/test/test_step_2/module_tests/test_module_dehashed.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_digitorus.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_discord.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_dnsbimi.py +103 -0
- bbot/test/test_step_2/module_tests/test_module_dnsbrute.py +9 -10
- bbot/test/test_step_2/module_tests/test_module_dnsbrute_mutations.py +1 -2
- bbot/test/test_step_2/module_tests/test_module_dnscommonsrv.py +1 -2
- bbot/test/test_step_2/module_tests/test_module_dnsdumpster.py +4 -4
- bbot/test/test_step_2/module_tests/test_module_dnstlsrpt.py +64 -0
- bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py +0 -8
- bbot/test/test_step_2/module_tests/test_module_excavate.py +17 -37
- bbot/test/test_step_2/module_tests/test_module_extractous.py +54 -0
- bbot/test/test_step_2/module_tests/test_module_ffuf_shortnames.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_filedownload.py +14 -14
- bbot/test/test_step_2/module_tests/test_module_git_clone.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_github_org.py +19 -8
- bbot/test/test_step_2/module_tests/test_module_github_workflows.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_gitlab.py +9 -4
- bbot/test/test_step_2/module_tests/test_module_google_playstore.py +83 -0
- bbot/test/test_step_2/module_tests/test_module_gowitness.py +4 -4
- bbot/test/test_step_2/module_tests/test_module_host_header.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_http.py +4 -4
- bbot/test/test_step_2/module_tests/test_module_httpx.py +10 -8
- bbot/test/test_step_2/module_tests/test_module_hunterio.py +68 -4
- bbot/test/test_step_2/module_tests/test_module_jadx.py +55 -0
- bbot/test/test_step_2/module_tests/test_module_json.py +24 -11
- bbot/test/test_step_2/module_tests/test_module_leakix.py +7 -3
- bbot/test/test_step_2/module_tests/test_module_mysql.py +76 -0
- bbot/test/test_step_2/module_tests/test_module_myssl.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_neo4j.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_newsletters.py +6 -6
- bbot/test/test_step_2/module_tests/test_module_ntlm.py +7 -7
- bbot/test/test_step_2/module_tests/test_module_oauth.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_otx.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_paramminer_cookies.py +1 -2
- bbot/test/test_step_2/module_tests/test_module_paramminer_getparams.py +0 -6
- bbot/test/test_step_2/module_tests/test_module_paramminer_headers.py +2 -9
- bbot/test/test_step_2/module_tests/test_module_passivetotal.py +3 -1
- bbot/test/test_step_2/module_tests/test_module_portscan.py +9 -8
- bbot/test/test_step_2/module_tests/test_module_postgres.py +74 -0
- bbot/test/test_step_2/module_tests/test_module_postman.py +84 -253
- bbot/test/test_step_2/module_tests/test_module_postman_download.py +439 -0
- bbot/test/test_step_2/module_tests/test_module_rapiddns.py +93 -1
- bbot/test/test_step_2/module_tests/test_module_securitytxt.py +50 -0
- bbot/test/test_step_2/module_tests/test_module_shodan_dns.py +20 -1
- bbot/test/test_step_2/module_tests/test_module_sitedossier.py +2 -2
- bbot/test/test_step_2/module_tests/test_module_smuggler.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_social.py +11 -1
- bbot/test/test_step_2/module_tests/test_module_speculate.py +2 -6
- bbot/test/test_step_2/module_tests/test_module_splunk.py +4 -4
- bbot/test/test_step_2/module_tests/test_module_sqlite.py +18 -0
- bbot/test/test_step_2/module_tests/test_module_sslcert.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_stdout.py +5 -3
- bbot/test/test_step_2/module_tests/test_module_subdomaincenter.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_subdomainradar.py +208 -0
- bbot/test/test_step_2/module_tests/test_module_subdomains.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_teams.py +8 -6
- bbot/test/test_step_2/module_tests/test_module_telerik.py +1 -1
- bbot/test/test_step_2/module_tests/test_module_trufflehog.py +317 -11
- bbot/test/test_step_2/module_tests/test_module_wayback.py +1 -1
- bbot/test/test_step_2/template_tests/test_template_subdomain_enum.py +135 -0
- {bbot-2.0.1.4654rc0.dist-info → bbot-2.3.0.5397rc0.dist-info}/METADATA +48 -18
- bbot-2.3.0.5397rc0.dist-info/RECORD +421 -0
- {bbot-2.0.1.4654rc0.dist-info → bbot-2.3.0.5397rc0.dist-info}/WHEEL +1 -1
- bbot/modules/unstructured.py +0 -163
- bbot/test/test_step_2/module_tests/test_module_unstructured.py +0 -102
- bbot-2.0.1.4654rc0.dist-info/RECORD +0 -385
- {bbot-2.0.1.4654rc0.dist-info → bbot-2.3.0.5397rc0.dist-info}/LICENSE +0 -0
- {bbot-2.0.1.4654rc0.dist-info → bbot-2.3.0.5397rc0.dist-info}/entry_points.txt +0 -0
|
@@ -6,6 +6,7 @@ import regex as re
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from bbot.errors import ExcavateError
|
|
8
8
|
import bbot.core.helpers.regexes as bbot_regexes
|
|
9
|
+
from bbot.modules.base import BaseInterceptModule
|
|
9
10
|
from bbot.modules.internal.base import BaseInternalModule
|
|
10
11
|
from urllib.parse import urlparse, urljoin, parse_qs, urlunparse
|
|
11
12
|
|
|
@@ -61,7 +62,6 @@ def _exclude_key(original_dict, key_to_exclude):
|
|
|
61
62
|
|
|
62
63
|
|
|
63
64
|
def extract_params_url(parsed_url):
|
|
64
|
-
|
|
65
65
|
params = parse_qs(parsed_url.query)
|
|
66
66
|
flat_params = {k: v[0] for k, v in params.items()}
|
|
67
67
|
|
|
@@ -93,7 +93,6 @@ def extract_params_location(location_header_value, original_parsed_url):
|
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
class YaraRuleSettings:
|
|
96
|
-
|
|
97
96
|
def __init__(self, description, tags, emit_match):
|
|
98
97
|
self.description = description
|
|
99
98
|
self.tags = tags
|
|
@@ -153,7 +152,9 @@ class ExcavateRule:
|
|
|
153
152
|
yara_rule_settings = YaraRuleSettings(description, tags, emit_match)
|
|
154
153
|
yara_results = {}
|
|
155
154
|
for h in r.strings:
|
|
156
|
-
yara_results[h.identifier.lstrip("$")] = sorted(
|
|
155
|
+
yara_results[h.identifier.lstrip("$")] = sorted(
|
|
156
|
+
{i.matched_data.decode("utf-8", errors="ignore") for i in h.instances}
|
|
157
|
+
)
|
|
157
158
|
await self.process(yara_results, event, yara_rule_settings, discovery_context)
|
|
158
159
|
|
|
159
160
|
async def process(self, yara_results, event, yara_rule_settings, discovery_context):
|
|
@@ -179,7 +180,7 @@ class ExcavateRule:
|
|
|
179
180
|
Returns:
|
|
180
181
|
None
|
|
181
182
|
"""
|
|
182
|
-
for
|
|
183
|
+
for results in yara_results.values():
|
|
183
184
|
for result in results:
|
|
184
185
|
event_data = {"description": f"{discovery_context} {yara_rule_settings.description}"}
|
|
185
186
|
if yara_rule_settings.emit_match:
|
|
@@ -260,7 +261,6 @@ class ExcavateRule:
|
|
|
260
261
|
|
|
261
262
|
|
|
262
263
|
class CustomExtractor(ExcavateRule):
|
|
263
|
-
|
|
264
264
|
def __init__(self, excavate):
|
|
265
265
|
super().__init__(excavate)
|
|
266
266
|
|
|
@@ -279,7 +279,7 @@ class CustomExtractor(ExcavateRule):
|
|
|
279
279
|
await self.report(event_data, event, yara_rule_settings, discovery_context)
|
|
280
280
|
|
|
281
281
|
|
|
282
|
-
class excavate(BaseInternalModule):
|
|
282
|
+
class excavate(BaseInternalModule, BaseInterceptModule):
|
|
283
283
|
"""
|
|
284
284
|
Example (simple) Excavate Rules:
|
|
285
285
|
|
|
@@ -310,32 +310,32 @@ class excavate(BaseInternalModule):
|
|
|
310
310
|
"custom_yara_rules": "Include custom Yara rules",
|
|
311
311
|
}
|
|
312
312
|
scope_distance_modifier = None
|
|
313
|
+
accept_dupes = False
|
|
313
314
|
|
|
314
315
|
_module_threads = 8
|
|
315
316
|
|
|
316
|
-
parameter_blacklist =
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
317
|
+
parameter_blacklist = {
|
|
318
|
+
p.lower()
|
|
319
|
+
for p in [
|
|
320
|
+
"__VIEWSTATE",
|
|
321
|
+
"__EVENTARGUMENT",
|
|
322
|
+
"__EVENTVALIDATION",
|
|
323
|
+
"__EVENTTARGET",
|
|
324
|
+
"__EVENTARGUMENT",
|
|
325
|
+
"__VIEWSTATEGENERATOR",
|
|
326
|
+
"__SCROLLPOSITIONY",
|
|
327
|
+
"__SCROLLPOSITIONX",
|
|
328
|
+
"ASP.NET_SessionId",
|
|
329
|
+
"JSESSIONID",
|
|
330
|
+
"PHPSESSID",
|
|
331
|
+
]
|
|
332
|
+
}
|
|
329
333
|
|
|
330
334
|
yara_rule_name_regex = re.compile(r"rule\s(\w+)\s{")
|
|
331
335
|
yara_rule_regex = re.compile(r"(?s)((?:rule\s+\w+\s*{[^{}]*(?:{[^{}]*}[^{}]*)*[^{}]*(?:/\S*?}[^/]*?/)*)*})")
|
|
332
336
|
|
|
333
337
|
def in_bl(self, value):
|
|
334
|
-
|
|
335
|
-
for bl_param in self.parameter_blacklist:
|
|
336
|
-
if bl_param.lower() == value.lower():
|
|
337
|
-
in_bl = True
|
|
338
|
-
return in_bl
|
|
338
|
+
return value.lower() in self.parameter_blacklist
|
|
339
339
|
|
|
340
340
|
def url_unparse(self, param_type, parsed_url):
|
|
341
341
|
if param_type == "GETPARAM":
|
|
@@ -355,7 +355,6 @@ class excavate(BaseInternalModule):
|
|
|
355
355
|
)
|
|
356
356
|
|
|
357
357
|
class ParameterExtractor(ExcavateRule):
|
|
358
|
-
|
|
359
358
|
yara_rules = {}
|
|
360
359
|
|
|
361
360
|
class ParameterExtractorRule:
|
|
@@ -369,7 +368,6 @@ class excavate(BaseInternalModule):
|
|
|
369
368
|
self.result = result
|
|
370
369
|
|
|
371
370
|
class GetJquery(ParameterExtractorRule):
|
|
372
|
-
|
|
373
371
|
name = "GET jquery"
|
|
374
372
|
discovery_regex = r"/\$.get\([^\)].+\)/ nocase"
|
|
375
373
|
extraction_regex = re.compile(r"\$.get\([\'\"](.+)[\'\"].+(\{.+\})\)")
|
|
@@ -390,8 +388,12 @@ class excavate(BaseInternalModule):
|
|
|
390
388
|
for action, extracted_parameters in extracted_results:
|
|
391
389
|
extracted_parameters_dict = self.convert_to_dict(extracted_parameters)
|
|
392
390
|
for parameter_name, original_value in extracted_parameters_dict.items():
|
|
393
|
-
yield
|
|
394
|
-
|
|
391
|
+
yield (
|
|
392
|
+
self.output_type,
|
|
393
|
+
parameter_name,
|
|
394
|
+
original_value,
|
|
395
|
+
action,
|
|
396
|
+
_exclude_key(extracted_parameters_dict, parameter_name),
|
|
395
397
|
)
|
|
396
398
|
|
|
397
399
|
class PostJquery(GetJquery):
|
|
@@ -415,8 +417,12 @@ class excavate(BaseInternalModule):
|
|
|
415
417
|
k: v[0] if isinstance(v, list) and len(v) == 1 else v for k, v in query_strings.items()
|
|
416
418
|
}
|
|
417
419
|
for parameter_name, original_value in query_strings_dict.items():
|
|
418
|
-
yield
|
|
419
|
-
|
|
420
|
+
yield (
|
|
421
|
+
self.output_type,
|
|
422
|
+
parameter_name,
|
|
423
|
+
original_value,
|
|
424
|
+
url,
|
|
425
|
+
_exclude_key(query_strings_dict, parameter_name),
|
|
420
426
|
)
|
|
421
427
|
|
|
422
428
|
class GetForm(ParameterExtractorRule):
|
|
@@ -441,8 +447,12 @@ class excavate(BaseInternalModule):
|
|
|
441
447
|
form_parameters[parameter_name] = original_value
|
|
442
448
|
|
|
443
449
|
for parameter_name, original_value in form_parameters.items():
|
|
444
|
-
yield
|
|
445
|
-
|
|
450
|
+
yield (
|
|
451
|
+
self.output_type,
|
|
452
|
+
parameter_name,
|
|
453
|
+
original_value,
|
|
454
|
+
form_action,
|
|
455
|
+
_exclude_key(form_parameters, parameter_name),
|
|
446
456
|
)
|
|
447
457
|
|
|
448
458
|
class PostForm(GetForm):
|
|
@@ -482,7 +492,6 @@ class excavate(BaseInternalModule):
|
|
|
482
492
|
endpoint,
|
|
483
493
|
additional_params,
|
|
484
494
|
) in extracted_params:
|
|
485
|
-
|
|
486
495
|
self.excavate.debug(
|
|
487
496
|
f"Found Parameter [{parameter_name}] in [{parameterExtractorSubModule.name}] ParameterExtractor Submodule"
|
|
488
497
|
)
|
|
@@ -494,7 +503,6 @@ class excavate(BaseInternalModule):
|
|
|
494
503
|
)
|
|
495
504
|
|
|
496
505
|
if self.excavate.helpers.validate_parameter(parameter_name, parameter_type):
|
|
497
|
-
|
|
498
506
|
if self.excavate.in_bl(parameter_name) == False:
|
|
499
507
|
parsed_url = urlparse(url)
|
|
500
508
|
description = f"HTTP Extracted Parameter [{parameter_name}] ({parameterExtractorSubModule.name} Submodule)"
|
|
@@ -524,13 +532,11 @@ class excavate(BaseInternalModule):
|
|
|
524
532
|
async def process(self, yara_results, event, yara_rule_settings, discovery_context):
|
|
525
533
|
for identifier in yara_results.keys():
|
|
526
534
|
for csp_str in yara_results[identifier]:
|
|
527
|
-
domains = await self.
|
|
528
|
-
|
|
529
|
-
for domain in unique_domains:
|
|
535
|
+
domains = await self.excavate.scan.extract_in_scope_hostnames(csp_str)
|
|
536
|
+
for domain in domains:
|
|
530
537
|
await self.report(domain, event, yara_rule_settings, discovery_context, event_type="DNS_NAME")
|
|
531
538
|
|
|
532
539
|
class EmailExtractor(ExcavateRule):
|
|
533
|
-
|
|
534
540
|
yara_rules = {
|
|
535
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 }',
|
|
536
542
|
}
|
|
@@ -549,7 +555,6 @@ class excavate(BaseInternalModule):
|
|
|
549
555
|
}
|
|
550
556
|
|
|
551
557
|
class ErrorExtractor(ExcavateRule):
|
|
552
|
-
|
|
553
558
|
signatures = {
|
|
554
559
|
"PHP_1": r"/\.php on line [0-9]+/",
|
|
555
560
|
"PHP_2": r"/\.php<\/b> on line <b>[0-9]+/",
|
|
@@ -587,7 +592,6 @@ class excavate(BaseInternalModule):
|
|
|
587
592
|
await self.report(event_data, event, yara_rule_settings, discovery_context, event_type="FINDING")
|
|
588
593
|
|
|
589
594
|
class SerializationExtractor(ExcavateRule):
|
|
590
|
-
|
|
591
595
|
regexes = {
|
|
592
596
|
"Java": re.compile(r"[^a-zA-Z0-9\/+]rO0[a-zA-Z0-9+\/]+={0,2}"),
|
|
593
597
|
"DOTNET": re.compile(r"[^a-zA-Z0-9\/+]AAEAAAD\/\/[a-zA-Z0-9\/+]+={0,2}"),
|
|
@@ -617,7 +621,6 @@ class excavate(BaseInternalModule):
|
|
|
617
621
|
await self.report(event_data, event, yara_rule_settings, discovery_context, event_type="FINDING")
|
|
618
622
|
|
|
619
623
|
class FunctionalityExtractor(ExcavateRule):
|
|
620
|
-
|
|
621
624
|
yara_rules = {
|
|
622
625
|
"File_Upload_Functionality": r'rule File_Upload_Functionality { meta: description = "contains file upload functionality" strings: $fileuploadfunc = /<input[^>]+type=["\']?file["\']?[^>]+>/ nocase condition: $fileuploadfunc }',
|
|
623
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 }',
|
|
@@ -631,7 +634,7 @@ class excavate(BaseInternalModule):
|
|
|
631
634
|
scheme_blacklist = ["javascript", "mailto", "tel", "data", "vbscript", "about", "file"]
|
|
632
635
|
|
|
633
636
|
async def process(self, yara_results, event, yara_rule_settings, discovery_context):
|
|
634
|
-
for
|
|
637
|
+
for results in yara_results.values():
|
|
635
638
|
for url_str in results:
|
|
636
639
|
scheme = url_str.split("://")[0]
|
|
637
640
|
if scheme in self.scheme_blacklist:
|
|
@@ -670,15 +673,38 @@ class excavate(BaseInternalModule):
|
|
|
670
673
|
|
|
671
674
|
class URLExtractor(ExcavateRule):
|
|
672
675
|
yara_rules = {
|
|
673
|
-
"url_full":
|
|
674
|
-
|
|
676
|
+
"url_full": (
|
|
677
|
+
r"""
|
|
678
|
+
rule url_full {
|
|
679
|
+
meta:
|
|
680
|
+
tags = "spider-danger"
|
|
681
|
+
description = "contains full URL"
|
|
682
|
+
strings:
|
|
683
|
+
$url_full = /https?:\/\/([\w\.-]+)(:\d{1,5})?([\/\w\.-]*)/
|
|
684
|
+
condition:
|
|
685
|
+
$url_full
|
|
686
|
+
}
|
|
687
|
+
"""
|
|
688
|
+
),
|
|
689
|
+
"url_attr": (
|
|
690
|
+
r"""
|
|
691
|
+
rule url_attr {
|
|
692
|
+
meta:
|
|
693
|
+
tags = "spider-danger"
|
|
694
|
+
description = "contains tag with src or href attribute"
|
|
695
|
+
strings:
|
|
696
|
+
$url_attr = /<[^>]+(href|src)=["\'][^"\']*["\'][^>]*>/
|
|
697
|
+
condition:
|
|
698
|
+
$url_attr
|
|
699
|
+
}
|
|
700
|
+
"""
|
|
701
|
+
),
|
|
675
702
|
}
|
|
676
703
|
full_url_regex = re.compile(r"(https?)://((?:\w|\d)(?:[\d\w-]+\.?)+(?::\d{1,5})?(?:/[-\w\.\(\)]*[-\w\.]+)*/?)")
|
|
677
704
|
full_url_regex_strict = re.compile(r"^(https?):\/\/([\w.-]+)(?::\d{1,5})?(\/[\w\/\.-]*)?(\?[^\s]+)?$")
|
|
678
705
|
tag_attribute_regex = bbot_regexes.tag_attribute_regex
|
|
679
706
|
|
|
680
707
|
async def process(self, yara_results, event, yara_rule_settings, discovery_context):
|
|
681
|
-
|
|
682
708
|
for identifier, results in yara_results.items():
|
|
683
709
|
urls_found = 0
|
|
684
710
|
final_url = ""
|
|
@@ -742,20 +768,33 @@ class excavate(BaseInternalModule):
|
|
|
742
768
|
|
|
743
769
|
def __init__(self, excavate):
|
|
744
770
|
super().__init__(excavate)
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
for i, r in enumerate(excavate.scan.dns_regexes_yara):
|
|
748
|
-
regexes_component_list.append(rf"$dns_name_{i} = /\b{r.pattern}/ nocase")
|
|
749
|
-
regexes_component = " ".join(regexes_component_list)
|
|
750
|
-
self.yara_rules[f"hostname_extraction"] = (
|
|
751
|
-
f'rule hostname_extraction {{meta: description = "matches DNS hostname pattern derived from target(s)" strings: {regexes_component} condition: any of them}}'
|
|
752
|
-
)
|
|
771
|
+
if excavate.scan.dns_yara_rules_uncompiled:
|
|
772
|
+
self.yara_rules[f"hostname_extraction"] = excavate.scan.dns_yara_rules_uncompiled
|
|
753
773
|
|
|
754
774
|
async def process(self, yara_results, event, yara_rule_settings, discovery_context):
|
|
755
775
|
for identifier in yara_results.keys():
|
|
756
776
|
for domain_str in yara_results[identifier]:
|
|
757
777
|
await self.report(domain_str, event, yara_rule_settings, discovery_context, event_type="DNS_NAME")
|
|
758
778
|
|
|
779
|
+
class LoginPageExtractor(ExcavateRule):
|
|
780
|
+
yara_rules = {
|
|
781
|
+
"login_page": r"""
|
|
782
|
+
rule login_page {
|
|
783
|
+
meta:
|
|
784
|
+
description = "Detects login pages with username and password fields"
|
|
785
|
+
strings:
|
|
786
|
+
$username_field = /<input[^>]+name=["']?(user|login|email)/ nocase
|
|
787
|
+
$password_field = /<input[^>]+name=["']?passw?/ nocase
|
|
788
|
+
condition:
|
|
789
|
+
$username_field and $password_field
|
|
790
|
+
}
|
|
791
|
+
"""
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
async def process(self, yara_results, event, yara_rule_settings, discovery_context):
|
|
795
|
+
if yara_results:
|
|
796
|
+
event.add_tag("login-page")
|
|
797
|
+
|
|
759
798
|
def add_yara_rule(self, rule_name, rule_content, rule_instance):
|
|
760
799
|
rule_instance.name = rule_name
|
|
761
800
|
self.yara_rules_dict[rule_name] = rule_content
|
|
@@ -806,9 +845,9 @@ class excavate(BaseInternalModule):
|
|
|
806
845
|
if Path(self.custom_yara_rules).is_file():
|
|
807
846
|
with open(self.custom_yara_rules) as f:
|
|
808
847
|
rules_content = f.read()
|
|
809
|
-
self.debug(f"Successfully loaded
|
|
848
|
+
self.debug(f"Successfully loaded custom yara rules file [{self.custom_yara_rules}]")
|
|
810
849
|
else:
|
|
811
|
-
self.debug(f"Custom
|
|
850
|
+
self.debug(f"Custom yara rules file is NOT a file. Will attempt to treat it as rule content")
|
|
812
851
|
rules_content = self.custom_yara_rules
|
|
813
852
|
|
|
814
853
|
self.debug(f"Final combined yara rule contents: {rules_content}")
|
|
@@ -817,13 +856,11 @@ class excavate(BaseInternalModule):
|
|
|
817
856
|
try:
|
|
818
857
|
yara.compile(source=rule_content)
|
|
819
858
|
except yara.SyntaxError as e:
|
|
820
|
-
|
|
821
|
-
return False
|
|
859
|
+
return False, f"Custom Yara rule failed to compile: {e}"
|
|
822
860
|
|
|
823
861
|
rule_match = await self.helpers.re.search(self.yara_rule_name_regex, rule_content)
|
|
824
862
|
if not rule_match:
|
|
825
|
-
|
|
826
|
-
return False
|
|
863
|
+
return False, f"Custom Yara formatted incorrectly: could not find rule name"
|
|
827
864
|
|
|
828
865
|
rule_name = rule_match.groups(1)[0]
|
|
829
866
|
c = CustomExtractor(self)
|
|
@@ -837,11 +874,13 @@ class excavate(BaseInternalModule):
|
|
|
837
874
|
yara.set_config(max_match_data=yara_max_match_data)
|
|
838
875
|
yara_rules_combined = "\n".join(self.yara_rules_dict.values())
|
|
839
876
|
try:
|
|
877
|
+
self.info(f"Compiling {len(self.yara_rules_dict):,} YARA rules")
|
|
878
|
+
for rule_name, rule_content in self.yara_rules_dict.items():
|
|
879
|
+
self.debug(f" - {rule_name}")
|
|
840
880
|
self.yara_rules = yara.compile(source=yara_rules_combined)
|
|
841
881
|
except yara.SyntaxError as e:
|
|
842
|
-
self.hugewarning(f"Yara Rules failed to compile with error: [{e}]")
|
|
843
882
|
self.debug(yara_rules_combined)
|
|
844
|
-
return False
|
|
883
|
+
return False, f"Yara Rules failed to compile with error: [{e}]"
|
|
845
884
|
|
|
846
885
|
# pre-load valid URL schemes
|
|
847
886
|
valid_schemes_filename = self.helpers.wordlist_dir / "valid_url_schemes.txt"
|
|
@@ -858,7 +897,6 @@ class excavate(BaseInternalModule):
|
|
|
858
897
|
decoded_data = await self.helpers.re.recursive_decode(data)
|
|
859
898
|
|
|
860
899
|
if self.parameter_extraction:
|
|
861
|
-
|
|
862
900
|
content_type_lower = content_type.lower() if content_type else ""
|
|
863
901
|
extraction_map = {
|
|
864
902
|
"json": self.helpers.extract_params_json,
|
|
@@ -895,7 +933,6 @@ class excavate(BaseInternalModule):
|
|
|
895
933
|
self.hugewarning(f"YARA Rule {rule_name} not found in pre-compiled rules")
|
|
896
934
|
|
|
897
935
|
async def handle_event(self, event):
|
|
898
|
-
|
|
899
936
|
if event.type == "HTTP_RESPONSE":
|
|
900
937
|
# Harvest GET parameters from URL, if it came directly from the target, and parameter extraction is enabled
|
|
901
938
|
if (
|
|
@@ -984,7 +1021,6 @@ class excavate(BaseInternalModule):
|
|
|
984
1021
|
|
|
985
1022
|
# Try to extract parameters from the redirect URL
|
|
986
1023
|
if self.parameter_extraction:
|
|
987
|
-
|
|
988
1024
|
for (
|
|
989
1025
|
method,
|
|
990
1026
|
parsed_url,
|
|
@@ -32,10 +32,11 @@ class speculate(BaseInternalModule):
|
|
|
32
32
|
"author": "@liquidsec",
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
options = {"max_hosts": 65536, "ports": "80,443"}
|
|
35
|
+
options = {"max_hosts": 65536, "ports": "80,443", "essential_only": False}
|
|
36
36
|
options_desc = {
|
|
37
37
|
"max_hosts": "Max number of IP_RANGE hosts to convert into IP_ADDRESS events",
|
|
38
38
|
"ports": "The set of ports to speculate on",
|
|
39
|
+
"essential_only": "Only enable essential speculate features (no extra discovery)",
|
|
39
40
|
}
|
|
40
41
|
scope_distance_modifier = 1
|
|
41
42
|
_priority = 4
|
|
@@ -44,14 +45,15 @@ class speculate(BaseInternalModule):
|
|
|
44
45
|
|
|
45
46
|
async def setup(self):
|
|
46
47
|
scan_modules = [m for m in self.scan.modules.values() if m._type == "scan"]
|
|
47
|
-
self.open_port_consumers = any(
|
|
48
|
+
self.open_port_consumers = any("OPEN_TCP_PORT" in m.watched_events for m in scan_modules)
|
|
48
49
|
# only consider active portscanners (still speculate if only passive ones are enabled)
|
|
49
50
|
self.portscanner_enabled = any(
|
|
50
|
-
|
|
51
|
+
"portscan" in m.flags and "active" in m.flags for m in self.scan.modules.values()
|
|
51
52
|
)
|
|
52
53
|
self.emit_open_ports = self.open_port_consumers and not self.portscanner_enabled
|
|
53
54
|
self.range_to_ip = True
|
|
54
55
|
self.dns_disable = self.scan.config.get("dns", {}).get("disable", False)
|
|
56
|
+
self.essential_only = self.config.get("essential_only", False)
|
|
55
57
|
self.org_stubs_seen = set()
|
|
56
58
|
|
|
57
59
|
port_string = self.config.get("ports", "80,443")
|
|
@@ -63,18 +65,26 @@ class speculate(BaseInternalModule):
|
|
|
63
65
|
if not self.portscanner_enabled:
|
|
64
66
|
self.info(f"No portscanner enabled. Assuming open ports: {', '.join(str(x) for x in self.ports)}")
|
|
65
67
|
|
|
66
|
-
target_len = len(self.scan.target)
|
|
68
|
+
target_len = len(self.scan.target.seeds)
|
|
67
69
|
if target_len > self.config.get("max_hosts", 65536):
|
|
68
70
|
if not self.portscanner_enabled:
|
|
69
71
|
self.hugewarning(
|
|
70
72
|
f"Selected target ({target_len:,} hosts) is too large, skipping IP_RANGE --> IP_ADDRESS speculation"
|
|
71
73
|
)
|
|
72
|
-
self.hugewarning(
|
|
74
|
+
self.hugewarning('Enabling the "portscan" module is highly recommended')
|
|
73
75
|
self.range_to_ip = False
|
|
74
76
|
|
|
75
77
|
return True
|
|
76
78
|
|
|
77
79
|
async def handle_event(self, event):
|
|
80
|
+
### BEGIN ESSENTIAL SPECULATION ###
|
|
81
|
+
# These features are required for smooth operation of bbot
|
|
82
|
+
# I.e. they are not "osinty" or intended to discover anything, they only compliment other modules
|
|
83
|
+
|
|
84
|
+
# we speculate on distance-1 stuff too, because distance-1 open ports are needed by certain modules like sslcert
|
|
85
|
+
event_in_scope_distance = event.scope_distance <= (self.scan.scope_search_distance + 1)
|
|
86
|
+
speculate_open_ports = self.emit_open_ports and event_in_scope_distance
|
|
87
|
+
|
|
78
88
|
# generate individual IP addresses from IP range
|
|
79
89
|
if event.type == "IP_RANGE" and self.range_to_ip:
|
|
80
90
|
net = ipaddress.ip_network(event.data)
|
|
@@ -89,28 +99,46 @@ class speculate(BaseInternalModule):
|
|
|
89
99
|
context=f"speculate converted range into individual IP_ADDRESS: {ip}",
|
|
90
100
|
)
|
|
91
101
|
|
|
102
|
+
# IP_ADDRESS / DNS_NAME --> OPEN_TCP_PORT
|
|
103
|
+
if speculate_open_ports:
|
|
104
|
+
# don't act on unresolved DNS_NAMEs
|
|
105
|
+
usable_dns = False
|
|
106
|
+
if event.type == "DNS_NAME":
|
|
107
|
+
if self.dns_disable or ("a-record" in event.tags or "aaaa-record" in event.tags):
|
|
108
|
+
usable_dns = True
|
|
109
|
+
|
|
110
|
+
if event.type == "IP_ADDRESS" or usable_dns:
|
|
111
|
+
for port in self.ports:
|
|
112
|
+
await self.emit_event(
|
|
113
|
+
self.helpers.make_netloc(event.data, port),
|
|
114
|
+
"OPEN_TCP_PORT",
|
|
115
|
+
parent=event,
|
|
116
|
+
internal=True,
|
|
117
|
+
context="speculated {event.type}: {event.data}",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
### END ESSENTIAL SPECULATION ###
|
|
121
|
+
if self.essential_only:
|
|
122
|
+
return
|
|
123
|
+
|
|
92
124
|
# parent domains
|
|
93
125
|
if event.type.startswith("DNS_NAME"):
|
|
94
|
-
parent = self.helpers.parent_domain(event.
|
|
126
|
+
parent = self.helpers.parent_domain(event.host_original)
|
|
95
127
|
if parent != event.data:
|
|
96
128
|
await self.emit_event(
|
|
97
|
-
parent, "DNS_NAME", parent=event, context=
|
|
129
|
+
parent, "DNS_NAME", parent=event, context="speculated parent {event.type}: {event.data}"
|
|
98
130
|
)
|
|
99
131
|
|
|
100
|
-
# we speculate on distance-1 stuff too, because distance-1 open ports are needed by certain modules like sslcert
|
|
101
|
-
event_in_scope_distance = event.scope_distance <= (self.scan.scope_search_distance + 1)
|
|
102
|
-
speculate_open_ports = self.emit_open_ports and event_in_scope_distance
|
|
103
|
-
|
|
104
132
|
# URL --> OPEN_TCP_PORT
|
|
105
|
-
|
|
133
|
+
event_is_url = event.type == "URL"
|
|
134
|
+
if event_is_url or (event.type == "URL_UNVERIFIED" and self.open_port_consumers):
|
|
106
135
|
# only speculate port from a URL if it wouldn't be speculated naturally from the host
|
|
107
136
|
if event.host and (event.port not in self.ports or not speculate_open_ports):
|
|
108
137
|
await self.emit_event(
|
|
109
138
|
self.helpers.make_netloc(event.host, event.port),
|
|
110
139
|
"OPEN_TCP_PORT",
|
|
111
140
|
parent=event,
|
|
112
|
-
internal=
|
|
113
|
-
quick=(event.type == "URL"),
|
|
141
|
+
internal=not event_is_url, # if the URL is verified, the port is definitely open
|
|
114
142
|
context=f"speculated {{event.type}} from {event.type}: {{event.data}}",
|
|
115
143
|
)
|
|
116
144
|
|
|
@@ -144,25 +172,6 @@ class speculate(BaseInternalModule):
|
|
|
144
172
|
context="speculated {event.type}: {event.data}",
|
|
145
173
|
)
|
|
146
174
|
|
|
147
|
-
# IP_ADDRESS / DNS_NAME --> OPEN_TCP_PORT
|
|
148
|
-
if speculate_open_ports:
|
|
149
|
-
# don't act on unresolved DNS_NAMEs
|
|
150
|
-
usable_dns = False
|
|
151
|
-
if event.type == "DNS_NAME":
|
|
152
|
-
if self.dns_disable or ("a-record" in event.tags or "aaaa-record" in event.tags):
|
|
153
|
-
usable_dns = True
|
|
154
|
-
|
|
155
|
-
if event.type == "IP_ADDRESS" or usable_dns:
|
|
156
|
-
for port in self.ports:
|
|
157
|
-
await self.emit_event(
|
|
158
|
-
self.helpers.make_netloc(event.data, port),
|
|
159
|
-
"OPEN_TCP_PORT",
|
|
160
|
-
parent=event,
|
|
161
|
-
internal=True,
|
|
162
|
-
quick=True,
|
|
163
|
-
context="speculated {event.type}: {event.data}",
|
|
164
|
-
)
|
|
165
|
-
|
|
166
175
|
# ORG_STUB from TLD, SOCIAL, AZURE_TENANT
|
|
167
176
|
org_stubs = set()
|
|
168
177
|
if event.type == "DNS_NAME" and event.scope_distance == 0:
|
bbot/modules/internetdb.py
CHANGED
|
@@ -48,6 +48,9 @@ class internetdb(BaseModule):
|
|
|
48
48
|
"show_open_ports": "Display OPEN_TCP_PORT events in output, even if they didn't lead to an interesting discovery"
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
# we get lots of 404s, that's normal
|
|
52
|
+
_api_failure_abort_threshold = 9999999999
|
|
53
|
+
|
|
51
54
|
_qsize = 500
|
|
52
55
|
|
|
53
56
|
base_url = "https://internetdb.shodan.io"
|
|
@@ -64,7 +67,7 @@ class internetdb(BaseModule):
|
|
|
64
67
|
if ip is None:
|
|
65
68
|
return
|
|
66
69
|
url = f"{self.base_url}/{ip}"
|
|
67
|
-
r = await self.
|
|
70
|
+
r = await self.api_request(url)
|
|
68
71
|
if r is None:
|
|
69
72
|
self.debug(f"No response for {event.data}")
|
|
70
73
|
return
|
|
@@ -113,7 +116,6 @@ class internetdb(BaseModule):
|
|
|
113
116
|
"OPEN_TCP_PORT",
|
|
114
117
|
parent=event,
|
|
115
118
|
internal=(not self.show_open_ports),
|
|
116
|
-
quick=True,
|
|
117
119
|
context=f'{{module}} queried Shodan\'s InternetDB API for "{query_host}" and found {{event.type}}: {{event.data}}',
|
|
118
120
|
)
|
|
119
121
|
vulns = data.get("vulns", [])
|
bbot/modules/ip2location.py
CHANGED
|
@@ -32,12 +32,10 @@ class IP2Location(BaseModule):
|
|
|
32
32
|
|
|
33
33
|
async def ping(self):
|
|
34
34
|
url = self.build_url("8.8.8.8")
|
|
35
|
-
|
|
36
|
-
resp_content = getattr(r, "text", "")
|
|
37
|
-
assert getattr(r, "status_code", 0) == 200, resp_content
|
|
35
|
+
await super().ping(url)
|
|
38
36
|
|
|
39
37
|
def build_url(self, data):
|
|
40
|
-
url = f"{self.base_url}/?key={
|
|
38
|
+
url = f"{self.base_url}/?key={{api_key}}&ip={data}&format=json&source=bbot"
|
|
41
39
|
if self.lang:
|
|
42
40
|
url = f"{url}&lang={self.lang}"
|
|
43
41
|
return url
|
|
@@ -45,7 +43,7 @@ class IP2Location(BaseModule):
|
|
|
45
43
|
async def handle_event(self, event):
|
|
46
44
|
try:
|
|
47
45
|
url = self.build_url(event.data)
|
|
48
|
-
result = await self.
|
|
46
|
+
result = await self.api_request(url)
|
|
49
47
|
if result:
|
|
50
48
|
geo_data = result.json()
|
|
51
49
|
if not geo_data:
|
bbot/modules/ipneighbor.py
CHANGED
|
@@ -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
|
|
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/ipstack.py
CHANGED
|
@@ -23,20 +23,15 @@ class Ipstack(BaseModule):
|
|
|
23
23
|
suppress_dupes = False
|
|
24
24
|
|
|
25
25
|
base_url = "http://api.ipstack.com"
|
|
26
|
+
ping_url = f"{base_url}/check?access_key={{api_key}}"
|
|
26
27
|
|
|
27
28
|
async def setup(self):
|
|
28
29
|
return await self.require_api_key()
|
|
29
30
|
|
|
30
|
-
async def ping(self):
|
|
31
|
-
url = f"{self.base_url}/check?access_key={self.api_key}"
|
|
32
|
-
r = await self.request_with_fail_count(url)
|
|
33
|
-
resp_content = getattr(r, "text", "")
|
|
34
|
-
assert getattr(r, "status_code", 0) == 200, resp_content
|
|
35
|
-
|
|
36
31
|
async def handle_event(self, event):
|
|
37
32
|
try:
|
|
38
|
-
url = f"{self.base_url}/{event.data}?access_key={
|
|
39
|
-
result = await self.
|
|
33
|
+
url = f"{self.base_url}/{event.data}?access_key={{api_key}}"
|
|
34
|
+
result = await self.api_request(url)
|
|
40
35
|
if result:
|
|
41
36
|
geo_data = result.json()
|
|
42
37
|
if not geo_data:
|