bbot 2.0.1.4720rc0__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 -4
- bbot/core/engine.py +9 -8
- bbot/core/event/base.py +131 -52
- bbot/core/helpers/bloom.py +10 -3
- bbot/core/helpers/command.py +8 -7
- bbot/core/helpers/depsinstaller/installer.py +31 -13
- bbot/core/helpers/diff.py +10 -10
- bbot/core/helpers/dns/brute.py +7 -4
- bbot/core/helpers/dns/dns.py +1 -2
- bbot/core/helpers/dns/engine.py +4 -6
- bbot/core/helpers/dns/helpers.py +2 -2
- bbot/core/helpers/dns/mock.py +0 -1
- 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 +1 -2
- bbot/core/helpers/web/web.py +4 -114
- 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 +11 -9
- 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 +22 -20
- bbot/modules/internal/excavate.py +85 -48
- bbot/modules/internal/speculate.py +41 -32
- 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 -1
- 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 +1 -1
- 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 +51 -16
- bbot/modules/templates/webhook.py +2 -4
- bbot/modules/trickest.py +8 -37
- bbot/modules/trufflehog.py +10 -12
- 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 +172 -62
- bbot/scanner/target.py +236 -434
- bbot/scripts/docs.py +1 -1
- bbot/test/bbot_fixtures.py +13 -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 +62 -25
- bbot/test/test_step_1/test_engine.py +17 -17
- bbot/test/test_step_1/test_events.py +183 -28
- 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 +333 -330
- bbot/test/test_step_1/test_modules_basic.py +68 -70
- 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 +4 -3
- bbot/test/test_step_1/test_target.py +243 -145
- bbot/test/test_step_1/test_web.py +14 -8
- bbot/test/test_step_2/module_tests/base.py +15 -7
- 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 +22 -9
- 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_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 -14
- 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 +2 -2
- {bbot-2.0.1.4720rc0.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.4720rc0.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.4720rc0.dist-info/RECORD +0 -387
- {bbot-2.0.1.4720rc0.dist-info → bbot-2.3.0.5397rc0.dist-info}/LICENSE +0 -0
- {bbot-2.0.1.4720rc0.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
|
|
@@ -85,12 +85,12 @@ async def test_dns_resolution(bbot_scanner):
|
|
|
85
85
|
for answer in answers:
|
|
86
86
|
responses += list(extract_targets(answer))
|
|
87
87
|
assert ("A", "1.1.1.1") in responses
|
|
88
|
-
assert
|
|
88
|
+
assert ("AAAA", "2606:4700:4700::1111") not in responses
|
|
89
89
|
answers, errors = await dnsengine.resolve_raw("one.one.one.one", type="AAAA")
|
|
90
90
|
responses = []
|
|
91
91
|
for answer in answers:
|
|
92
92
|
responses += list(extract_targets(answer))
|
|
93
|
-
assert
|
|
93
|
+
assert ("A", "1.1.1.1") not in responses
|
|
94
94
|
assert ("AAAA", "2606:4700:4700::1111") in responses
|
|
95
95
|
answers, errors = await dnsengine.resolve_raw("1.1.1.1")
|
|
96
96
|
responses = []
|
|
@@ -106,13 +106,14 @@ async def test_dns_resolution(bbot_scanner):
|
|
|
106
106
|
assert "2606:4700:4700::1111" in await dnsengine.resolve("one.one.one.one", type="AAAA")
|
|
107
107
|
assert "one.one.one.one" in await dnsengine.resolve("1.1.1.1")
|
|
108
108
|
for rdtype in ("NS", "SOA", "MX", "TXT"):
|
|
109
|
-
|
|
109
|
+
results = await dnsengine.resolve("google.com", type=rdtype)
|
|
110
|
+
assert len(results) > 0
|
|
110
111
|
|
|
111
112
|
# batch resolution
|
|
112
113
|
batch_results = [r async for r in dnsengine.resolve_batch(["1.1.1.1", "one.one.one.one"])]
|
|
113
114
|
assert len(batch_results) == 2
|
|
114
115
|
batch_results = dict(batch_results)
|
|
115
|
-
assert any(
|
|
116
|
+
assert any(x in batch_results["one.one.one.one"] for x in ("1.1.1.1", "1.0.0.1"))
|
|
116
117
|
assert "one.one.one.one" in batch_results["1.1.1.1"]
|
|
117
118
|
|
|
118
119
|
# custom batch resolution
|
|
@@ -140,11 +141,11 @@ async def test_dns_resolution(bbot_scanner):
|
|
|
140
141
|
assert hash(("1.1.1.1", "PTR")) in dnsengine._dns_cache
|
|
141
142
|
await dnsengine.resolve("one.one.one.one", type="A")
|
|
142
143
|
assert hash(("one.one.one.one", "A")) in dnsengine._dns_cache
|
|
143
|
-
assert
|
|
144
|
+
assert hash(("one.one.one.one", "AAAA")) not in dnsengine._dns_cache
|
|
144
145
|
dnsengine._dns_cache.clear()
|
|
145
146
|
await dnsengine.resolve("one.one.one.one", type="AAAA")
|
|
146
147
|
assert hash(("one.one.one.one", "AAAA")) in dnsengine._dns_cache
|
|
147
|
-
assert
|
|
148
|
+
assert hash(("one.one.one.one", "A")) not in dnsengine._dns_cache
|
|
148
149
|
|
|
149
150
|
await dnsengine._shutdown()
|
|
150
151
|
|
|
@@ -164,7 +165,7 @@ async def test_dns_resolution(bbot_scanner):
|
|
|
164
165
|
assert "A" in resolved_hosts_event1.raw_dns_records
|
|
165
166
|
assert "AAAA" in resolved_hosts_event1.raw_dns_records
|
|
166
167
|
assert "a-record" in resolved_hosts_event1.tags
|
|
167
|
-
assert
|
|
168
|
+
assert "a-record" not in resolved_hosts_event2.tags
|
|
168
169
|
|
|
169
170
|
scan2 = bbot_scanner("evilcorp.com", config={"dns": {"minimal": False}})
|
|
170
171
|
await scan2.helpers.dns._mock_dns(
|
|
@@ -197,7 +198,7 @@ async def test_wildcards(bbot_scanner):
|
|
|
197
198
|
assert len(dnsengine._wildcard_cache) == len(all_rdtypes) + (len(all_rdtypes) - 2)
|
|
198
199
|
for rdtype in all_rdtypes:
|
|
199
200
|
assert hash(("github.io", rdtype)) in dnsengine._wildcard_cache
|
|
200
|
-
if not
|
|
201
|
+
if rdtype not in ("A", "AAAA"):
|
|
201
202
|
assert hash(("asdf.github.io", rdtype)) in dnsengine._wildcard_cache
|
|
202
203
|
assert "github.io" in wildcard_domains
|
|
203
204
|
assert "A" in wildcard_domains["github.io"]
|
|
@@ -263,7 +264,7 @@ def custom_lookup(query, rdtype):
|
|
|
263
264
|
await scan.helpers.dns._mock_dns(mock_data, custom_lookup_fn=custom_lookup)
|
|
264
265
|
|
|
265
266
|
events = [e async for e in scan.async_start()]
|
|
266
|
-
assert len(events) ==
|
|
267
|
+
assert len(events) == 12
|
|
267
268
|
assert len([e for e in events if e.type == "DNS_NAME"]) == 5
|
|
268
269
|
assert len([e for e in events if e.type == "RAW_DNS_RECORD"]) == 4
|
|
269
270
|
assert sorted([e.data for e in events if e.type == "DNS_NAME"]) == [
|
|
@@ -320,7 +321,7 @@ def custom_lookup(query, rdtype):
|
|
|
320
321
|
await scan.helpers.dns._mock_dns(mock_data, custom_lookup_fn=custom_lookup)
|
|
321
322
|
|
|
322
323
|
events = [e async for e in scan.async_start()]
|
|
323
|
-
assert len(events) ==
|
|
324
|
+
assert len(events) == 12
|
|
324
325
|
assert len([e for e in events if e.type == "DNS_NAME"]) == 5
|
|
325
326
|
assert len([e for e in events if e.type == "RAW_DNS_RECORD"]) == 4
|
|
326
327
|
assert sorted([e.data for e in events if e.type == "DNS_NAME"]) == [
|
|
@@ -418,7 +419,7 @@ def custom_lookup(query, rdtype):
|
|
|
418
419
|
|
|
419
420
|
events = [e async for e in scan.async_start()]
|
|
420
421
|
|
|
421
|
-
assert len(events) ==
|
|
422
|
+
assert len(events) == 11
|
|
422
423
|
assert len([e for e in events if e.type == "DNS_NAME"]) == 5
|
|
423
424
|
assert len([e for e in events if e.type == "RAW_DNS_RECORD"]) == 4
|
|
424
425
|
assert sorted([e.data for e in events if e.type == "DNS_NAME"]) == [
|
|
@@ -546,8 +547,8 @@ def custom_lookup(query, rdtype):
|
|
|
546
547
|
)
|
|
547
548
|
await scan2.ingress_module.queue_event(other_event, {})
|
|
548
549
|
events = [e async for e in scan2.async_start()]
|
|
549
|
-
assert len(events) ==
|
|
550
|
-
assert
|
|
550
|
+
assert len(events) == 4
|
|
551
|
+
assert 2 == len([e for e in events if e.type == "SCAN"])
|
|
551
552
|
unmodified_wildcard_events = [
|
|
552
553
|
e for e in events if e.type == "DNS_NAME" and e.data == "asdfl.gashdgkjsadgsdf.github.io"
|
|
553
554
|
]
|
|
@@ -592,8 +593,8 @@ def custom_lookup(query, rdtype):
|
|
|
592
593
|
)
|
|
593
594
|
await scan2.ingress_module.queue_event(other_event, {})
|
|
594
595
|
events = [e async for e in scan2.async_start()]
|
|
595
|
-
assert len(events) ==
|
|
596
|
-
assert
|
|
596
|
+
assert len(events) == 4
|
|
597
|
+
assert 2 == len([e for e in events if e.type == "SCAN"])
|
|
597
598
|
unmodified_wildcard_events = [e for e in events if e.type == "DNS_NAME" and "_wildcard" not in e.data]
|
|
598
599
|
assert len(unmodified_wildcard_events) == 2
|
|
599
600
|
assert 1 == len(
|
|
@@ -631,6 +632,42 @@ def custom_lookup(query, rdtype):
|
|
|
631
632
|
assert len(modified_wildcard_events) == 0
|
|
632
633
|
|
|
633
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
|
+
|
|
634
671
|
@pytest.mark.asyncio
|
|
635
672
|
async def test_dns_raw_records(bbot_scanner):
|
|
636
673
|
|
|
@@ -696,7 +733,7 @@ async def test_dns_raw_records(bbot_scanner):
|
|
|
696
733
|
dummy_module = DummyModule(scan)
|
|
697
734
|
scan.modules["dummy_module"] = dummy_module
|
|
698
735
|
events = [e async for e in scan.async_start()]
|
|
699
|
-
# no raw records should be
|
|
736
|
+
# no raw records should be output
|
|
700
737
|
assert 0 == len([e for e in events if e.type == "RAW_DNS_RECORD"])
|
|
701
738
|
# but they should still make it to the module
|
|
702
739
|
assert 1 == len(
|
|
@@ -729,7 +766,7 @@ async def test_dns_graph_structure(bbot_scanner):
|
|
|
729
766
|
}
|
|
730
767
|
)
|
|
731
768
|
events = [e async for e in scan.async_start()]
|
|
732
|
-
assert len(events) ==
|
|
769
|
+
assert len(events) == 6
|
|
733
770
|
non_scan_events = [e for e in events if e.type != "SCAN"]
|
|
734
771
|
assert sorted([e.type for e in non_scan_events]) == ["DNS_NAME", "DNS_NAME", "DNS_NAME", "URL_UNVERIFIED"]
|
|
735
772
|
events_by_data = {e.data: e for e in non_scan_events}
|
|
@@ -744,16 +781,16 @@ async def test_dns_graph_structure(bbot_scanner):
|
|
|
744
781
|
|
|
745
782
|
@pytest.mark.asyncio
|
|
746
783
|
async def test_dns_helpers(bbot_scanner):
|
|
747
|
-
assert service_record("")
|
|
748
|
-
assert service_record("localhost")
|
|
749
|
-
assert service_record("www.example.com")
|
|
750
|
-
assert service_record("www.example.com", "SRV")
|
|
751
|
-
assert service_record("_custom._service.example.com", "SRV")
|
|
752
|
-
assert service_record("_custom._service.example.com", "A")
|
|
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
|
|
753
790
|
# top 100 most common SRV records
|
|
754
791
|
for srv_record in common_srvs[:100]:
|
|
755
792
|
hostname = f"{srv_record}.example.com"
|
|
756
|
-
assert service_record(hostname)
|
|
793
|
+
assert service_record(hostname) is True
|
|
757
794
|
|
|
758
795
|
# make sure system nameservers are excluded from use by DNS brute force
|
|
759
796
|
brute_nameservers = tempwordlist(["1.2.3.4", "8.8.4.4", "4.3.2.1", "8.8.8.8"])
|
|
@@ -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()
|
|
@@ -4,6 +4,7 @@ import ipaddress
|
|
|
4
4
|
|
|
5
5
|
from ..bbot_fixtures import *
|
|
6
6
|
from bbot.scanner import Scanner
|
|
7
|
+
from bbot.core.helpers.regexes import event_uuid_regex
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
@pytest.mark.asyncio
|
|
@@ -13,10 +14,19 @@ async def test_events(events, helpers):
|
|
|
13
14
|
await scan._prep()
|
|
14
15
|
|
|
15
16
|
assert events.ipv4.type == "IP_ADDRESS"
|
|
17
|
+
assert events.ipv4.netloc == "8.8.8.8"
|
|
18
|
+
assert events.ipv4.port is None
|
|
16
19
|
assert events.ipv6.type == "IP_ADDRESS"
|
|
20
|
+
assert events.ipv6.netloc == "[2001:4860:4860::8888]"
|
|
21
|
+
assert events.ipv6.port is None
|
|
22
|
+
assert events.ipv6_open_port.netloc == "[2001:4860:4860::8888]:443"
|
|
17
23
|
assert events.netv4.type == "IP_RANGE"
|
|
24
|
+
assert events.netv4.netloc is None
|
|
25
|
+
assert "netloc" not in events.netv4.json()
|
|
18
26
|
assert events.netv6.type == "IP_RANGE"
|
|
19
27
|
assert events.domain.type == "DNS_NAME"
|
|
28
|
+
assert events.domain.netloc == "publicapis.org"
|
|
29
|
+
assert events.domain.port is None
|
|
20
30
|
assert "domain" in events.domain.tags
|
|
21
31
|
assert events.subdomain.type == "DNS_NAME"
|
|
22
32
|
assert "subdomain" in events.subdomain.tags
|
|
@@ -32,6 +42,7 @@ async def test_events(events, helpers):
|
|
|
32
42
|
# ip tests
|
|
33
43
|
assert events.ipv4 == scan.make_event("8.8.8.8", dummy=True)
|
|
34
44
|
assert "8.8.8.8" in events.ipv4
|
|
45
|
+
assert events.ipv4.host_filterable == "8.8.8.8"
|
|
35
46
|
assert "8.8.8.8" == events.ipv4
|
|
36
47
|
assert "8.8.8.8" in events.netv4
|
|
37
48
|
assert "8.8.8.9" not in events.ipv4
|
|
@@ -49,11 +60,19 @@ async def test_events(events, helpers):
|
|
|
49
60
|
assert events.emoji not in events.ipv4
|
|
50
61
|
assert events.emoji not in events.netv6
|
|
51
62
|
assert events.netv6 not in events.emoji
|
|
52
|
-
|
|
63
|
+
ipv6_event = scan.make_event(" [DEaD::c0De]:88", "DNS_NAME", dummy=True)
|
|
64
|
+
assert "dead::c0de" == ipv6_event
|
|
65
|
+
assert ipv6_event.host_filterable == "dead::c0de"
|
|
66
|
+
range_to_ip = scan.make_event("1.2.3.4/32", dummy=True)
|
|
67
|
+
assert range_to_ip.type == "IP_ADDRESS"
|
|
68
|
+
range_to_ip = scan.make_event("dead::beef/128", dummy=True)
|
|
69
|
+
assert range_to_ip.type == "IP_ADDRESS"
|
|
53
70
|
|
|
54
71
|
# hostname tests
|
|
55
72
|
assert events.domain.host == "publicapis.org"
|
|
73
|
+
assert events.domain.host_filterable == "publicapis.org"
|
|
56
74
|
assert events.subdomain.host == "api.publicapis.org"
|
|
75
|
+
assert events.subdomain.host_filterable == "api.publicapis.org"
|
|
57
76
|
assert events.domain.host_stem == "publicapis"
|
|
58
77
|
assert events.subdomain.host_stem == "api.publicapis"
|
|
59
78
|
assert "api.publicapis.org" in events.domain
|
|
@@ -62,23 +81,39 @@ async def test_events(events, helpers):
|
|
|
62
81
|
assert "fsocie.ty" not in events.subdomain
|
|
63
82
|
assert events.subdomain in events.domain
|
|
64
83
|
assert events.domain not in events.subdomain
|
|
65
|
-
assert
|
|
66
|
-
assert
|
|
84
|
+
assert events.ipv4 not in events.domain
|
|
85
|
+
assert events.netv6 not in events.domain
|
|
67
86
|
assert events.emoji not in events.domain
|
|
68
87
|
assert events.domain not in events.emoji
|
|
69
|
-
|
|
70
|
-
|
|
88
|
+
open_port_event = scan.make_event(" eViLcorp.COM.:88", "DNS_NAME", dummy=True)
|
|
89
|
+
dns_event = scan.make_event("evilcorp.com.", "DNS_NAME", dummy=True)
|
|
90
|
+
for e in (open_port_event, dns_event):
|
|
91
|
+
assert "evilcorp.com" == e
|
|
92
|
+
assert e.netloc == "evilcorp.com"
|
|
93
|
+
assert e.json()["netloc"] == "evilcorp.com"
|
|
94
|
+
assert e.port is None
|
|
95
|
+
assert "port" not in e.json()
|
|
71
96
|
|
|
72
97
|
# url tests
|
|
73
|
-
|
|
98
|
+
url_no_trailing_slash = scan.make_event("http://evilcorp.com", dummy=True)
|
|
99
|
+
url_trailing_slash = scan.make_event("http://evilcorp.com/", dummy=True)
|
|
100
|
+
assert url_no_trailing_slash == url_trailing_slash
|
|
101
|
+
assert url_no_trailing_slash.host_filterable == "http://evilcorp.com/"
|
|
102
|
+
assert url_trailing_slash.host_filterable == "http://evilcorp.com/"
|
|
74
103
|
assert events.url_unverified.host == "api.publicapis.org"
|
|
75
104
|
assert events.url_unverified in events.domain
|
|
76
105
|
assert events.url_unverified in events.subdomain
|
|
77
106
|
assert "api.publicapis.org:443" in events.url_unverified
|
|
78
107
|
assert "publicapis.org" not in events.url_unverified
|
|
79
108
|
assert events.ipv4_url_unverified in events.ipv4
|
|
109
|
+
assert events.ipv4_url_unverified.netloc == "8.8.8.8:443"
|
|
110
|
+
assert events.ipv4_url_unverified.port == 443
|
|
111
|
+
assert events.ipv4_url_unverified.json()["port"] == 443
|
|
80
112
|
assert events.ipv4_url_unverified in events.netv4
|
|
81
113
|
assert events.ipv6_url_unverified in events.ipv6
|
|
114
|
+
assert events.ipv6_url_unverified.netloc == "[2001:4860:4860::8888]:443"
|
|
115
|
+
assert events.ipv6_url_unverified.port == 443
|
|
116
|
+
assert events.ipv6_url_unverified.json()["port"] == 443
|
|
82
117
|
assert events.ipv6_url_unverified in events.netv6
|
|
83
118
|
assert events.emoji not in events.url_unverified
|
|
84
119
|
assert events.emoji not in events.ipv6_url_unverified
|
|
@@ -107,6 +142,7 @@ async def test_events(events, helpers):
|
|
|
107
142
|
assert events.http_response.port == 80
|
|
108
143
|
assert events.http_response.parsed_url.scheme == "http"
|
|
109
144
|
assert events.http_response.with_port().geturl() == "http://example.com:80/"
|
|
145
|
+
assert events.http_response.host_filterable == "http://example.com/"
|
|
110
146
|
|
|
111
147
|
http_response = scan.make_event(
|
|
112
148
|
{
|
|
@@ -171,7 +207,7 @@ async def test_events(events, helpers):
|
|
|
171
207
|
|
|
172
208
|
# scope distance
|
|
173
209
|
event1 = scan.make_event("1.2.3.4", dummy=True)
|
|
174
|
-
assert event1._scope_distance
|
|
210
|
+
assert event1._scope_distance is None
|
|
175
211
|
event1.scope_distance = 0
|
|
176
212
|
assert event1._scope_distance == 0
|
|
177
213
|
event2 = scan.make_event("2.3.4.5", parent=event1)
|
|
@@ -192,6 +228,8 @@ async def test_events(events, helpers):
|
|
|
192
228
|
|
|
193
229
|
org_stub_1 = scan.make_event("STUB1", "ORG_STUB", parent=scan.root_event)
|
|
194
230
|
org_stub_1.scope_distance == 1
|
|
231
|
+
assert org_stub_1.netloc is None
|
|
232
|
+
assert "netloc" not in org_stub_1.json()
|
|
195
233
|
org_stub_2 = scan.make_event("STUB2", "ORG_STUB", parent=org_stub_1)
|
|
196
234
|
org_stub_2.scope_distance == 2
|
|
197
235
|
|
|
@@ -199,7 +237,7 @@ async def test_events(events, helpers):
|
|
|
199
237
|
root_event = scan.make_event("0.0.0.0", dummy=True)
|
|
200
238
|
root_event.scope_distance = 0
|
|
201
239
|
internal_event1 = scan.make_event("1.2.3.4", parent=root_event, internal=True)
|
|
202
|
-
assert internal_event1._internal
|
|
240
|
+
assert internal_event1._internal is True
|
|
203
241
|
assert "internal" in internal_event1.tags
|
|
204
242
|
|
|
205
243
|
# tag inheritance
|
|
@@ -231,8 +269,8 @@ async def test_events(events, helpers):
|
|
|
231
269
|
# updating module
|
|
232
270
|
event3 = scan.make_event("127.0.0.1", parent=scan.root_event)
|
|
233
271
|
updated_event = scan.make_event(event3, internal=True)
|
|
234
|
-
assert event3.internal
|
|
235
|
-
assert updated_event.internal
|
|
272
|
+
assert event3.internal is False
|
|
273
|
+
assert updated_event.internal is True
|
|
236
274
|
|
|
237
275
|
# event sorting
|
|
238
276
|
parent1 = scan.make_event("127.0.0.1", parent=scan.root_event)
|
|
@@ -412,17 +450,55 @@ async def test_events(events, helpers):
|
|
|
412
450
|
== "http://xn--12c1bik6bbd8ab6hd1b5jc6jta.com/ทดสอบ"
|
|
413
451
|
)
|
|
414
452
|
|
|
453
|
+
# test event uuid
|
|
454
|
+
import uuid
|
|
455
|
+
|
|
456
|
+
parent_event1 = scan.make_event("evilcorp.com", parent=scan.root_event, context="test context")
|
|
457
|
+
parent_event2 = scan.make_event("evilcorp.com", parent=scan.root_event, context="test context")
|
|
458
|
+
|
|
459
|
+
event1 = scan.make_event("evilcorp.com:80", parent=parent_event1, context="test context")
|
|
460
|
+
assert hasattr(event1, "_uuid")
|
|
461
|
+
assert hasattr(event1, "uuid")
|
|
462
|
+
assert isinstance(event1._uuid, uuid.UUID)
|
|
463
|
+
assert isinstance(event1.uuid, str)
|
|
464
|
+
assert event1.uuid == f"{event1.type}:{event1._uuid}"
|
|
465
|
+
event2 = scan.make_event("evilcorp.com:80", parent=parent_event2, context="test context")
|
|
466
|
+
assert hasattr(event2, "_uuid")
|
|
467
|
+
assert hasattr(event2, "uuid")
|
|
468
|
+
assert isinstance(event2._uuid, uuid.UUID)
|
|
469
|
+
assert isinstance(event2.uuid, str)
|
|
470
|
+
assert event2.uuid == f"{event2.type}:{event2._uuid}"
|
|
471
|
+
# ids should match because the event type + data is the same
|
|
472
|
+
assert event1.id == event2.id
|
|
473
|
+
# but uuids should be unique!
|
|
474
|
+
assert event1.uuid != event2.uuid
|
|
475
|
+
# parent ids should match
|
|
476
|
+
assert event1.parent_id == event2.parent_id == parent_event1.id == parent_event2.id
|
|
477
|
+
# uuids should not
|
|
478
|
+
assert event1.parent_uuid == parent_event1.uuid
|
|
479
|
+
assert event2.parent_uuid == parent_event2.uuid
|
|
480
|
+
assert event1.parent_uuid != event2.parent_uuid
|
|
481
|
+
|
|
415
482
|
# test event serialization
|
|
416
483
|
from bbot.core.event import event_from_json
|
|
417
484
|
|
|
418
485
|
db_event = scan.make_event("evilcorp.com:80", parent=scan.root_event, context="test context")
|
|
486
|
+
assert db_event.parent == scan.root_event
|
|
487
|
+
assert db_event.parent is scan.root_event
|
|
419
488
|
db_event._resolved_hosts = {"127.0.0.1"}
|
|
420
489
|
db_event.scope_distance = 1
|
|
421
490
|
assert db_event.discovery_context == "test context"
|
|
422
491
|
assert db_event.discovery_path == ["test context"]
|
|
423
|
-
assert db_event.parent_chain ==
|
|
492
|
+
assert len(db_event.parent_chain) == 1
|
|
493
|
+
assert all(event_uuid_regex.match(u) for u in db_event.parent_chain)
|
|
494
|
+
assert db_event.parent_chain[0] == str(db_event.uuid)
|
|
495
|
+
assert db_event.parent.uuid == scan.root_event.uuid
|
|
496
|
+
assert db_event.parent_uuid == scan.root_event.uuid
|
|
424
497
|
timestamp = db_event.timestamp.isoformat()
|
|
425
498
|
json_event = db_event.json()
|
|
499
|
+
assert isinstance(json_event["uuid"], str)
|
|
500
|
+
assert json_event["uuid"] == str(db_event.uuid)
|
|
501
|
+
assert json_event["parent_uuid"] == str(scan.root_event.uuid)
|
|
426
502
|
assert json_event["scope_distance"] == 1
|
|
427
503
|
assert json_event["data"] == "evilcorp.com:80"
|
|
428
504
|
assert json_event["type"] == "OPEN_TCP_PORT"
|
|
@@ -430,8 +506,14 @@ async def test_events(events, helpers):
|
|
|
430
506
|
assert json_event["timestamp"] == timestamp
|
|
431
507
|
assert json_event["discovery_context"] == "test context"
|
|
432
508
|
assert json_event["discovery_path"] == ["test context"]
|
|
433
|
-
assert json_event["parent_chain"] ==
|
|
509
|
+
assert json_event["parent_chain"] == db_event.parent_chain
|
|
510
|
+
assert json_event["parent_chain"][0] == str(db_event.uuid)
|
|
434
511
|
reconstituted_event = event_from_json(json_event)
|
|
512
|
+
assert isinstance(reconstituted_event._uuid, uuid.UUID)
|
|
513
|
+
assert str(reconstituted_event.uuid) == json_event["uuid"]
|
|
514
|
+
assert str(reconstituted_event.parent_uuid) == json_event["parent_uuid"]
|
|
515
|
+
assert reconstituted_event.uuid == db_event.uuid
|
|
516
|
+
assert reconstituted_event.parent_uuid == scan.root_event.uuid
|
|
435
517
|
assert reconstituted_event.scope_distance == 1
|
|
436
518
|
assert reconstituted_event.timestamp.isoformat() == timestamp
|
|
437
519
|
assert reconstituted_event.data == "evilcorp.com:80"
|
|
@@ -439,13 +521,13 @@ async def test_events(events, helpers):
|
|
|
439
521
|
assert reconstituted_event.host == "evilcorp.com"
|
|
440
522
|
assert reconstituted_event.discovery_context == "test context"
|
|
441
523
|
assert reconstituted_event.discovery_path == ["test context"]
|
|
442
|
-
assert reconstituted_event.parent_chain ==
|
|
524
|
+
assert reconstituted_event.parent_chain == db_event.parent_chain
|
|
443
525
|
assert "127.0.0.1" in reconstituted_event.resolved_hosts
|
|
444
526
|
hostless_event = scan.make_event("asdf", "ASDF", dummy=True)
|
|
445
527
|
hostless_event_json = hostless_event.json()
|
|
446
528
|
assert hostless_event_json["type"] == "ASDF"
|
|
447
529
|
assert hostless_event_json["data"] == "asdf"
|
|
448
|
-
assert
|
|
530
|
+
assert "host" not in hostless_event_json
|
|
449
531
|
|
|
450
532
|
# SIEM-friendly serialize/deserialize
|
|
451
533
|
json_event_siemfriendly = db_event.json(siem_friendly=True)
|
|
@@ -616,7 +698,7 @@ async def test_event_discovery_context():
|
|
|
616
698
|
)
|
|
617
699
|
|
|
618
700
|
events = [e async for e in scan.async_start()]
|
|
619
|
-
assert len(events) ==
|
|
701
|
+
assert len(events) == 7
|
|
620
702
|
|
|
621
703
|
assert 1 == len(
|
|
622
704
|
[
|
|
@@ -723,7 +805,7 @@ async def test_event_web_spider_distance(bbot_scanner):
|
|
|
723
805
|
)
|
|
724
806
|
assert url_event_3.web_spider_distance == 1
|
|
725
807
|
assert "spider-danger" in url_event_3.tags
|
|
726
|
-
assert
|
|
808
|
+
assert "spider-max" not in url_event_3.tags
|
|
727
809
|
social_event = scan.make_event(
|
|
728
810
|
{"platform": "github", "url": "http://www.evilcorp.com/test4"}, "SOCIAL", parent=url_event_3
|
|
729
811
|
)
|
|
@@ -746,42 +828,42 @@ async def test_event_web_spider_distance(bbot_scanner):
|
|
|
746
828
|
|
|
747
829
|
url_event = scan.make_event("http://www.evilcorp.com", "URL_UNVERIFIED", parent=scan.root_event)
|
|
748
830
|
assert url_event.web_spider_distance == 0
|
|
749
|
-
assert
|
|
750
|
-
assert
|
|
831
|
+
assert "spider-danger" not in url_event.tags
|
|
832
|
+
assert "spider-max" not in url_event.tags
|
|
751
833
|
url_event_2 = scan.make_event(
|
|
752
834
|
"http://www.evilcorp.com", "URL_UNVERIFIED", parent=scan.root_event, tags="spider-danger"
|
|
753
835
|
)
|
|
754
836
|
# spider distance shouldn't increment because it's not the same host
|
|
755
837
|
assert url_event_2.web_spider_distance == 0
|
|
756
838
|
assert "spider-danger" in url_event_2.tags
|
|
757
|
-
assert
|
|
839
|
+
assert "spider-max" not in url_event_2.tags
|
|
758
840
|
url_event_3 = scan.make_event(
|
|
759
841
|
"http://www.evilcorp.com/3", "URL_UNVERIFIED", parent=url_event_2, tags="spider-danger"
|
|
760
842
|
)
|
|
761
843
|
assert url_event_3.web_spider_distance == 1
|
|
762
844
|
assert "spider-danger" in url_event_3.tags
|
|
763
|
-
assert
|
|
845
|
+
assert "spider-max" not in url_event_3.tags
|
|
764
846
|
url_event_4 = scan.make_event("http://evilcorp.com", "URL_UNVERIFIED", parent=url_event_3)
|
|
765
847
|
assert url_event_4.web_spider_distance == 0
|
|
766
|
-
assert
|
|
767
|
-
assert
|
|
848
|
+
assert "spider-danger" not in url_event_4.tags
|
|
849
|
+
assert "spider-max" not in url_event_4.tags
|
|
768
850
|
url_event_4.add_tag("spider-danger")
|
|
769
851
|
assert url_event_4.web_spider_distance == 0
|
|
770
852
|
assert "spider-danger" in url_event_4.tags
|
|
771
|
-
assert
|
|
853
|
+
assert "spider-max" not in url_event_4.tags
|
|
772
854
|
url_event_4.remove_tag("spider-danger")
|
|
773
855
|
assert url_event_4.web_spider_distance == 0
|
|
774
|
-
assert
|
|
775
|
-
assert
|
|
856
|
+
assert "spider-danger" not in url_event_4.tags
|
|
857
|
+
assert "spider-max" not in url_event_4.tags
|
|
776
858
|
url_event_5 = scan.make_event("http://evilcorp.com/5", "URL_UNVERIFIED", parent=url_event_4)
|
|
777
859
|
assert url_event_5.web_spider_distance == 0
|
|
778
|
-
assert
|
|
779
|
-
assert
|
|
860
|
+
assert "spider-danger" not in url_event_5.tags
|
|
861
|
+
assert "spider-max" not in url_event_5.tags
|
|
780
862
|
url_event_5.add_tag("spider-danger")
|
|
781
863
|
# if host is the same as parent, web spider distance should auto-increment after adding spider-danger tag
|
|
782
864
|
assert url_event_5.web_spider_distance == 1
|
|
783
865
|
assert "spider-danger" in url_event_5.tags
|
|
784
|
-
assert
|
|
866
|
+
assert "spider-max" not in url_event_5.tags
|
|
785
867
|
|
|
786
868
|
|
|
787
869
|
def test_event_confidence():
|
|
@@ -856,3 +938,76 @@ def test_event_closest_host():
|
|
|
856
938
|
vuln = scan.make_event(
|
|
857
939
|
{"path": "/tmp/asdf.txt", "description": "test", "severity": "HIGH"}, "VULNERABILITY", parent=event3
|
|
858
940
|
)
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def test_event_magic():
|
|
944
|
+
from bbot.core.helpers.libmagic import get_magic_info, get_compression
|
|
945
|
+
|
|
946
|
+
import base64
|
|
947
|
+
|
|
948
|
+
zip_base64 = "UEsDBAoDAAAAAOMmZ1lR4FaHBQAAAAUAAAAIAAAAYXNkZi50eHRhc2RmClBLAQI/AwoDAAAAAOMmZ1lR4FaHBQAAAAUAAAAIACQAAAAAAAAAIICkgQAAAABhc2RmLnR4dAoAIAAAAAAAAQAYAICi2B77MNsBgKLYHvsw2wGAotge+zDbAVBLBQYAAAAAAQABAFoAAAArAAAAAAA="
|
|
949
|
+
zip_bytes = base64.b64decode(zip_base64)
|
|
950
|
+
zip_file = Path("/tmp/.bbottestzipasdkfjalsdf.zip")
|
|
951
|
+
with open(zip_file, "wb") as f:
|
|
952
|
+
f.write(zip_bytes)
|
|
953
|
+
|
|
954
|
+
# test magic helpers
|
|
955
|
+
extension, mime_type, description, confidence = get_magic_info(zip_file)
|
|
956
|
+
assert extension == ".zip"
|
|
957
|
+
assert mime_type == "application/zip"
|
|
958
|
+
assert description == "PKZIP Archive file"
|
|
959
|
+
assert confidence > 0
|
|
960
|
+
assert get_compression(mime_type) == "zip"
|
|
961
|
+
|
|
962
|
+
# test filesystem event - file
|
|
963
|
+
scan = Scanner()
|
|
964
|
+
event = scan.make_event({"path": zip_file}, "FILESYSTEM", parent=scan.root_event)
|
|
965
|
+
assert event.data == {
|
|
966
|
+
"path": "/tmp/.bbottestzipasdkfjalsdf.zip",
|
|
967
|
+
"magic_extension": ".zip",
|
|
968
|
+
"magic_mime_type": "application/zip",
|
|
969
|
+
"magic_description": "PKZIP Archive file",
|
|
970
|
+
"magic_confidence": 0.9,
|
|
971
|
+
"compression": "zip",
|
|
972
|
+
}
|
|
973
|
+
assert event.tags == {"file", "zip-archive", "compressed"}
|
|
974
|
+
|
|
975
|
+
# test filesystem event - folder
|
|
976
|
+
scan = Scanner()
|
|
977
|
+
event = scan.make_event({"path": "/tmp"}, "FILESYSTEM", parent=scan.root_event)
|
|
978
|
+
assert event.data == {"path": "/tmp"}
|
|
979
|
+
assert event.tags == {"folder"}
|
|
980
|
+
|
|
981
|
+
zip_file.unlink()
|
|
982
|
+
|
|
983
|
+
|
|
984
|
+
def test_event_hashing():
|
|
985
|
+
scan = Scanner("example.com")
|
|
986
|
+
url_event = scan.make_event("https://api.example.com/", "URL_UNVERIFIED", parent=scan.root_event)
|
|
987
|
+
host_event_1 = scan.make_event("www.example.com", "DNS_NAME", parent=url_event)
|
|
988
|
+
host_event_2 = scan.make_event("test.example.com", "DNS_NAME", parent=url_event)
|
|
989
|
+
finding_data = {"description": "Custom Yara Rule [find_string] Matched via identifier [str1]"}
|
|
990
|
+
finding1 = scan.make_event(finding_data, "FINDING", parent=host_event_1)
|
|
991
|
+
finding2 = scan.make_event(finding_data, "FINDING", parent=host_event_2)
|
|
992
|
+
finding3 = scan.make_event(finding_data, "FINDING", parent=host_event_2)
|
|
993
|
+
|
|
994
|
+
assert finding1.data == {
|
|
995
|
+
"description": "Custom Yara Rule [find_string] Matched via identifier [str1]",
|
|
996
|
+
"host": "www.example.com",
|
|
997
|
+
}
|
|
998
|
+
assert finding2.data == {
|
|
999
|
+
"description": "Custom Yara Rule [find_string] Matched via identifier [str1]",
|
|
1000
|
+
"host": "test.example.com",
|
|
1001
|
+
}
|
|
1002
|
+
assert finding3.data == {
|
|
1003
|
+
"description": "Custom Yara Rule [find_string] Matched via identifier [str1]",
|
|
1004
|
+
"host": "test.example.com",
|
|
1005
|
+
}
|
|
1006
|
+
assert finding1.id != finding2.id
|
|
1007
|
+
assert finding2.id == finding3.id
|
|
1008
|
+
assert finding1.data_id != finding2.data_id
|
|
1009
|
+
assert finding2.data_id == finding3.data_id
|
|
1010
|
+
assert finding1.data_hash != finding2.data_hash
|
|
1011
|
+
assert finding2.data_hash == finding3.data_hash
|
|
1012
|
+
assert hash(finding1) != hash(finding2)
|
|
1013
|
+
assert hash(finding2) == hash(finding3)
|