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
bbot/core/helpers/misc.py
CHANGED
|
@@ -227,13 +227,13 @@ def split_host_port(d):
|
|
|
227
227
|
|
|
228
228
|
match = bbot_regexes.extract_open_port_regex.match(netloc)
|
|
229
229
|
if match is None:
|
|
230
|
-
raise ValueError(f'split_port() failed to parse netloc "{netloc}"')
|
|
230
|
+
raise ValueError(f'split_port() failed to parse netloc "{netloc}" (original value: {d})')
|
|
231
231
|
|
|
232
232
|
host = match.group(2)
|
|
233
233
|
if host is None:
|
|
234
234
|
host = match.group(1)
|
|
235
235
|
if host is None:
|
|
236
|
-
raise ValueError(f'split_port() failed to locate host in netloc "{netloc}"')
|
|
236
|
+
raise ValueError(f'split_port() failed to locate host in netloc "{netloc}" (original value: {d})')
|
|
237
237
|
|
|
238
238
|
port = match.group(3)
|
|
239
239
|
if port is None and scheme is not None:
|
|
@@ -365,7 +365,7 @@ def parent_url(u):
|
|
|
365
365
|
if path.parent == path:
|
|
366
366
|
return None
|
|
367
367
|
else:
|
|
368
|
-
return urlunparse(parsed._replace(path=str(path.parent)))
|
|
368
|
+
return urlunparse(parsed._replace(path=str(path.parent), query=""))
|
|
369
369
|
|
|
370
370
|
|
|
371
371
|
def url_parents(u):
|
|
@@ -391,7 +391,7 @@ def url_parents(u):
|
|
|
391
391
|
parent_list = []
|
|
392
392
|
while 1:
|
|
393
393
|
parent = parent_url(u)
|
|
394
|
-
if parent
|
|
394
|
+
if parent is None:
|
|
395
395
|
return parent_list
|
|
396
396
|
elif parent not in parent_list:
|
|
397
397
|
parent_list.append(parent)
|
|
@@ -512,7 +512,7 @@ def domain_stem(domain):
|
|
|
512
512
|
- Utilizes the `tldextract` function for domain parsing.
|
|
513
513
|
"""
|
|
514
514
|
parsed = tldextract(str(domain))
|
|
515
|
-
return
|
|
515
|
+
return ".".join(parsed.subdomain.split(".") + parsed.domain.split(".")).strip(".")
|
|
516
516
|
|
|
517
517
|
|
|
518
518
|
def ip_network_parents(i, include_self=False):
|
|
@@ -586,17 +586,18 @@ def is_dns_name(d, include_local=True):
|
|
|
586
586
|
if include_local:
|
|
587
587
|
if bbot_regexes.hostname_regex.match(d):
|
|
588
588
|
return True
|
|
589
|
-
if bbot_regexes.
|
|
589
|
+
if bbot_regexes.dns_name_validation_regex.match(d):
|
|
590
590
|
return True
|
|
591
591
|
return False
|
|
592
592
|
|
|
593
593
|
|
|
594
|
-
def is_ip(d, version=None):
|
|
594
|
+
def is_ip(d, version=None, include_network=False):
|
|
595
595
|
"""
|
|
596
596
|
Checks if the given string or object represents a valid IP address.
|
|
597
597
|
|
|
598
598
|
Args:
|
|
599
599
|
d (str or ipaddress.IPvXAddress): The IP address to check.
|
|
600
|
+
include_network (bool, optional): Whether to include network types (IPv4Network or IPv6Network). Defaults to False.
|
|
600
601
|
version (int, optional): The IP version to validate (4 or 6). Default is None.
|
|
601
602
|
|
|
602
603
|
Returns:
|
|
@@ -612,21 +613,27 @@ def is_ip(d, version=None):
|
|
|
612
613
|
>>> is_ip('evilcorp.com')
|
|
613
614
|
False
|
|
614
615
|
"""
|
|
616
|
+
ip = None
|
|
615
617
|
try:
|
|
616
618
|
ip = ipaddress.ip_address(d)
|
|
617
|
-
if version is None or ip.version == version:
|
|
618
|
-
return True
|
|
619
619
|
except Exception:
|
|
620
|
-
|
|
620
|
+
if include_network:
|
|
621
|
+
try:
|
|
622
|
+
ip = ipaddress.ip_network(d, strict=False)
|
|
623
|
+
except Exception:
|
|
624
|
+
pass
|
|
625
|
+
if ip is not None and (version is None or ip.version == version):
|
|
626
|
+
return True
|
|
621
627
|
return False
|
|
622
628
|
|
|
623
629
|
|
|
624
|
-
def is_ip_type(i):
|
|
630
|
+
def is_ip_type(i, network=None):
|
|
625
631
|
"""
|
|
626
632
|
Checks if the given object is an instance of an IPv4 or IPv6 type from the ipaddress module.
|
|
627
633
|
|
|
628
634
|
Args:
|
|
629
635
|
i (ipaddress._BaseV4 or ipaddress._BaseV6): The IP object to check.
|
|
636
|
+
network (bool, optional): Whether to restrict the check to network types (IPv4Network or IPv6Network). Defaults to False.
|
|
630
637
|
|
|
631
638
|
Returns:
|
|
632
639
|
bool: True if the object is an instance of ipaddress._BaseV4 or ipaddress._BaseV6, False otherwise.
|
|
@@ -639,6 +646,12 @@ def is_ip_type(i):
|
|
|
639
646
|
>>> is_ip_type("192.168.1.0/24")
|
|
640
647
|
False
|
|
641
648
|
"""
|
|
649
|
+
if network is not None:
|
|
650
|
+
is_network = ipaddress._BaseNetwork in i.__class__.__mro__
|
|
651
|
+
if network:
|
|
652
|
+
return is_network
|
|
653
|
+
else:
|
|
654
|
+
return not is_network
|
|
642
655
|
return ipaddress._IPAddressBase in i.__class__.__mro__
|
|
643
656
|
|
|
644
657
|
|
|
@@ -908,12 +921,12 @@ def extract_params_xml(xml_data, compare_mode="getparam"):
|
|
|
908
921
|
|
|
909
922
|
# Define valid characters for each mode based on RFCs
|
|
910
923
|
valid_chars_dict = {
|
|
911
|
-
"header":
|
|
924
|
+
"header": {
|
|
912
925
|
chr(c) for c in range(33, 127) if chr(c) in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
|
|
913
|
-
|
|
914
|
-
"getparam":
|
|
915
|
-
"postparam":
|
|
916
|
-
"cookie":
|
|
926
|
+
},
|
|
927
|
+
"getparam": {chr(c) for c in range(33, 127) if chr(c) not in ":/?#[]@!$&'()*+,;="},
|
|
928
|
+
"postparam": {chr(c) for c in range(33, 127) if chr(c) not in ":/?#[]@!$&'()*+,;="},
|
|
929
|
+
"cookie": {chr(c) for c in range(33, 127) if chr(c) not in '()<>@,;:"/[]?={} \t'},
|
|
917
930
|
}
|
|
918
931
|
|
|
919
932
|
|
|
@@ -1135,7 +1148,7 @@ def chain_lists(
|
|
|
1135
1148
|
"""
|
|
1136
1149
|
if isinstance(l, str):
|
|
1137
1150
|
l = [l]
|
|
1138
|
-
final_list =
|
|
1151
|
+
final_list = {}
|
|
1139
1152
|
for entry in l:
|
|
1140
1153
|
for s in split_regex.split(entry):
|
|
1141
1154
|
f = s.strip()
|
|
@@ -1260,7 +1273,7 @@ def gen_numbers(n, padding=2):
|
|
|
1260
1273
|
return results
|
|
1261
1274
|
|
|
1262
1275
|
|
|
1263
|
-
def make_netloc(host, port):
|
|
1276
|
+
def make_netloc(host, port=None):
|
|
1264
1277
|
"""Constructs a network location string from a given host and port.
|
|
1265
1278
|
|
|
1266
1279
|
Args:
|
|
@@ -1289,7 +1302,7 @@ def make_netloc(host, port):
|
|
|
1289
1302
|
if is_ip(host, version=6):
|
|
1290
1303
|
host = f"[{host}]"
|
|
1291
1304
|
if port is None:
|
|
1292
|
-
return host
|
|
1305
|
+
return str(host)
|
|
1293
1306
|
return f"{host}:{port}"
|
|
1294
1307
|
|
|
1295
1308
|
|
|
@@ -1332,7 +1345,7 @@ def search_dict_by_key(key, d):
|
|
|
1332
1345
|
if isinstance(d, dict):
|
|
1333
1346
|
if key in d:
|
|
1334
1347
|
yield d[key]
|
|
1335
|
-
for
|
|
1348
|
+
for v in d.values():
|
|
1336
1349
|
yield from search_dict_by_key(key, v)
|
|
1337
1350
|
elif isinstance(d, list):
|
|
1338
1351
|
for v in d:
|
|
@@ -1399,7 +1412,7 @@ def search_dict_values(d, *regexes):
|
|
|
1399
1412
|
results.add(h)
|
|
1400
1413
|
yield result
|
|
1401
1414
|
elif isinstance(d, dict):
|
|
1402
|
-
for
|
|
1415
|
+
for v in d.values():
|
|
1403
1416
|
yield from search_dict_values(v, *regexes)
|
|
1404
1417
|
elif isinstance(d, list):
|
|
1405
1418
|
for v in d:
|
|
@@ -2384,7 +2397,7 @@ def in_exception_chain(e, exc_types):
|
|
|
2384
2397
|
... if not in_exception_chain(e, (KeyboardInterrupt, asyncio.CancelledError)):
|
|
2385
2398
|
... raise
|
|
2386
2399
|
"""
|
|
2387
|
-
return any(
|
|
2400
|
+
return any(isinstance(_, exc_types) for _ in get_exception_chain(e))
|
|
2388
2401
|
|
|
2389
2402
|
|
|
2390
2403
|
def get_traceback_details(e):
|
|
@@ -2788,3 +2801,33 @@ def top_tcp_ports(n, as_string=False):
|
|
|
2788
2801
|
if as_string:
|
|
2789
2802
|
return ",".join([str(s) for s in top_ports])
|
|
2790
2803
|
return top_ports
|
|
2804
|
+
|
|
2805
|
+
|
|
2806
|
+
class SafeDict(dict):
|
|
2807
|
+
def __missing__(self, key):
|
|
2808
|
+
return "{" + key + "}"
|
|
2809
|
+
|
|
2810
|
+
|
|
2811
|
+
def safe_format(s, **kwargs):
|
|
2812
|
+
"""
|
|
2813
|
+
Format string while ignoring unused keys (prevents KeyError)
|
|
2814
|
+
"""
|
|
2815
|
+
return s.format_map(SafeDict(kwargs))
|
|
2816
|
+
|
|
2817
|
+
|
|
2818
|
+
def get_python_constraints():
|
|
2819
|
+
req_regex = re.compile(r"([^(]+)\s*\((.*)\)", re.IGNORECASE)
|
|
2820
|
+
|
|
2821
|
+
def clean_requirement(req_string):
|
|
2822
|
+
# Extract package name and version constraints from format like "package (>=1.0,<2.0)"
|
|
2823
|
+
match = req_regex.match(req_string)
|
|
2824
|
+
if match:
|
|
2825
|
+
name, constraints = match.groups()
|
|
2826
|
+
return f"{name.strip()}{constraints}"
|
|
2827
|
+
|
|
2828
|
+
return req_string
|
|
2829
|
+
|
|
2830
|
+
from importlib.metadata import distribution
|
|
2831
|
+
|
|
2832
|
+
dist = distribution("bbot")
|
|
2833
|
+
return [clean_requirement(r) for r in dist.requires]
|
|
@@ -10,9 +10,11 @@ adjectives = [
|
|
|
10
10
|
"affectionate",
|
|
11
11
|
"aggravated",
|
|
12
12
|
"aggrieved",
|
|
13
|
+
"agoraphobic",
|
|
13
14
|
"almighty",
|
|
14
15
|
"anal",
|
|
15
16
|
"atrocious",
|
|
17
|
+
"autistic",
|
|
16
18
|
"awkward",
|
|
17
19
|
"baby",
|
|
18
20
|
"begrudged",
|
|
@@ -65,6 +67,7 @@ adjectives = [
|
|
|
65
67
|
"dramatic",
|
|
66
68
|
"drunk",
|
|
67
69
|
"effeminate",
|
|
70
|
+
"effervescent",
|
|
68
71
|
"elden",
|
|
69
72
|
"eldritch",
|
|
70
73
|
"embarrassed",
|
|
@@ -75,6 +78,7 @@ adjectives = [
|
|
|
75
78
|
"ethereal",
|
|
76
79
|
"euphoric",
|
|
77
80
|
"evil",
|
|
81
|
+
"expired",
|
|
78
82
|
"exquisite",
|
|
79
83
|
"extreme",
|
|
80
84
|
"ferocious",
|
|
@@ -87,10 +91,8 @@ adjectives = [
|
|
|
87
91
|
"foreboding",
|
|
88
92
|
"frenetic",
|
|
89
93
|
"frolicking",
|
|
90
|
-
"frothy",
|
|
91
94
|
"furry",
|
|
92
95
|
"fuzzy",
|
|
93
|
-
"gay",
|
|
94
96
|
"gentle",
|
|
95
97
|
"giddy",
|
|
96
98
|
"glowering",
|
|
@@ -112,6 +114,7 @@ adjectives = [
|
|
|
112
114
|
"imaginary",
|
|
113
115
|
"immense",
|
|
114
116
|
"immoral",
|
|
117
|
+
"impulsive",
|
|
115
118
|
"incomprehensible",
|
|
116
119
|
"inebriated",
|
|
117
120
|
"inexplicable",
|
|
@@ -149,6 +152,7 @@ adjectives = [
|
|
|
149
152
|
"muscular",
|
|
150
153
|
"mushy",
|
|
151
154
|
"mysterious",
|
|
155
|
+
"nascent",
|
|
152
156
|
"naughty",
|
|
153
157
|
"nefarious",
|
|
154
158
|
"negligent",
|
|
@@ -163,6 +167,7 @@ adjectives = [
|
|
|
163
167
|
"overzealous",
|
|
164
168
|
"paranoid",
|
|
165
169
|
"pasty",
|
|
170
|
+
"peckish",
|
|
166
171
|
"pedantic",
|
|
167
172
|
"pernicious",
|
|
168
173
|
"perturbed",
|
|
@@ -183,7 +188,6 @@ adjectives = [
|
|
|
183
188
|
"psychic",
|
|
184
189
|
"puffy",
|
|
185
190
|
"pure",
|
|
186
|
-
"queer",
|
|
187
191
|
"questionable",
|
|
188
192
|
"rabid",
|
|
189
193
|
"raging",
|
|
@@ -210,6 +214,7 @@ adjectives = [
|
|
|
210
214
|
"sneaky",
|
|
211
215
|
"soft",
|
|
212
216
|
"sophisticated",
|
|
217
|
+
"spicy",
|
|
213
218
|
"spiteful",
|
|
214
219
|
"squishy",
|
|
215
220
|
"steamy",
|
|
@@ -269,12 +274,14 @@ adjectives = [
|
|
|
269
274
|
"wispy",
|
|
270
275
|
"witty",
|
|
271
276
|
"woolly",
|
|
277
|
+
"zesty",
|
|
272
278
|
]
|
|
273
279
|
|
|
274
280
|
names = [
|
|
275
281
|
"aaron",
|
|
276
282
|
"abigail",
|
|
277
283
|
"adam",
|
|
284
|
+
"adeem",
|
|
278
285
|
"alan",
|
|
279
286
|
"albert",
|
|
280
287
|
"alex",
|
|
@@ -412,6 +419,7 @@ names = [
|
|
|
412
419
|
"evelyn",
|
|
413
420
|
"faramir",
|
|
414
421
|
"florence",
|
|
422
|
+
"fox",
|
|
415
423
|
"frances",
|
|
416
424
|
"francis",
|
|
417
425
|
"frank",
|
|
@@ -437,6 +445,7 @@ names = [
|
|
|
437
445
|
"gregory",
|
|
438
446
|
"gus",
|
|
439
447
|
"hagrid",
|
|
448
|
+
"hank",
|
|
440
449
|
"hannah",
|
|
441
450
|
"harold",
|
|
442
451
|
"harry",
|
|
@@ -447,6 +456,7 @@ names = [
|
|
|
447
456
|
"hermione",
|
|
448
457
|
"homer",
|
|
449
458
|
"howard",
|
|
459
|
+
"hunter",
|
|
450
460
|
"irene",
|
|
451
461
|
"isaac",
|
|
452
462
|
"isabella",
|
|
@@ -505,6 +515,7 @@ names = [
|
|
|
505
515
|
"kevin",
|
|
506
516
|
"kimberly",
|
|
507
517
|
"kyle",
|
|
518
|
+
"kylie",
|
|
508
519
|
"lantern",
|
|
509
520
|
"larry",
|
|
510
521
|
"laura",
|
|
@@ -528,6 +539,7 @@ names = [
|
|
|
528
539
|
"lupin",
|
|
529
540
|
"madison",
|
|
530
541
|
"magnus",
|
|
542
|
+
"marcus",
|
|
531
543
|
"margaret",
|
|
532
544
|
"maria",
|
|
533
545
|
"marie",
|
|
@@ -626,11 +638,13 @@ names = [
|
|
|
626
638
|
"stephen",
|
|
627
639
|
"steven",
|
|
628
640
|
"susan",
|
|
641
|
+
"syrina",
|
|
629
642
|
"tammy",
|
|
630
643
|
"taylor",
|
|
631
644
|
"teresa",
|
|
632
645
|
"terry",
|
|
633
646
|
"theoden",
|
|
647
|
+
"theon",
|
|
634
648
|
"theresa",
|
|
635
649
|
"thomas",
|
|
636
650
|
"tiffany",
|
bbot/core/helpers/process.py
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import traceback
|
|
3
3
|
import threading
|
|
4
|
-
import multiprocessing
|
|
5
4
|
from multiprocessing.context import SpawnProcess
|
|
6
5
|
|
|
7
6
|
from .misc import in_exception_chain
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
current_process = multiprocessing.current_process()
|
|
11
|
-
|
|
12
|
-
|
|
13
9
|
class BBOTThread(threading.Thread):
|
|
14
|
-
|
|
15
10
|
default_name = "default bbot thread"
|
|
16
11
|
|
|
17
12
|
def __init__(self, *args, **kwargs):
|
|
@@ -28,7 +23,6 @@ class BBOTThread(threading.Thread):
|
|
|
28
23
|
|
|
29
24
|
|
|
30
25
|
class BBOTProcess(SpawnProcess):
|
|
31
|
-
|
|
32
26
|
default_name = "bbot process pool"
|
|
33
27
|
|
|
34
28
|
def __init__(self, *args, **kwargs):
|
|
@@ -57,17 +51,3 @@ class BBOTProcess(SpawnProcess):
|
|
|
57
51
|
if not in_exception_chain(e, (KeyboardInterrupt,)):
|
|
58
52
|
log.warning(f"Error in {self.name}: {e}")
|
|
59
53
|
log.trace(traceback.format_exc())
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if current_process.name == "MainProcess":
|
|
63
|
-
# if this is the main bbot process, set the logger and queue for the first time
|
|
64
|
-
from bbot.core import CORE
|
|
65
|
-
from functools import partialmethod
|
|
66
|
-
|
|
67
|
-
BBOTProcess.__init__ = partialmethod(
|
|
68
|
-
BBOTProcess.__init__, log_level=CORE.logger.log_level, log_queue=CORE.logger.queue
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
# this makes our process class the default for process pools, etc.
|
|
72
|
-
mp_context = multiprocessing.get_context("spawn")
|
|
73
|
-
mp_context.Process = BBOTProcess
|
bbot/core/helpers/regex.py
CHANGED
|
@@ -41,7 +41,7 @@ class RegexHelper:
|
|
|
41
41
|
"""
|
|
42
42
|
if not isinstance(compiled_regexes, dict):
|
|
43
43
|
raise ValueError('compiled_regexes must be a dictionary like this: {"regex_name": <compiled_regex>}')
|
|
44
|
-
for
|
|
44
|
+
for v in compiled_regexes.values():
|
|
45
45
|
self.ensure_compiled_regex(v)
|
|
46
46
|
|
|
47
47
|
tasks = {}
|
bbot/core/helpers/regexes.py
CHANGED
|
@@ -23,7 +23,7 @@ num_regex = re.compile(r"\d+")
|
|
|
23
23
|
_ipv4_regex = r"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}"
|
|
24
24
|
ipv4_regex = re.compile(_ipv4_regex, re.I)
|
|
25
25
|
|
|
26
|
-
# IPv6 is complicated, so we have
|
|
26
|
+
# IPv6 is complicated, so we have accommodate alternative patterns,
|
|
27
27
|
# :(:[A-F0-9]{1,4}){1,7} == ::1, ::ffff:1
|
|
28
28
|
# ([A-F0-9]{1,4}:){1,7}: == 2001::, 2001:db8::, 2001:db8:0:1:2:3::
|
|
29
29
|
# ([A-F0-9]{1,4}:){1,6}:([A-F0-9]{1,4}) == 2001::1, 2001:db8::1, 2001:db8:0:1:2:3::1
|
|
@@ -36,11 +36,12 @@ _ip_range_regexes = (
|
|
|
36
36
|
_ipv4_regex + r"\/[0-9]{1,2}",
|
|
37
37
|
_ipv6_regex + r"\/[0-9]{1,3}",
|
|
38
38
|
)
|
|
39
|
-
ip_range_regexes =
|
|
39
|
+
ip_range_regexes = [re.compile(r, re.I) for r in _ip_range_regexes]
|
|
40
40
|
|
|
41
41
|
# dns names with periods
|
|
42
42
|
_dns_name_regex = r"(?:\w(?:[\w-]{0,100}\w)?\.)+(?:[xX][nN]--)?[^\W_]{1,63}\.?"
|
|
43
|
-
|
|
43
|
+
dns_name_extraction_regex = re.compile(_dns_name_regex, re.I)
|
|
44
|
+
dns_name_validation_regex = re.compile(r"^" + _dns_name_regex + r"$", re.I)
|
|
44
45
|
|
|
45
46
|
# dns names without periods
|
|
46
47
|
_hostname_regex = r"(?!\w*\.\w+)\w(?:[\w-]{0,100}\w)?"
|
|
@@ -54,20 +55,23 @@ ptr_regex = re.compile(_ptr_regex)
|
|
|
54
55
|
# uuid regex
|
|
55
56
|
_uuid_regex = r"[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}"
|
|
56
57
|
uuid_regex = re.compile(_uuid_regex, re.I)
|
|
58
|
+
# event uuid regex
|
|
59
|
+
_event_uuid_regex = r"[0-9A-Z_]+:[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}"
|
|
60
|
+
event_uuid_regex = re.compile(_event_uuid_regex, re.I)
|
|
57
61
|
|
|
58
62
|
_open_port_regexes = (
|
|
59
63
|
_dns_name_regex + r":[0-9]{1,5}",
|
|
60
64
|
_hostname_regex + r":[0-9]{1,5}",
|
|
61
65
|
r"\[" + _ipv6_regex + r"\]:[0-9]{1,5}",
|
|
62
66
|
)
|
|
63
|
-
open_port_regexes =
|
|
67
|
+
open_port_regexes = [re.compile(r, re.I) for r in _open_port_regexes]
|
|
64
68
|
|
|
65
69
|
_url_regexes = (
|
|
66
70
|
r"https?://" + _dns_name_regex + r"(?::[0-9]{1,5})?(?:(?:/|\?).*)?",
|
|
67
71
|
r"https?://" + _hostname_regex + r"(?::[0-9]{1,5})?(?:(?:/|\?).*)?",
|
|
68
72
|
r"https?://\[" + _ipv6_regex + r"\](?::[0-9]{1,5})?(?:(?:/|\?).*)?",
|
|
69
73
|
)
|
|
70
|
-
url_regexes =
|
|
74
|
+
url_regexes = [re.compile(r, re.I) for r in _url_regexes]
|
|
71
75
|
|
|
72
76
|
_double_slash_regex = r"/{2,}"
|
|
73
77
|
double_slash_regex = re.compile(_double_slash_regex)
|
|
@@ -114,7 +118,7 @@ event_type_regexes = OrderedDict(
|
|
|
114
118
|
scan_name_regex = re.compile(r"[a-z]{3,20}_[a-z]{3,20}")
|
|
115
119
|
|
|
116
120
|
|
|
117
|
-
# For use with excavate
|
|
121
|
+
# For use with excavate parameters extractor
|
|
118
122
|
input_tag_regex = re.compile(
|
|
119
123
|
r"<input[^>]+?name=[\"\']?([\.$\w]+)[\"\']?(?:[^>]*?value=[\"\']([=+\/\w]*)[\"\'])?[^>]*>"
|
|
120
124
|
)
|
|
@@ -152,3 +156,5 @@ extract_host_regex = re.compile(_extract_host_regex, re.I)
|
|
|
152
156
|
# for use in recursive_decode()
|
|
153
157
|
encoded_regex = re.compile(r"%[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|\\[ntrbv]")
|
|
154
158
|
backslash_regex = re.compile(r"(?P<slashes>\\+)(?P<char>[ntrvb])")
|
|
159
|
+
|
|
160
|
+
uuid_regex = re.compile(r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
|
bbot/core/helpers/validators.py
CHANGED
|
@@ -132,7 +132,7 @@ def validate_host(host: Union[str, ipaddress.IPv4Address, ipaddress.IPv6Address]
|
|
|
132
132
|
@validator
|
|
133
133
|
def validate_severity(severity: str):
|
|
134
134
|
severity = str(severity).strip().upper()
|
|
135
|
-
if not
|
|
135
|
+
if severity not in ("UNKNOWN", "INFO", "LOW", "MEDIUM", "HIGH", "CRITICAL"):
|
|
136
136
|
raise ValueError(f"Invalid severity: {severity}")
|
|
137
137
|
return severity
|
|
138
138
|
|
|
@@ -299,7 +299,6 @@ def is_email(email):
|
|
|
299
299
|
|
|
300
300
|
|
|
301
301
|
class Validators:
|
|
302
|
-
|
|
303
302
|
def __init__(self, parent_helper):
|
|
304
303
|
self.parent_helper = parent_helper
|
|
305
304
|
|
bbot/core/helpers/web/client.py
CHANGED
bbot/core/helpers/web/engine.py
CHANGED
|
@@ -14,7 +14,6 @@ log = logging.getLogger("bbot.core.helpers.web.engine")
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class HTTPEngine(EngineServer):
|
|
17
|
-
|
|
18
17
|
CMDS = {
|
|
19
18
|
0: "request",
|
|
20
19
|
1: "request_batch",
|
|
@@ -81,15 +80,20 @@ class HTTPEngine(EngineServer):
|
|
|
81
80
|
if client_kwargs:
|
|
82
81
|
client = self.AsyncClient(**client_kwargs)
|
|
83
82
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
83
|
+
try:
|
|
84
|
+
async with self._acatch(url, raise_error):
|
|
85
|
+
if self.http_debug:
|
|
86
|
+
log.trace(f"Web request: {str(args)}, {str(kwargs)}")
|
|
87
|
+
response = await client.request(*args, **kwargs)
|
|
88
|
+
if self.http_debug:
|
|
89
|
+
log.trace(
|
|
90
|
+
f"Web response from {url}: {response} (Length: {len(response.content)}) headers: {response.headers}"
|
|
91
|
+
)
|
|
92
|
+
return response
|
|
93
|
+
except httpx.HTTPError as e:
|
|
94
|
+
if raise_error:
|
|
95
|
+
_response = getattr(e, "response", None)
|
|
96
|
+
return {"_request_error": str(e), "_response": _response}
|
|
93
97
|
|
|
94
98
|
async def request_batch(self, urls, threads=10, **kwargs):
|
|
95
99
|
async for (args, _, _), response in self.task_pool(
|
|
@@ -105,8 +109,8 @@ class HTTPEngine(EngineServer):
|
|
|
105
109
|
|
|
106
110
|
async def download(self, url, **kwargs):
|
|
107
111
|
warn = kwargs.pop("warn", True)
|
|
112
|
+
raise_error = kwargs.pop("raise_error", False)
|
|
108
113
|
filename = kwargs.pop("filename")
|
|
109
|
-
raise_error = kwargs.get("raise_error", False)
|
|
110
114
|
try:
|
|
111
115
|
result = await self.stream_request(url, **kwargs)
|
|
112
116
|
if result is None:
|
|
@@ -123,7 +127,8 @@ class HTTPEngine(EngineServer):
|
|
|
123
127
|
log_fn = log.warning
|
|
124
128
|
log_fn(f"Failed to download {url}: {e}")
|
|
125
129
|
if raise_error:
|
|
126
|
-
|
|
130
|
+
_response = getattr(e, "response", None)
|
|
131
|
+
return {"_download_error": str(e), "_response": _response}
|
|
127
132
|
|
|
128
133
|
async def stream_request(self, url, **kwargs):
|
|
129
134
|
follow_redirects = kwargs.pop("follow_redirects", True)
|
|
@@ -132,7 +137,7 @@ class HTTPEngine(EngineServer):
|
|
|
132
137
|
if max_size is not None:
|
|
133
138
|
max_size = human_to_bytes(max_size)
|
|
134
139
|
kwargs["follow_redirects"] = follow_redirects
|
|
135
|
-
if
|
|
140
|
+
if "method" not in kwargs:
|
|
136
141
|
kwargs["method"] = "GET"
|
|
137
142
|
try:
|
|
138
143
|
total_size = 0
|