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
|
@@ -23,7 +23,7 @@ async def test_dns_engine(bbot_scanner):
|
|
|
23
23
|
)
|
|
24
24
|
result = await scan.helpers.resolve("one.one.one.one")
|
|
25
25
|
assert "1.1.1.1" in result
|
|
26
|
-
assert
|
|
26
|
+
assert "2606:4700:4700::1111" not in result
|
|
27
27
|
|
|
28
28
|
results = [_ async for _ in scan.helpers.resolve_batch(("one.one.one.one", "1.1.1.1"))]
|
|
29
29
|
pass_1 = False
|
|
@@ -38,12 +38,14 @@ async def test_dns_engine(bbot_scanner):
|
|
|
38
38
|
results = [_ async for _ in scan.helpers.resolve_raw_batch((("one.one.one.one", "A"), ("1.1.1.1", "PTR")))]
|
|
39
39
|
pass_1 = False
|
|
40
40
|
pass_2 = False
|
|
41
|
-
for (query, rdtype), (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
for (query, rdtype), (answers, errors) in results:
|
|
42
|
+
results = []
|
|
43
|
+
for answer in answers:
|
|
44
|
+
for t in extract_targets(answer):
|
|
45
|
+
results.append(t[1])
|
|
46
|
+
if query == "one.one.one.one" and "1.1.1.1" in results:
|
|
45
47
|
pass_1 = True
|
|
46
|
-
elif query == "1.1.1.1" and "one.one.one.one" in
|
|
48
|
+
elif query == "1.1.1.1" and "one.one.one.one" in results:
|
|
47
49
|
pass_2 = True
|
|
48
50
|
assert pass_1 and pass_2
|
|
49
51
|
|
|
@@ -83,12 +85,12 @@ async def test_dns_resolution(bbot_scanner):
|
|
|
83
85
|
for answer in answers:
|
|
84
86
|
responses += list(extract_targets(answer))
|
|
85
87
|
assert ("A", "1.1.1.1") in responses
|
|
86
|
-
assert
|
|
88
|
+
assert ("AAAA", "2606:4700:4700::1111") not in responses
|
|
87
89
|
answers, errors = await dnsengine.resolve_raw("one.one.one.one", type="AAAA")
|
|
88
90
|
responses = []
|
|
89
91
|
for answer in answers:
|
|
90
92
|
responses += list(extract_targets(answer))
|
|
91
|
-
assert
|
|
93
|
+
assert ("A", "1.1.1.1") not in responses
|
|
92
94
|
assert ("AAAA", "2606:4700:4700::1111") in responses
|
|
93
95
|
answers, errors = await dnsengine.resolve_raw("1.1.1.1")
|
|
94
96
|
responses = []
|
|
@@ -104,42 +106,46 @@ async def test_dns_resolution(bbot_scanner):
|
|
|
104
106
|
assert "2606:4700:4700::1111" in await dnsengine.resolve("one.one.one.one", type="AAAA")
|
|
105
107
|
assert "one.one.one.one" in await dnsengine.resolve("1.1.1.1")
|
|
106
108
|
for rdtype in ("NS", "SOA", "MX", "TXT"):
|
|
107
|
-
|
|
109
|
+
results = await dnsengine.resolve("google.com", type=rdtype)
|
|
110
|
+
assert len(results) > 0
|
|
108
111
|
|
|
109
112
|
# batch resolution
|
|
110
113
|
batch_results = [r async for r in dnsengine.resolve_batch(["1.1.1.1", "one.one.one.one"])]
|
|
111
114
|
assert len(batch_results) == 2
|
|
112
115
|
batch_results = dict(batch_results)
|
|
113
|
-
assert any(
|
|
116
|
+
assert any(x in batch_results["one.one.one.one"] for x in ("1.1.1.1", "1.0.0.1"))
|
|
114
117
|
assert "one.one.one.one" in batch_results["1.1.1.1"]
|
|
115
118
|
|
|
116
119
|
# custom batch resolution
|
|
117
120
|
batch_results = [r async for r in dnsengine.resolve_raw_batch([("1.1.1.1", "PTR"), ("one.one.one.one", "A")])]
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
batch_results_new = []
|
|
122
|
+
for query, (answers, errors) in batch_results:
|
|
123
|
+
for answer in answers:
|
|
124
|
+
batch_results_new.append((answer.to_text(), answer.rdtype.name))
|
|
125
|
+
assert len(batch_results_new) == 3
|
|
126
|
+
assert any(answer == "1.0.0.1" and rdtype == "A" for answer, rdtype in batch_results_new)
|
|
127
|
+
assert any(answer == "one.one.one.one." and rdtype == "PTR" for answer, rdtype in batch_results_new)
|
|
122
128
|
|
|
123
129
|
# dns cache
|
|
124
130
|
dnsengine._dns_cache.clear()
|
|
125
|
-
assert hash(
|
|
126
|
-
assert hash(
|
|
127
|
-
assert hash(
|
|
131
|
+
assert hash(("1.1.1.1", "PTR")) not in dnsengine._dns_cache
|
|
132
|
+
assert hash(("one.one.one.one", "A")) not in dnsengine._dns_cache
|
|
133
|
+
assert hash(("one.one.one.one", "AAAA")) not in dnsengine._dns_cache
|
|
128
134
|
await dnsengine.resolve("1.1.1.1", use_cache=False)
|
|
129
135
|
await dnsengine.resolve("one.one.one.one", use_cache=False)
|
|
130
|
-
assert hash(
|
|
131
|
-
assert hash(
|
|
132
|
-
assert hash(
|
|
136
|
+
assert hash(("1.1.1.1", "PTR")) not in dnsengine._dns_cache
|
|
137
|
+
assert hash(("one.one.one.one", "A")) not in dnsengine._dns_cache
|
|
138
|
+
assert hash(("one.one.one.one", "AAAA")) not in dnsengine._dns_cache
|
|
133
139
|
|
|
134
140
|
await dnsengine.resolve("1.1.1.1")
|
|
135
|
-
assert hash(
|
|
141
|
+
assert hash(("1.1.1.1", "PTR")) in dnsengine._dns_cache
|
|
136
142
|
await dnsengine.resolve("one.one.one.one", type="A")
|
|
137
|
-
assert hash(
|
|
138
|
-
assert
|
|
143
|
+
assert hash(("one.one.one.one", "A")) in dnsengine._dns_cache
|
|
144
|
+
assert hash(("one.one.one.one", "AAAA")) not in dnsengine._dns_cache
|
|
139
145
|
dnsengine._dns_cache.clear()
|
|
140
146
|
await dnsengine.resolve("one.one.one.one", type="AAAA")
|
|
141
|
-
assert hash(
|
|
142
|
-
assert
|
|
147
|
+
assert hash(("one.one.one.one", "AAAA")) in dnsengine._dns_cache
|
|
148
|
+
assert hash(("one.one.one.one", "A")) not in dnsengine._dns_cache
|
|
143
149
|
|
|
144
150
|
await dnsengine._shutdown()
|
|
145
151
|
|
|
@@ -148,11 +154,7 @@ async def test_dns_resolution(bbot_scanner):
|
|
|
148
154
|
resolved_hosts_event1 = scan.make_event("one.one.one.one", "DNS_NAME", parent=scan.root_event)
|
|
149
155
|
resolved_hosts_event2 = scan.make_event("http://one.one.one.one/", "URL_UNVERIFIED", parent=scan.root_event)
|
|
150
156
|
dnsresolve = scan.modules["dnsresolve"]
|
|
151
|
-
assert hash(resolved_hosts_event1.host) not in dnsresolve._event_cache
|
|
152
|
-
assert hash(resolved_hosts_event2.host) not in dnsresolve._event_cache
|
|
153
157
|
await dnsresolve.handle_event(resolved_hosts_event1)
|
|
154
|
-
assert hash(resolved_hosts_event1.host) in dnsresolve._event_cache
|
|
155
|
-
assert hash(resolved_hosts_event2.host) in dnsresolve._event_cache
|
|
156
158
|
await dnsresolve.handle_event(resolved_hosts_event2)
|
|
157
159
|
assert "1.1.1.1" in resolved_hosts_event2.resolved_hosts
|
|
158
160
|
# URL event should not have dns_children
|
|
@@ -160,8 +162,10 @@ async def test_dns_resolution(bbot_scanner):
|
|
|
160
162
|
assert resolved_hosts_event1.resolved_hosts == resolved_hosts_event2.resolved_hosts
|
|
161
163
|
# DNS_NAME event should have dns_children
|
|
162
164
|
assert "1.1.1.1" in resolved_hosts_event1.dns_children["A"]
|
|
165
|
+
assert "A" in resolved_hosts_event1.raw_dns_records
|
|
166
|
+
assert "AAAA" in resolved_hosts_event1.raw_dns_records
|
|
163
167
|
assert "a-record" in resolved_hosts_event1.tags
|
|
164
|
-
assert
|
|
168
|
+
assert "a-record" not in resolved_hosts_event2.tags
|
|
165
169
|
|
|
166
170
|
scan2 = bbot_scanner("evilcorp.com", config={"dns": {"minimal": False}})
|
|
167
171
|
await scan2.helpers.dns._mock_dns(
|
|
@@ -181,40 +185,316 @@ async def test_dns_resolution(bbot_scanner):
|
|
|
181
185
|
|
|
182
186
|
@pytest.mark.asyncio
|
|
183
187
|
async def test_wildcards(bbot_scanner):
|
|
188
|
+
|
|
184
189
|
scan = bbot_scanner("1.1.1.1")
|
|
185
190
|
helpers = scan.helpers
|
|
186
191
|
|
|
187
|
-
from bbot.core.helpers.dns.engine import DNSEngine
|
|
192
|
+
from bbot.core.helpers.dns.engine import DNSEngine, all_rdtypes
|
|
188
193
|
|
|
189
|
-
dnsengine = DNSEngine(None)
|
|
194
|
+
dnsengine = DNSEngine(None, debug=True)
|
|
190
195
|
|
|
191
|
-
#
|
|
192
|
-
wildcard_domains = await dnsengine.is_wildcard_domain("asdf.github.io")
|
|
193
|
-
assert
|
|
194
|
-
|
|
196
|
+
# is_wildcard_domain
|
|
197
|
+
wildcard_domains = await dnsengine.is_wildcard_domain("asdf.github.io", all_rdtypes)
|
|
198
|
+
assert len(dnsengine._wildcard_cache) == len(all_rdtypes) + (len(all_rdtypes) - 2)
|
|
199
|
+
for rdtype in all_rdtypes:
|
|
200
|
+
assert hash(("github.io", rdtype)) in dnsengine._wildcard_cache
|
|
201
|
+
if rdtype not in ("A", "AAAA"):
|
|
202
|
+
assert hash(("asdf.github.io", rdtype)) in dnsengine._wildcard_cache
|
|
195
203
|
assert "github.io" in wildcard_domains
|
|
196
204
|
assert "A" in wildcard_domains["github.io"]
|
|
197
205
|
assert "SRV" not in wildcard_domains["github.io"]
|
|
198
|
-
assert wildcard_domains["github.io"]["A"] and all(helpers.is_ip(r) for r in wildcard_domains["github.io"]["A"])
|
|
206
|
+
assert wildcard_domains["github.io"]["A"] and all(helpers.is_ip(r) for r in wildcard_domains["github.io"]["A"][0])
|
|
199
207
|
dnsengine._wildcard_cache.clear()
|
|
200
208
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
209
|
+
# is_wildcard
|
|
210
|
+
for test_domain in ("blacklanternsecurity.github.io", "asdf.asdf.asdf.github.io"):
|
|
211
|
+
wildcard_rdtypes = await dnsengine.is_wildcard(test_domain, all_rdtypes)
|
|
212
|
+
assert "A" in wildcard_rdtypes
|
|
213
|
+
assert "SRV" not in wildcard_rdtypes
|
|
214
|
+
assert wildcard_rdtypes["A"] == (True, "github.io")
|
|
215
|
+
assert wildcard_rdtypes["AAAA"] == (True, "github.io")
|
|
216
|
+
assert len(dnsengine._wildcard_cache) == 2
|
|
217
|
+
for rdtype in ("A", "AAAA"):
|
|
218
|
+
assert hash(("github.io", rdtype)) in dnsengine._wildcard_cache
|
|
219
|
+
assert len(dnsengine._wildcard_cache[hash(("github.io", rdtype))]) == 2
|
|
220
|
+
assert len(dnsengine._wildcard_cache[hash(("github.io", rdtype))][0]) > 0
|
|
221
|
+
assert len(dnsengine._wildcard_cache[hash(("github.io", rdtype))][1]) > 0
|
|
222
|
+
dnsengine._wildcard_cache.clear()
|
|
223
|
+
|
|
224
|
+
### wildcard TXT record ###
|
|
225
|
+
|
|
226
|
+
custom_lookup = """
|
|
227
|
+
def custom_lookup(query, rdtype):
|
|
228
|
+
if rdtype == "TXT" and query.strip(".").endswith("test.evilcorp.com"):
|
|
229
|
+
return {""}
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
mock_data = {
|
|
233
|
+
"evilcorp.com": {"A": ["127.0.0.1"]},
|
|
234
|
+
"test.evilcorp.com": {"A": ["127.0.0.2"]},
|
|
235
|
+
"www.test.evilcorp.com": {"AAAA": ["dead::beef"]},
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
# basic sanity checks
|
|
239
|
+
|
|
240
|
+
await dnsengine._mock_dns(mock_data, custom_lookup_fn=custom_lookup)
|
|
241
|
+
|
|
242
|
+
a_result = await dnsengine.resolve("evilcorp.com")
|
|
243
|
+
assert a_result == {"127.0.0.1"}
|
|
244
|
+
aaaa_result = await dnsengine.resolve("www.test.evilcorp.com", type="AAAA")
|
|
245
|
+
assert aaaa_result == {"dead::beef"}
|
|
246
|
+
txt_result = await dnsengine.resolve("asdf.www.test.evilcorp.com", type="TXT")
|
|
247
|
+
assert txt_result == set()
|
|
248
|
+
txt_result_raw, errors = await dnsengine.resolve_raw("asdf.www.test.evilcorp.com", type="TXT")
|
|
249
|
+
txt_result_raw = list(txt_result_raw)
|
|
250
|
+
assert txt_result_raw
|
|
251
|
+
|
|
252
|
+
await dnsengine._shutdown()
|
|
253
|
+
|
|
254
|
+
# first, we check with wildcard detection disabled
|
|
255
|
+
|
|
256
|
+
scan = bbot_scanner(
|
|
257
|
+
"bbot.fdsa.www.test.evilcorp.com",
|
|
258
|
+
whitelist=["evilcorp.com"],
|
|
259
|
+
config={
|
|
260
|
+
"dns": {"minimal": False, "disable": False, "search_distance": 5, "wildcard_ignore": ["evilcorp.com"]},
|
|
261
|
+
"speculate": True,
|
|
262
|
+
},
|
|
263
|
+
)
|
|
264
|
+
await scan.helpers.dns._mock_dns(mock_data, custom_lookup_fn=custom_lookup)
|
|
265
|
+
|
|
266
|
+
events = [e async for e in scan.async_start()]
|
|
267
|
+
assert len(events) == 12
|
|
268
|
+
assert len([e for e in events if e.type == "DNS_NAME"]) == 5
|
|
269
|
+
assert len([e for e in events if e.type == "RAW_DNS_RECORD"]) == 4
|
|
270
|
+
assert sorted([e.data for e in events if e.type == "DNS_NAME"]) == [
|
|
271
|
+
"bbot.fdsa.www.test.evilcorp.com",
|
|
272
|
+
"evilcorp.com",
|
|
273
|
+
"fdsa.www.test.evilcorp.com",
|
|
274
|
+
"test.evilcorp.com",
|
|
275
|
+
"www.test.evilcorp.com",
|
|
276
|
+
]
|
|
277
|
+
|
|
278
|
+
dns_names_by_host = {e.host: e for e in events if e.type == "DNS_NAME"}
|
|
279
|
+
assert dns_names_by_host["evilcorp.com"].tags == {"domain", "private-ip", "in-scope", "a-record"}
|
|
280
|
+
assert dns_names_by_host["evilcorp.com"].resolved_hosts == {"127.0.0.1"}
|
|
281
|
+
assert dns_names_by_host["test.evilcorp.com"].tags == {
|
|
282
|
+
"subdomain",
|
|
283
|
+
"private-ip",
|
|
284
|
+
"in-scope",
|
|
285
|
+
"a-record",
|
|
286
|
+
"txt-record",
|
|
287
|
+
}
|
|
288
|
+
assert dns_names_by_host["test.evilcorp.com"].resolved_hosts == {"127.0.0.2"}
|
|
289
|
+
assert dns_names_by_host["www.test.evilcorp.com"].tags == {"subdomain", "in-scope", "aaaa-record", "txt-record"}
|
|
290
|
+
assert dns_names_by_host["www.test.evilcorp.com"].resolved_hosts == {"dead::beef"}
|
|
291
|
+
assert dns_names_by_host["fdsa.www.test.evilcorp.com"].tags == {"subdomain", "in-scope", "txt-record"}
|
|
292
|
+
assert dns_names_by_host["fdsa.www.test.evilcorp.com"].resolved_hosts == set()
|
|
293
|
+
assert dns_names_by_host["bbot.fdsa.www.test.evilcorp.com"].tags == {
|
|
294
|
+
"target",
|
|
295
|
+
"subdomain",
|
|
296
|
+
"in-scope",
|
|
297
|
+
"txt-record",
|
|
298
|
+
}
|
|
299
|
+
assert dns_names_by_host["bbot.fdsa.www.test.evilcorp.com"].resolved_hosts == set()
|
|
300
|
+
|
|
301
|
+
raw_records_by_host = {e.host: e for e in events if e.type == "RAW_DNS_RECORD"}
|
|
302
|
+
assert raw_records_by_host["test.evilcorp.com"].tags == {"subdomain", "in-scope", "txt-record"}
|
|
303
|
+
assert raw_records_by_host["test.evilcorp.com"].resolved_hosts == {"127.0.0.2"}
|
|
304
|
+
assert raw_records_by_host["www.test.evilcorp.com"].tags == {"subdomain", "in-scope", "txt-record"}
|
|
305
|
+
assert raw_records_by_host["www.test.evilcorp.com"].resolved_hosts == {"dead::beef"}
|
|
306
|
+
assert raw_records_by_host["fdsa.www.test.evilcorp.com"].tags == {"subdomain", "in-scope", "txt-record"}
|
|
307
|
+
assert raw_records_by_host["fdsa.www.test.evilcorp.com"].resolved_hosts == set()
|
|
308
|
+
assert raw_records_by_host["bbot.fdsa.www.test.evilcorp.com"].tags == {"subdomain", "in-scope", "txt-record"}
|
|
309
|
+
assert raw_records_by_host["bbot.fdsa.www.test.evilcorp.com"].resolved_hosts == set()
|
|
310
|
+
|
|
311
|
+
# then we run it again with wildcard detection enabled
|
|
312
|
+
|
|
313
|
+
scan = bbot_scanner(
|
|
314
|
+
"bbot.fdsa.www.test.evilcorp.com",
|
|
315
|
+
whitelist=["evilcorp.com"],
|
|
316
|
+
config={
|
|
317
|
+
"dns": {"minimal": False, "disable": False, "search_distance": 5, "wildcard_ignore": []},
|
|
318
|
+
"speculate": True,
|
|
319
|
+
},
|
|
320
|
+
)
|
|
321
|
+
await scan.helpers.dns._mock_dns(mock_data, custom_lookup_fn=custom_lookup)
|
|
322
|
+
|
|
323
|
+
events = [e async for e in scan.async_start()]
|
|
324
|
+
assert len(events) == 12
|
|
325
|
+
assert len([e for e in events if e.type == "DNS_NAME"]) == 5
|
|
326
|
+
assert len([e for e in events if e.type == "RAW_DNS_RECORD"]) == 4
|
|
327
|
+
assert sorted([e.data for e in events if e.type == "DNS_NAME"]) == [
|
|
328
|
+
"_wildcard.test.evilcorp.com",
|
|
329
|
+
"bbot.fdsa.www.test.evilcorp.com",
|
|
330
|
+
"evilcorp.com",
|
|
331
|
+
"test.evilcorp.com",
|
|
332
|
+
"www.test.evilcorp.com",
|
|
333
|
+
]
|
|
334
|
+
|
|
335
|
+
dns_names_by_host = {e.host: e for e in events if e.type == "DNS_NAME"}
|
|
336
|
+
assert dns_names_by_host["evilcorp.com"].tags == {"domain", "private-ip", "in-scope", "a-record"}
|
|
337
|
+
assert dns_names_by_host["evilcorp.com"].resolved_hosts == {"127.0.0.1"}
|
|
338
|
+
assert dns_names_by_host["test.evilcorp.com"].tags == {
|
|
339
|
+
"subdomain",
|
|
340
|
+
"private-ip",
|
|
341
|
+
"in-scope",
|
|
342
|
+
"a-record",
|
|
343
|
+
"txt-record",
|
|
344
|
+
}
|
|
345
|
+
assert dns_names_by_host["test.evilcorp.com"].resolved_hosts == {"127.0.0.2"}
|
|
346
|
+
assert dns_names_by_host["_wildcard.test.evilcorp.com"].tags == {
|
|
347
|
+
"subdomain",
|
|
348
|
+
"in-scope",
|
|
349
|
+
"txt-record",
|
|
350
|
+
"txt-wildcard",
|
|
351
|
+
"wildcard",
|
|
352
|
+
}
|
|
353
|
+
assert dns_names_by_host["_wildcard.test.evilcorp.com"].resolved_hosts == set()
|
|
354
|
+
assert dns_names_by_host["www.test.evilcorp.com"].tags == {
|
|
355
|
+
"subdomain",
|
|
356
|
+
"in-scope",
|
|
357
|
+
"aaaa-record",
|
|
358
|
+
"txt-record",
|
|
359
|
+
"txt-wildcard",
|
|
360
|
+
"wildcard",
|
|
361
|
+
}
|
|
362
|
+
assert dns_names_by_host["www.test.evilcorp.com"].resolved_hosts == {"dead::beef"}
|
|
363
|
+
assert dns_names_by_host["bbot.fdsa.www.test.evilcorp.com"].tags == {
|
|
364
|
+
"target",
|
|
365
|
+
"subdomain",
|
|
366
|
+
"in-scope",
|
|
367
|
+
"txt-record",
|
|
368
|
+
"txt-wildcard",
|
|
369
|
+
"wildcard",
|
|
370
|
+
}
|
|
371
|
+
assert dns_names_by_host["bbot.fdsa.www.test.evilcorp.com"].resolved_hosts == set()
|
|
372
|
+
|
|
373
|
+
raw_records_by_host = {e.host: e for e in events if e.type == "RAW_DNS_RECORD"}
|
|
374
|
+
assert raw_records_by_host["test.evilcorp.com"].tags == {"subdomain", "in-scope", "txt-record"}
|
|
375
|
+
assert raw_records_by_host["test.evilcorp.com"].resolved_hosts == {"127.0.0.2"}
|
|
376
|
+
assert raw_records_by_host["www.test.evilcorp.com"].tags == {"subdomain", "in-scope", "txt-record", "txt-wildcard"}
|
|
377
|
+
assert raw_records_by_host["www.test.evilcorp.com"].resolved_hosts == {"dead::beef"}
|
|
378
|
+
assert raw_records_by_host["_wildcard.test.evilcorp.com"].tags == {
|
|
379
|
+
"subdomain",
|
|
380
|
+
"in-scope",
|
|
381
|
+
"txt-record",
|
|
382
|
+
"txt-wildcard",
|
|
383
|
+
}
|
|
384
|
+
assert raw_records_by_host["_wildcard.test.evilcorp.com"].resolved_hosts == set()
|
|
385
|
+
assert raw_records_by_host["bbot.fdsa.www.test.evilcorp.com"].tags == {
|
|
386
|
+
"subdomain",
|
|
387
|
+
"in-scope",
|
|
388
|
+
"txt-record",
|
|
389
|
+
"txt-wildcard",
|
|
390
|
+
}
|
|
391
|
+
assert raw_records_by_host["bbot.fdsa.www.test.evilcorp.com"].resolved_hosts == set()
|
|
392
|
+
|
|
393
|
+
### runaway SRV wildcard ###
|
|
394
|
+
|
|
395
|
+
custom_lookup = """
|
|
396
|
+
def custom_lookup(query, rdtype):
|
|
397
|
+
if rdtype == "SRV" and query.strip(".").endswith("evilcorp.com"):
|
|
398
|
+
return {f"0 100 389 test.{query}"}
|
|
399
|
+
"""
|
|
400
|
+
|
|
401
|
+
mock_data = {
|
|
402
|
+
"evilcorp.com": {"A": ["127.0.0.1"]},
|
|
403
|
+
"test.evilcorp.com": {"AAAA": ["dead::beef"]},
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
scan = bbot_scanner(
|
|
407
|
+
"evilcorp.com",
|
|
408
|
+
config={
|
|
409
|
+
"dns": {
|
|
410
|
+
"minimal": False,
|
|
411
|
+
"disable": False,
|
|
412
|
+
"search_distance": 5,
|
|
413
|
+
"wildcard_ignore": [],
|
|
414
|
+
"runaway_limit": 3,
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
)
|
|
418
|
+
await scan.helpers.dns._mock_dns(mock_data, custom_lookup_fn=custom_lookup)
|
|
419
|
+
|
|
420
|
+
events = [e async for e in scan.async_start()]
|
|
421
|
+
|
|
422
|
+
assert len(events) == 11
|
|
423
|
+
assert len([e for e in events if e.type == "DNS_NAME"]) == 5
|
|
424
|
+
assert len([e for e in events if e.type == "RAW_DNS_RECORD"]) == 4
|
|
425
|
+
assert sorted([e.data for e in events if e.type == "DNS_NAME"]) == [
|
|
426
|
+
"evilcorp.com",
|
|
427
|
+
"test.evilcorp.com",
|
|
428
|
+
"test.test.evilcorp.com",
|
|
429
|
+
"test.test.test.evilcorp.com",
|
|
430
|
+
"test.test.test.test.evilcorp.com",
|
|
431
|
+
]
|
|
208
432
|
|
|
209
|
-
|
|
210
|
-
assert "
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
433
|
+
dns_names_by_host = {e.host: e for e in events if e.type == "DNS_NAME"}
|
|
434
|
+
assert dns_names_by_host["evilcorp.com"].tags == {
|
|
435
|
+
"target",
|
|
436
|
+
"a-record",
|
|
437
|
+
"in-scope",
|
|
438
|
+
"domain",
|
|
439
|
+
"srv-record",
|
|
440
|
+
"private-ip",
|
|
441
|
+
}
|
|
442
|
+
assert dns_names_by_host["test.evilcorp.com"].tags == {
|
|
443
|
+
"in-scope",
|
|
444
|
+
"srv-record",
|
|
445
|
+
"aaaa-record",
|
|
446
|
+
"srv-wildcard-possible",
|
|
447
|
+
"wildcard-possible",
|
|
448
|
+
"subdomain",
|
|
449
|
+
}
|
|
450
|
+
assert dns_names_by_host["test.test.evilcorp.com"].tags == {
|
|
451
|
+
"in-scope",
|
|
452
|
+
"srv-record",
|
|
453
|
+
"srv-wildcard-possible",
|
|
454
|
+
"wildcard-possible",
|
|
455
|
+
"subdomain",
|
|
456
|
+
}
|
|
457
|
+
assert dns_names_by_host["test.test.test.evilcorp.com"].tags == {
|
|
458
|
+
"in-scope",
|
|
459
|
+
"srv-record",
|
|
460
|
+
"srv-wildcard-possible",
|
|
461
|
+
"wildcard-possible",
|
|
462
|
+
"subdomain",
|
|
463
|
+
}
|
|
464
|
+
assert dns_names_by_host["test.test.test.test.evilcorp.com"].tags == {
|
|
465
|
+
"in-scope",
|
|
466
|
+
"srv-record",
|
|
467
|
+
"srv-wildcard-possible",
|
|
468
|
+
"wildcard-possible",
|
|
469
|
+
"subdomain",
|
|
470
|
+
"runaway-dns-3",
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
raw_records_by_host = {e.host: e for e in events if e.type == "RAW_DNS_RECORD"}
|
|
474
|
+
assert raw_records_by_host["evilcorp.com"].tags == {"in-scope", "srv-record", "domain"}
|
|
475
|
+
assert raw_records_by_host["test.evilcorp.com"].tags == {
|
|
476
|
+
"in-scope",
|
|
477
|
+
"srv-record",
|
|
478
|
+
"srv-wildcard-possible",
|
|
479
|
+
"subdomain",
|
|
480
|
+
}
|
|
481
|
+
assert raw_records_by_host["test.test.evilcorp.com"].tags == {
|
|
482
|
+
"in-scope",
|
|
483
|
+
"srv-record",
|
|
484
|
+
"srv-wildcard-possible",
|
|
485
|
+
"subdomain",
|
|
486
|
+
}
|
|
487
|
+
assert raw_records_by_host["test.test.test.evilcorp.com"].tags == {
|
|
488
|
+
"in-scope",
|
|
489
|
+
"srv-record",
|
|
490
|
+
"srv-wildcard-possible",
|
|
491
|
+
"subdomain",
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
scan = bbot_scanner("1.1.1.1")
|
|
495
|
+
helpers = scan.helpers
|
|
496
|
+
|
|
497
|
+
# event resolution
|
|
218
498
|
wildcard_event1 = scan.make_event("wat.asdf.fdsa.github.io", "DNS_NAME", parent=scan.root_event)
|
|
219
499
|
wildcard_event1.scope_distance = 0
|
|
220
500
|
wildcard_event2 = scan.make_event("wats.asd.fdsa.github.io", "DNS_NAME", parent=scan.root_event)
|
|
@@ -222,9 +502,6 @@ async def test_wildcards(bbot_scanner):
|
|
|
222
502
|
wildcard_event3 = scan.make_event("github.io", "DNS_NAME", parent=scan.root_event)
|
|
223
503
|
wildcard_event3.scope_distance = 0
|
|
224
504
|
|
|
225
|
-
await dnsengine._shutdown()
|
|
226
|
-
|
|
227
|
-
# event resolution
|
|
228
505
|
await scan._prep()
|
|
229
506
|
dnsresolve = scan.modules["dnsresolve"]
|
|
230
507
|
await dnsresolve.handle_event(wildcard_event1)
|
|
@@ -270,8 +547,8 @@ async def test_wildcards(bbot_scanner):
|
|
|
270
547
|
)
|
|
271
548
|
await scan2.ingress_module.queue_event(other_event, {})
|
|
272
549
|
events = [e async for e in scan2.async_start()]
|
|
273
|
-
assert len(events) ==
|
|
274
|
-
assert
|
|
550
|
+
assert len(events) == 4
|
|
551
|
+
assert 2 == len([e for e in events if e.type == "SCAN"])
|
|
275
552
|
unmodified_wildcard_events = [
|
|
276
553
|
e for e in events if e.type == "DNS_NAME" and e.data == "asdfl.gashdgkjsadgsdf.github.io"
|
|
277
554
|
]
|
|
@@ -316,8 +593,8 @@ async def test_wildcards(bbot_scanner):
|
|
|
316
593
|
)
|
|
317
594
|
await scan2.ingress_module.queue_event(other_event, {})
|
|
318
595
|
events = [e async for e in scan2.async_start()]
|
|
319
|
-
assert len(events) ==
|
|
320
|
-
assert
|
|
596
|
+
assert len(events) == 4
|
|
597
|
+
assert 2 == len([e for e in events if e.type == "SCAN"])
|
|
321
598
|
unmodified_wildcard_events = [e for e in events if e.type == "DNS_NAME" and "_wildcard" not in e.data]
|
|
322
599
|
assert len(unmodified_wildcard_events) == 2
|
|
323
600
|
assert 1 == len(
|
|
@@ -355,6 +632,42 @@ async def test_wildcards(bbot_scanner):
|
|
|
355
632
|
assert len(modified_wildcard_events) == 0
|
|
356
633
|
|
|
357
634
|
|
|
635
|
+
@pytest.mark.asyncio
|
|
636
|
+
async def test_wildcard_deduplication(bbot_scanner):
|
|
637
|
+
|
|
638
|
+
custom_lookup = """
|
|
639
|
+
def custom_lookup(query, rdtype):
|
|
640
|
+
if rdtype == "TXT" and query.strip(".").endswith("evilcorp.com"):
|
|
641
|
+
return {""}
|
|
642
|
+
"""
|
|
643
|
+
|
|
644
|
+
mock_data = {
|
|
645
|
+
"evilcorp.com": {"A": ["127.0.0.1"]},
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
from bbot.modules.base import BaseModule
|
|
649
|
+
|
|
650
|
+
class DummyModule(BaseModule):
|
|
651
|
+
watched_events = ["DNS_NAME"]
|
|
652
|
+
per_domain_only = True
|
|
653
|
+
|
|
654
|
+
async def handle_event(self, event):
|
|
655
|
+
for i in range(30):
|
|
656
|
+
await self.emit_event(f"www{i}.evilcorp.com", "DNS_NAME", parent=event)
|
|
657
|
+
|
|
658
|
+
# scan without omitted event type
|
|
659
|
+
scan = bbot_scanner(
|
|
660
|
+
"evilcorp.com", config={"dns": {"minimal": False, "wildcard_ignore": []}, "omit_event_types": []}
|
|
661
|
+
)
|
|
662
|
+
await scan.helpers.dns._mock_dns(mock_data, custom_lookup_fn=custom_lookup)
|
|
663
|
+
dummy_module = DummyModule(scan)
|
|
664
|
+
scan.modules["dummy_module"] = dummy_module
|
|
665
|
+
events = [e async for e in scan.async_start()]
|
|
666
|
+
dns_name_events = [e for e in events if e.type == "DNS_NAME"]
|
|
667
|
+
assert len(dns_name_events) == 2
|
|
668
|
+
assert 1 == len([e for e in dns_name_events if e.data == "_wildcard.evilcorp.com"])
|
|
669
|
+
|
|
670
|
+
|
|
358
671
|
@pytest.mark.asyncio
|
|
359
672
|
async def test_dns_raw_records(bbot_scanner):
|
|
360
673
|
|
|
@@ -420,7 +733,7 @@ async def test_dns_raw_records(bbot_scanner):
|
|
|
420
733
|
dummy_module = DummyModule(scan)
|
|
421
734
|
scan.modules["dummy_module"] = dummy_module
|
|
422
735
|
events = [e async for e in scan.async_start()]
|
|
423
|
-
# no raw records should be
|
|
736
|
+
# no raw records should be output
|
|
424
737
|
assert 0 == len([e for e in events if e.type == "RAW_DNS_RECORD"])
|
|
425
738
|
# but they should still make it to the module
|
|
426
739
|
assert 1 == len(
|
|
@@ -453,7 +766,7 @@ async def test_dns_graph_structure(bbot_scanner):
|
|
|
453
766
|
}
|
|
454
767
|
)
|
|
455
768
|
events = [e async for e in scan.async_start()]
|
|
456
|
-
assert len(events) ==
|
|
769
|
+
assert len(events) == 6
|
|
457
770
|
non_scan_events = [e for e in events if e.type != "SCAN"]
|
|
458
771
|
assert sorted([e.type for e in non_scan_events]) == ["DNS_NAME", "DNS_NAME", "DNS_NAME", "URL_UNVERIFIED"]
|
|
459
772
|
events_by_data = {e.data: e for e in non_scan_events}
|
|
@@ -466,14 +779,23 @@ async def test_dns_graph_structure(bbot_scanner):
|
|
|
466
779
|
assert str(events_by_data["evilcorp.com"].module) == "host"
|
|
467
780
|
|
|
468
781
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
assert service_record("
|
|
472
|
-
assert service_record("
|
|
473
|
-
assert service_record("www.example.com"
|
|
474
|
-
assert service_record("
|
|
475
|
-
assert service_record("_custom._service.example.com", "
|
|
782
|
+
@pytest.mark.asyncio
|
|
783
|
+
async def test_dns_helpers(bbot_scanner):
|
|
784
|
+
assert service_record("") is False
|
|
785
|
+
assert service_record("localhost") is False
|
|
786
|
+
assert service_record("www.example.com") is False
|
|
787
|
+
assert service_record("www.example.com", "SRV") is True
|
|
788
|
+
assert service_record("_custom._service.example.com", "SRV") is True
|
|
789
|
+
assert service_record("_custom._service.example.com", "A") is False
|
|
476
790
|
# top 100 most common SRV records
|
|
477
791
|
for srv_record in common_srvs[:100]:
|
|
478
792
|
hostname = f"{srv_record}.example.com"
|
|
479
|
-
assert service_record(hostname)
|
|
793
|
+
assert service_record(hostname) is True
|
|
794
|
+
|
|
795
|
+
# make sure system nameservers are excluded from use by DNS brute force
|
|
796
|
+
brute_nameservers = tempwordlist(["1.2.3.4", "8.8.4.4", "4.3.2.1", "8.8.8.8"])
|
|
797
|
+
scan = bbot_scanner(config={"dns": {"brute_nameservers": brute_nameservers}})
|
|
798
|
+
scan.helpers.dns.system_resolvers = ["8.8.8.8", "8.8.4.4"]
|
|
799
|
+
resolver_file = await scan.helpers.dns.brute.resolver_file()
|
|
800
|
+
resolvers = set(scan.helpers.read_file(resolver_file))
|
|
801
|
+
assert resolvers == {"1.2.3.4", "4.3.2.1"}
|
|
@@ -72,7 +72,7 @@ async def test_engine():
|
|
|
72
72
|
|
|
73
73
|
# test async generator
|
|
74
74
|
assert counter == 0
|
|
75
|
-
assert yield_cancelled
|
|
75
|
+
assert yield_cancelled is False
|
|
76
76
|
yield_res = [r async for r in test_engine.yield_stuff(13)]
|
|
77
77
|
assert yield_res == [f"thing{i}" for i in range(13)]
|
|
78
78
|
assert len(yield_res) == 13
|
|
@@ -88,8 +88,8 @@ async def test_engine():
|
|
|
88
88
|
await agen.aclose()
|
|
89
89
|
break
|
|
90
90
|
await asyncio.sleep(5)
|
|
91
|
-
assert yield_cancelled
|
|
92
|
-
assert yield_errored
|
|
91
|
+
assert yield_cancelled is True
|
|
92
|
+
assert yield_errored is False
|
|
93
93
|
assert counter < 15
|
|
94
94
|
|
|
95
95
|
# test async generator with error
|
|
@@ -99,8 +99,8 @@ async def test_engine():
|
|
|
99
99
|
with pytest.raises(BBOTEngineError):
|
|
100
100
|
async for _ in agen:
|
|
101
101
|
pass
|
|
102
|
-
assert yield_cancelled
|
|
103
|
-
assert yield_errored
|
|
102
|
+
assert yield_cancelled is False
|
|
103
|
+
assert yield_errored is True
|
|
104
104
|
|
|
105
105
|
# test return with cancellation
|
|
106
106
|
return_started = False
|
|
@@ -113,10 +113,10 @@ async def test_engine():
|
|
|
113
113
|
with pytest.raises(asyncio.CancelledError):
|
|
114
114
|
await task
|
|
115
115
|
await asyncio.sleep(0.1)
|
|
116
|
-
assert return_started
|
|
117
|
-
assert return_finished
|
|
118
|
-
assert return_cancelled
|
|
119
|
-
assert return_errored
|
|
116
|
+
assert return_started is True
|
|
117
|
+
assert return_finished is False
|
|
118
|
+
assert return_cancelled is True
|
|
119
|
+
assert return_errored is False
|
|
120
120
|
|
|
121
121
|
# test return with late cancellation
|
|
122
122
|
return_started = False
|
|
@@ -128,10 +128,10 @@ async def test_engine():
|
|
|
128
128
|
task.cancel()
|
|
129
129
|
result = await task
|
|
130
130
|
assert result == "thing1"
|
|
131
|
-
assert return_started
|
|
132
|
-
assert return_finished
|
|
133
|
-
assert return_cancelled
|
|
134
|
-
assert return_errored
|
|
131
|
+
assert return_started is True
|
|
132
|
+
assert return_finished is True
|
|
133
|
+
assert return_cancelled is False
|
|
134
|
+
assert return_errored is False
|
|
135
135
|
|
|
136
136
|
# test return with error
|
|
137
137
|
return_started = False
|
|
@@ -140,9 +140,9 @@ async def test_engine():
|
|
|
140
140
|
return_errored = False
|
|
141
141
|
with pytest.raises(BBOTEngineError):
|
|
142
142
|
result = await test_engine.return_thing(None)
|
|
143
|
-
assert return_started
|
|
144
|
-
assert return_finished
|
|
145
|
-
assert return_cancelled
|
|
146
|
-
assert return_errored
|
|
143
|
+
assert return_started is True
|
|
144
|
+
assert return_finished is False
|
|
145
|
+
assert return_cancelled is False
|
|
146
|
+
assert return_errored is True
|
|
147
147
|
|
|
148
148
|
await test_engine.shutdown()
|