bbot 2.0.1.4720rc0__py3-none-any.whl → 2.3.0.5401rc0__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 +12 -10
- 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 +20 -21
- 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 +29 -12
- bbot/modules/internal/dnsresolve.py +22 -22
- bbot/modules/internal/excavate.py +97 -59
- 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 +8 -11
- 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 +18 -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 -2
- bbot/presets/spider.yml +4 -0
- bbot/presets/subdomain-enum.yml +7 -7
- bbot/presets/web/dotnet-audit.yml +0 -1
- bbot/scanner/manager.py +5 -16
- bbot/scanner/preset/args.py +46 -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__module__tests.py +0 -1
- bbot/test/test_step_1/test_bbot_fastapi.py +79 -0
- bbot/test/test_step_1/test_bloom_filter.py +2 -1
- bbot/test/test_step_1/test_cli.py +138 -64
- bbot/test/test_step_1/test_dns.py +61 -27
- bbot/test/test_step_1/test_engine.py +17 -19
- bbot/test/test_step_1/test_events.py +183 -30
- bbot/test/test_step_1/test_helpers.py +64 -29
- 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 +183 -100
- 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 +242 -145
- bbot/test/test_step_1/test_web.py +14 -10
- 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 +28 -48
- 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 -6
- 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 +16 -16
- bbot/test/test_step_2/module_tests/test_module_ntlm.py +8 -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_pgp.py +2 -2
- 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 +14 -14
- bbot/test/test_step_2/module_tests/test_module_social.py +11 -1
- bbot/test/test_step_2/module_tests/test_module_speculate.py +4 -8
- 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_viewdns.py +1 -1
- 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/wordlists/devops_mutations.txt +1 -1
- bbot/wordlists/ffuf_shortname_candidates.txt +1 -1
- bbot/wordlists/nameservers.txt +1 -1
- bbot/wordlists/paramminer_headers.txt +1 -1
- bbot/wordlists/paramminer_parameters.txt +1 -1
- bbot/wordlists/raft-small-extensions-lowercase_CLEANED.txt +1 -1
- bbot/wordlists/valid_url_schemes.txt +1 -1
- {bbot-2.0.1.4720rc0.dist-info → bbot-2.3.0.5401rc0.dist-info}/METADATA +48 -18
- bbot-2.3.0.5401rc0.dist-info/RECORD +421 -0
- {bbot-2.0.1.4720rc0.dist-info → bbot-2.3.0.5401rc0.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.5401rc0.dist-info}/LICENSE +0 -0
- {bbot-2.0.1.4720rc0.dist-info → bbot-2.3.0.5401rc0.dist-info}/entry_points.txt +0 -0
|
@@ -6,7 +6,6 @@ from ..bbot_fixtures import *
|
|
|
6
6
|
|
|
7
7
|
@pytest.mark.asyncio
|
|
8
8
|
async def test_web_engine(bbot_scanner, bbot_httpserver, httpx_mock):
|
|
9
|
-
|
|
10
9
|
from werkzeug.wrappers import Response
|
|
11
10
|
|
|
12
11
|
def server_handler(request):
|
|
@@ -29,7 +28,7 @@ async def test_web_engine(bbot_scanner, bbot_httpserver, httpx_mock):
|
|
|
29
28
|
urls = [f"{base_url}{i}" for i in range(num_urls)]
|
|
30
29
|
responses = [r async for r in scan.helpers.request_batch(urls)]
|
|
31
30
|
assert len(responses) == 100
|
|
32
|
-
assert all(
|
|
31
|
+
assert all(r[1].status_code == 200 and r[1].text.startswith(f"{r[0]}: ") for r in responses)
|
|
33
32
|
|
|
34
33
|
# request_batch w/ cancellation
|
|
35
34
|
agen = scan.helpers.request_batch(urls)
|
|
@@ -134,7 +133,6 @@ async def test_request_batch_cancellation(bbot_scanner, bbot_httpserver, httpx_m
|
|
|
134
133
|
|
|
135
134
|
@pytest.mark.asyncio
|
|
136
135
|
async def test_web_helpers(bbot_scanner, bbot_httpserver, httpx_mock):
|
|
137
|
-
|
|
138
136
|
# json conversion
|
|
139
137
|
scan = bbot_scanner("evilcorp.com")
|
|
140
138
|
url = "http://www.evilcorp.com/json_test?a=b"
|
|
@@ -153,9 +151,12 @@ async def test_web_helpers(bbot_scanner, bbot_httpserver, httpx_mock):
|
|
|
153
151
|
|
|
154
152
|
await scan._cleanup()
|
|
155
153
|
|
|
156
|
-
scan1 = bbot_scanner("8.8.8.8")
|
|
154
|
+
scan1 = bbot_scanner("8.8.8.8", modules=["ipneighbor"])
|
|
157
155
|
scan2 = bbot_scanner("127.0.0.1")
|
|
158
156
|
|
|
157
|
+
await scan1._prep()
|
|
158
|
+
module = scan1.modules["ipneighbor"]
|
|
159
|
+
|
|
159
160
|
web_config = CORE.config.get("web", {})
|
|
160
161
|
user_agent = web_config.get("user_agent", "")
|
|
161
162
|
headers = {"User-Agent": user_agent}
|
|
@@ -208,14 +209,14 @@ async def test_web_helpers(bbot_scanner, bbot_httpserver, httpx_mock):
|
|
|
208
209
|
url = bbot_httpserver.url_for(path)
|
|
209
210
|
bbot_httpserver.expect_request(uri=path).respond_with_data(download_content, status=200)
|
|
210
211
|
webpage = await scan1.helpers.request(url)
|
|
211
|
-
assert webpage,
|
|
212
|
+
assert webpage, "Webpage is False"
|
|
212
213
|
soup = scan1.helpers.beautifulsoup(webpage, "html.parser")
|
|
213
|
-
assert soup,
|
|
214
|
+
assert soup, "Soup is False"
|
|
214
215
|
# pretty_print = soup.prettify()
|
|
215
216
|
# assert pretty_print, f"PrettyPrint is False"
|
|
216
217
|
# scan1.helpers.log.info(f"{pretty_print}")
|
|
217
218
|
html_text = soup.find(text="Example Domain")
|
|
218
|
-
assert html_text,
|
|
219
|
+
assert html_text, "Find HTML Text is False"
|
|
219
220
|
|
|
220
221
|
# 404
|
|
221
222
|
path = "/test_http_helpers_download_404"
|
|
@@ -252,7 +253,7 @@ async def test_web_helpers(bbot_scanner, bbot_httpserver, httpx_mock):
|
|
|
252
253
|
uri=f"{base_path}/3", query_string={"page_size": "100", "offset": "200"}
|
|
253
254
|
).respond_with_data("page3")
|
|
254
255
|
results = []
|
|
255
|
-
agen =
|
|
256
|
+
agen = module.api_page_iter(template_url)
|
|
256
257
|
try:
|
|
257
258
|
async for result in agen:
|
|
258
259
|
if result and result.text.startswith("page"):
|
|
@@ -262,7 +263,7 @@ async def test_web_helpers(bbot_scanner, bbot_httpserver, httpx_mock):
|
|
|
262
263
|
finally:
|
|
263
264
|
await agen.aclose()
|
|
264
265
|
assert not results
|
|
265
|
-
agen =
|
|
266
|
+
agen = module.api_page_iter(template_url, json=False)
|
|
266
267
|
try:
|
|
267
268
|
async for result in agen:
|
|
268
269
|
if result and result.text.startswith("page"):
|
|
@@ -386,7 +387,7 @@ async def test_web_http_compare(httpx_mock, bbot_scanner):
|
|
|
386
387
|
await compare_helper.compare("http://www.example.com", check_reflection=True)
|
|
387
388
|
compare_helper.compare_body({"asdf": "fdsa"}, {"fdsa": "asdf"})
|
|
388
389
|
for mode in ("getparam", "header", "cookie"):
|
|
389
|
-
assert await compare_helper.canary_check("http://www.example.com", mode=mode)
|
|
390
|
+
assert await compare_helper.canary_check("http://www.example.com", mode=mode) is True
|
|
390
391
|
|
|
391
392
|
await scan._cleanup()
|
|
392
393
|
|
|
@@ -468,6 +469,9 @@ async def test_web_cookies(bbot_scanner, httpx_mock):
|
|
|
468
469
|
# but that they're not sent in the response
|
|
469
470
|
with pytest.raises(httpx.TimeoutException):
|
|
470
471
|
r = await client2.get(url="http://www2.evilcorp.com/cookies/test")
|
|
472
|
+
# make sure cookies are sent
|
|
473
|
+
r = await client2.get(url="http://www2.evilcorp.com/cookies/test", cookies={"wats": "fdsa"})
|
|
474
|
+
assert r.status_code == 200
|
|
471
475
|
# make sure we can manually send cookies
|
|
472
476
|
httpx_mock.add_response(url="http://www2.evilcorp.com/cookies/test2", match_headers={"Cookie": "fdsa=wats"})
|
|
473
477
|
r = await client2.get(url="http://www2.evilcorp.com/cookies/test2", cookies={"fdsa": "wats"})
|
|
@@ -20,6 +20,8 @@ class ModuleTestBase:
|
|
|
20
20
|
config_overrides = {}
|
|
21
21
|
modules_overrides = None
|
|
22
22
|
log = logging.getLogger("bbot")
|
|
23
|
+
# if True, the test will be skipped (useful for tests that require docker)
|
|
24
|
+
skip_distro_tests = False
|
|
23
25
|
|
|
24
26
|
class ModuleTest:
|
|
25
27
|
def __init__(
|
|
@@ -89,33 +91,39 @@ class ModuleTestBase:
|
|
|
89
91
|
async def module_test(
|
|
90
92
|
self, httpx_mock, bbot_httpserver, bbot_httpserver_ssl, monkeypatch, request, caplog, capsys
|
|
91
93
|
):
|
|
94
|
+
# Skip dastardly test if we're in the distro tests (because dastardly uses docker)
|
|
95
|
+
if os.getenv("BBOT_DISTRO_TESTS") and self.skip_distro_tests:
|
|
96
|
+
pytest.skip("Skipping module_test for dastardly module due to BBOT_DISTRO_TESTS environment variable")
|
|
97
|
+
|
|
92
98
|
self.log.info(f"Starting {self.name} module test")
|
|
93
99
|
module_test = self.ModuleTest(
|
|
94
100
|
self, httpx_mock, bbot_httpserver, bbot_httpserver_ssl, monkeypatch, request, caplog, capsys
|
|
95
101
|
)
|
|
96
|
-
self.log.debug(
|
|
102
|
+
self.log.debug("Mocking DNS")
|
|
97
103
|
await module_test.mock_dns({"blacklanternsecurity.com": {"A": ["127.0.0.88"]}})
|
|
98
|
-
self.log.debug(
|
|
104
|
+
self.log.debug("Executing setup_before_prep()")
|
|
99
105
|
await self.setup_before_prep(module_test)
|
|
100
|
-
self.log.debug(
|
|
106
|
+
self.log.debug("Executing scan._prep()")
|
|
101
107
|
await module_test.scan._prep()
|
|
102
|
-
self.log.debug(
|
|
108
|
+
self.log.debug("Executing setup_after_prep()")
|
|
103
109
|
await self.setup_after_prep(module_test)
|
|
104
|
-
self.log.debug(
|
|
110
|
+
self.log.debug("Starting scan")
|
|
105
111
|
module_test.events = [e async for e in module_test.scan.async_start()]
|
|
106
112
|
self.log.debug(f"Finished {module_test.name} module test")
|
|
107
113
|
yield module_test
|
|
108
114
|
|
|
109
115
|
@pytest.mark.asyncio
|
|
110
116
|
async def test_module_run(self, module_test):
|
|
111
|
-
|
|
117
|
+
from bbot.core.helpers.misc import execute_sync_or_async
|
|
118
|
+
|
|
119
|
+
await execute_sync_or_async(self.check, module_test, module_test.events)
|
|
112
120
|
module_test.log.info(f"Finished {self.name} module test")
|
|
113
121
|
current_task = asyncio.current_task()
|
|
114
122
|
tasks = [t for t in asyncio.all_tasks() if t != current_task]
|
|
115
123
|
if len(tasks):
|
|
116
124
|
module_test.log.info(f"Unfinished tasks detected: {tasks}")
|
|
117
125
|
else:
|
|
118
|
-
module_test.log.info(
|
|
126
|
+
module_test.log.info("No unfinished tasks detected")
|
|
119
127
|
|
|
120
128
|
def check(self, module_test, events):
|
|
121
129
|
assert False, f"Must override {self.name}.check()"
|
|
@@ -5,7 +5,7 @@ class TestAnubisdb(ModuleTestBase):
|
|
|
5
5
|
async def setup_after_prep(self, module_test):
|
|
6
6
|
module_test.module.abort_if = lambda e: False
|
|
7
7
|
module_test.httpx_mock.add_response(
|
|
8
|
-
url=
|
|
8
|
+
url="https://jldc.me/anubis/subdomains/blacklanternsecurity.com",
|
|
9
9
|
json=["asdf.blacklanternsecurity.com", "zzzz.blacklanternsecurity.com"],
|
|
10
10
|
)
|
|
11
11
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from .base import ModuleTestBase, tempapkfile
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TestAPKPure(ModuleTestBase):
|
|
6
|
+
modules_overrides = ["apkpure", "google_playstore", "speculate"]
|
|
7
|
+
apk_file = tempapkfile()
|
|
8
|
+
|
|
9
|
+
async def setup_after_prep(self, module_test):
|
|
10
|
+
await module_test.mock_dns({"blacklanternsecurity.com": {"A": ["127.0.0.99"]}})
|
|
11
|
+
module_test.httpx_mock.add_response(
|
|
12
|
+
url="https://play.google.com/store/search?q=blacklanternsecurity&c=apps",
|
|
13
|
+
text="""<!DOCTYPE html>
|
|
14
|
+
<html>
|
|
15
|
+
<head>
|
|
16
|
+
<title>"blacklanternsecurity" - Android Apps on Google Play</title>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<a href="/store/apps/details?id=com.bbot.test&pcampaignid=dontmatchme&pli=1"/>
|
|
20
|
+
</body>
|
|
21
|
+
</html>""",
|
|
22
|
+
)
|
|
23
|
+
module_test.httpx_mock.add_response(
|
|
24
|
+
url="https://play.google.com/store/apps/details?id=com.bbot.test",
|
|
25
|
+
text="""<!DOCTYPE html>
|
|
26
|
+
<html>
|
|
27
|
+
<head>
|
|
28
|
+
<title>BBOT</title>
|
|
29
|
+
</head>
|
|
30
|
+
<body>
|
|
31
|
+
<meta name="appstore:developer_url" content="https://www.blacklanternsecurity.com">
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</body>
|
|
35
|
+
</html>""",
|
|
36
|
+
)
|
|
37
|
+
module_test.httpx_mock.add_response(
|
|
38
|
+
url="https://d.apkpure.com/b/XAPK/com.bbot.test?version=latest",
|
|
39
|
+
content=self.apk_file,
|
|
40
|
+
headers={
|
|
41
|
+
"Content-Type": "application/vnd.android.package-archive",
|
|
42
|
+
"Content-Disposition": "attachment; filename=com.bbot.test.apk",
|
|
43
|
+
},
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def check(self, module_test, events):
|
|
47
|
+
assert len(events) == 6
|
|
48
|
+
assert 1 == len(
|
|
49
|
+
[
|
|
50
|
+
e
|
|
51
|
+
for e in events
|
|
52
|
+
if e.type == "DNS_NAME" and e.data == "blacklanternsecurity.com" and e.scope_distance == 0
|
|
53
|
+
]
|
|
54
|
+
), "Failed to emit target DNS_NAME"
|
|
55
|
+
assert 1 == len(
|
|
56
|
+
[e for e in events if e.type == "ORG_STUB" and e.data == "blacklanternsecurity" and e.scope_distance == 0]
|
|
57
|
+
), "Failed to find ORG_STUB"
|
|
58
|
+
assert 1 == len(
|
|
59
|
+
[
|
|
60
|
+
e
|
|
61
|
+
for e in events
|
|
62
|
+
if e.type == "MOBILE_APP"
|
|
63
|
+
and "android" in e.tags
|
|
64
|
+
and e.data["id"] == "com.bbot.test"
|
|
65
|
+
and e.data["url"] == "https://play.google.com/store/apps/details?id=com.bbot.test"
|
|
66
|
+
]
|
|
67
|
+
), "Failed to find bbot android app"
|
|
68
|
+
filesystem_event = [e for e in events if e.type == "FILESYSTEM" and "com.bbot.test.apk" in e.data["path"]]
|
|
69
|
+
assert 1 == len(filesystem_event), "Failed to download apk"
|
|
70
|
+
file = Path(filesystem_event[0].data["path"])
|
|
71
|
+
assert file.is_file(), "Destination apk doesn't exist"
|
|
@@ -10,7 +10,6 @@ class TestAsset_Inventory(ModuleTestBase):
|
|
|
10
10
|
masscan_output = """{ "ip": "127.0.0.1", "timestamp": "1680197558", "ports": [ {"port": 9999, "proto": "tcp", "status": "open", "reason": "syn-ack", "ttl": 54} ] }"""
|
|
11
11
|
|
|
12
12
|
async def setup_before_prep(self, module_test):
|
|
13
|
-
|
|
14
13
|
async def run_masscan(command, *args, **kwargs):
|
|
15
14
|
if "masscan" in command[:2]:
|
|
16
15
|
targets = open(command[11]).read().splitlines()
|
|
@@ -22,7 +22,7 @@ class TestAzure_Realm(ModuleTestBase):
|
|
|
22
22
|
async def setup_after_prep(self, module_test):
|
|
23
23
|
await module_test.mock_dns({"evilcorp.com": {"A": ["127.0.0.5"]}})
|
|
24
24
|
module_test.httpx_mock.add_response(
|
|
25
|
-
url=
|
|
25
|
+
url="https://login.microsoftonline.com/getuserrealm.srf?login=test@evilcorp.com",
|
|
26
26
|
json=self.response_json,
|
|
27
27
|
)
|
|
28
28
|
|
|
@@ -31,9 +31,9 @@ class TestBaddns_cname_nxdomain(BaseTestBaddns):
|
|
|
31
31
|
module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS)
|
|
32
32
|
|
|
33
33
|
def check(self, module_test, events):
|
|
34
|
-
assert any(
|
|
35
|
-
assert any(
|
|
36
|
-
assert any(
|
|
34
|
+
assert any(e.data == "baddns.azurewebsites.net" for e in events), "CNAME detection failed"
|
|
35
|
+
assert any(e.type == "VULNERABILITY" for e in events), "Failed to emit VULNERABILITY"
|
|
36
|
+
assert any("baddns-cname" in e.tags for e in events), "Failed to add baddns tag"
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
class TestBaddns_cname_signature(BaseTestBaddns):
|
|
@@ -60,8 +60,8 @@ class TestBaddns_cname_signature(BaseTestBaddns):
|
|
|
60
60
|
module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS)
|
|
61
61
|
|
|
62
62
|
def check(self, module_test, events):
|
|
63
|
-
assert any(
|
|
63
|
+
assert any(e for e in events)
|
|
64
64
|
assert any(
|
|
65
|
-
|
|
65
|
+
e.type == "VULNERABILITY" and "bigcartel.com" in e.data["description"] for e in events
|
|
66
66
|
), "Failed to emit VULNERABILITY"
|
|
67
|
-
assert any(
|
|
67
|
+
assert any("baddns-cname" in e.tags for e in events), "Failed to add baddns tag"
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from .base import ModuleTestBase
|
|
2
|
+
from bbot.modules.base import BaseModule
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BaseTestBaddns(ModuleTestBase):
|
|
6
|
+
modules_overrides = ["baddns_direct"]
|
|
7
|
+
targets = ["bad.dns"]
|
|
8
|
+
config_overrides = {"dns": {"minimal": False}, "cloudcheck": True}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestBaddns_direct_cloudflare(BaseTestBaddns):
|
|
12
|
+
targets = ["bad.dns:8888"]
|
|
13
|
+
modules_overrides = ["baddns_direct"]
|
|
14
|
+
|
|
15
|
+
async def dispatchWHOIS(self):
|
|
16
|
+
return None
|
|
17
|
+
|
|
18
|
+
class DummyModule(BaseModule):
|
|
19
|
+
watched_events = ["DNS_NAME"]
|
|
20
|
+
_name = "dummy_module"
|
|
21
|
+
events_seen = []
|
|
22
|
+
|
|
23
|
+
async def handle_event(self, event):
|
|
24
|
+
if event.data == "bad.dns":
|
|
25
|
+
await self.helpers.sleep(0.5)
|
|
26
|
+
self.events_seen.append(event.data)
|
|
27
|
+
url = "http://bad.dns:8888/"
|
|
28
|
+
url_event = self.scan.make_event(
|
|
29
|
+
url, "URL", parent=self.scan.root_event, tags=["cdn-cloudflare", "in-scope", "status-401"]
|
|
30
|
+
)
|
|
31
|
+
if url_event is not None:
|
|
32
|
+
await self.emit_event(url_event)
|
|
33
|
+
|
|
34
|
+
async def setup_after_prep(self, module_test):
|
|
35
|
+
from baddns.base import BadDNS_base
|
|
36
|
+
from baddns.lib.whoismanager import WhoisManager
|
|
37
|
+
|
|
38
|
+
def set_target(self, target):
|
|
39
|
+
return "127.0.0.1:8888"
|
|
40
|
+
|
|
41
|
+
self.module_test = module_test
|
|
42
|
+
|
|
43
|
+
self.dummy_module = self.DummyModule(module_test.scan)
|
|
44
|
+
module_test.scan.modules["dummy_module"] = self.dummy_module
|
|
45
|
+
|
|
46
|
+
expect_args = {"method": "GET", "uri": "/"}
|
|
47
|
+
respond_args = {"response_data": "The specified bucket does not exist", "status": 401}
|
|
48
|
+
module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args)
|
|
49
|
+
|
|
50
|
+
await module_test.mock_dns({"bad.dns": {"A": ["127.0.0.1"]}})
|
|
51
|
+
|
|
52
|
+
module_test.monkeypatch.setattr(BadDNS_base, "set_target", set_target)
|
|
53
|
+
module_test.monkeypatch.setattr(WhoisManager, "dispatchWHOIS", self.dispatchWHOIS)
|
|
54
|
+
|
|
55
|
+
def check(self, module_test, events):
|
|
56
|
+
assert any(
|
|
57
|
+
e.type == "FINDING"
|
|
58
|
+
and "Possible [AWS Bucket Takeover Detection] via direct BadDNS analysis. Indicator: [[Words: The specified bucket does not exist | Condition: and | Part: body] Matchers-Condition: and] Trigger: [self] baddns Module: [CNAME]"
|
|
59
|
+
in e.data["description"]
|
|
60
|
+
for e in events
|
|
61
|
+
), "Failed to emit FINDING"
|
|
62
|
+
assert any("baddns-cname" in e.tags for e in events), "Failed to add baddns tag"
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
1
3
|
from .base import ModuleTestBase
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
class TestBeVigil(ModuleTestBase):
|
|
7
|
+
module_name = "bevigil"
|
|
5
8
|
config_overrides = {"modules": {"bevigil": {"api_key": "asdf", "urls": True}}}
|
|
6
9
|
|
|
7
10
|
async def setup_after_prep(self, module_test):
|
|
8
11
|
module_test.httpx_mock.add_response(
|
|
9
|
-
url=
|
|
12
|
+
url="https://osint.bevigil.com/api/blacklanternsecurity.com/subdomains/",
|
|
13
|
+
match_headers={"X-Access-Token": "asdf"},
|
|
10
14
|
json={
|
|
11
15
|
"domain": "blacklanternsecurity.com",
|
|
12
16
|
"subdomains": [
|
|
@@ -15,10 +19,33 @@ class TestBeVigil(ModuleTestBase):
|
|
|
15
19
|
},
|
|
16
20
|
)
|
|
17
21
|
module_test.httpx_mock.add_response(
|
|
18
|
-
url=
|
|
22
|
+
url="https://osint.bevigil.com/api/blacklanternsecurity.com/urls/",
|
|
19
23
|
json={"domain": "blacklanternsecurity.com", "urls": ["https://asdf.blacklanternsecurity.com"]},
|
|
20
24
|
)
|
|
21
25
|
|
|
22
26
|
def check(self, module_test, events):
|
|
23
27
|
assert any(e.data == "asdf.blacklanternsecurity.com" for e in events), "Failed to detect subdomain"
|
|
24
28
|
assert any(e.data == "https://asdf.blacklanternsecurity.com/" for e in events), "Failed to detect url"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestBeVigilMultiKey(TestBeVigil):
|
|
32
|
+
api_keys = ["1234", "4321", "asdf", "fdsa"]
|
|
33
|
+
random.shuffle(api_keys)
|
|
34
|
+
config_overrides = {"modules": {"bevigil": {"api_key": api_keys, "urls": True}}}
|
|
35
|
+
|
|
36
|
+
async def setup_after_prep(self, module_test):
|
|
37
|
+
module_test.httpx_mock.add_response(
|
|
38
|
+
url="https://osint.bevigil.com/api/blacklanternsecurity.com/subdomains/",
|
|
39
|
+
match_headers={"X-Access-Token": "fdsa"},
|
|
40
|
+
json={
|
|
41
|
+
"domain": "blacklanternsecurity.com",
|
|
42
|
+
"subdomains": [
|
|
43
|
+
"asdf.blacklanternsecurity.com",
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
)
|
|
47
|
+
module_test.httpx_mock.add_response(
|
|
48
|
+
match_headers={"X-Access-Token": "asdf"},
|
|
49
|
+
url="https://osint.bevigil.com/api/blacklanternsecurity.com/urls/",
|
|
50
|
+
json={"domain": "blacklanternsecurity.com", "urls": ["https://asdf.blacklanternsecurity.com"]},
|
|
51
|
+
)
|
|
@@ -6,7 +6,8 @@ class TestBinaryEdge(ModuleTestBase):
|
|
|
6
6
|
|
|
7
7
|
async def setup_before_prep(self, module_test):
|
|
8
8
|
module_test.httpx_mock.add_response(
|
|
9
|
-
url=
|
|
9
|
+
url="https://api.binaryedge.io/v2/query/domains/subdomain/blacklanternsecurity.com",
|
|
10
|
+
match_headers={"X-Key": "asdf"},
|
|
10
11
|
json={
|
|
11
12
|
"query": "blacklanternsecurity.com",
|
|
12
13
|
"page": 1,
|
|
@@ -18,7 +19,8 @@ class TestBinaryEdge(ModuleTestBase):
|
|
|
18
19
|
},
|
|
19
20
|
)
|
|
20
21
|
module_test.httpx_mock.add_response(
|
|
21
|
-
url=
|
|
22
|
+
url="https://api.binaryedge.io/v2/user/subscription",
|
|
23
|
+
match_headers={"X-Key": "asdf"},
|
|
22
24
|
json={
|
|
23
25
|
"subscription": {"name": "Free"},
|
|
24
26
|
"end_date": "2023-06-17",
|
|
@@ -82,8 +82,8 @@ class Bucket_Amazon_Base(ModuleTestBase):
|
|
|
82
82
|
if e.type == "FINDING" and str(e.module) == self.module_name:
|
|
83
83
|
url = e.data.get("url", "")
|
|
84
84
|
assert self.random_bucket_2 in url
|
|
85
|
-
assert
|
|
86
|
-
assert
|
|
85
|
+
assert self.random_bucket_1 not in url
|
|
86
|
+
assert self.random_bucket_3 not in url
|
|
87
87
|
# make sure bucket mutations were found
|
|
88
88
|
assert any(
|
|
89
89
|
e.type == "STORAGE_BUCKET"
|
|
@@ -21,7 +21,7 @@ class TestBucket_Azure_NoDup(ModuleTestBase):
|
|
|
21
21
|
|
|
22
22
|
async def setup_before_prep(self, module_test):
|
|
23
23
|
module_test.httpx_mock.add_response(
|
|
24
|
-
url=
|
|
24
|
+
url="https://tesla.blob.core.windows.net/tesla?restype=container",
|
|
25
25
|
text="",
|
|
26
26
|
)
|
|
27
27
|
await module_test.mock_dns(
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from .base import ModuleTestBase
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TestBufferOverrun(ModuleTestBase):
|
|
5
|
+
config_overrides = {"modules": {"bufferoverrun": {"api_key": "asdf", "commercial": False}}}
|
|
6
|
+
|
|
7
|
+
async def setup_before_prep(self, module_test):
|
|
8
|
+
# Mock response for non-commercial API
|
|
9
|
+
module_test.httpx_mock.add_response(
|
|
10
|
+
url="https://tls.bufferover.run/dns?q=.blacklanternsecurity.com",
|
|
11
|
+
match_headers={"x-api-key": "asdf"},
|
|
12
|
+
json={"Results": ["1.2.3.4,example.com,*,*,sub.blacklanternsecurity.com"]},
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def check(self, module_test, events):
|
|
16
|
+
assert any(e.data == "sub.blacklanternsecurity.com" for e in events), "Failed to detect subdomain for free API"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TestBufferOverrunCommercial(ModuleTestBase):
|
|
20
|
+
modules_overrides = ["bufferoverrun"]
|
|
21
|
+
module_name = "bufferoverrun"
|
|
22
|
+
config_overrides = {"modules": {"bufferoverrun": {"api_key": "asdf", "commercial": True}}}
|
|
23
|
+
|
|
24
|
+
async def setup_before_prep(self, module_test):
|
|
25
|
+
# Mock response for commercial API
|
|
26
|
+
module_test.httpx_mock.add_response(
|
|
27
|
+
url="https://bufferover-run-tls.p.rapidapi.com/ipv4/dns?q=.blacklanternsecurity.com",
|
|
28
|
+
match_headers={"x-rapidapi-host": "bufferover-run-tls.p.rapidapi.com", "x-rapidapi-key": "asdf"},
|
|
29
|
+
json={"Results": ["5.6.7.8,blacklanternsecurity.com,*,*,sub.blacklanternsecurity.com"]},
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def check(self, module_test, events):
|
|
33
|
+
assert any(
|
|
34
|
+
e.data == "sub.blacklanternsecurity.com" for e in events
|
|
35
|
+
), "Failed to detect subdomain for commercial API"
|
|
@@ -6,7 +6,7 @@ class TestBuiltWith(ModuleTestBase):
|
|
|
6
6
|
|
|
7
7
|
async def setup_after_prep(self, module_test):
|
|
8
8
|
module_test.httpx_mock.add_response(
|
|
9
|
-
url=
|
|
9
|
+
url="https://api.builtwith.com/v20/api.json?KEY=asdf&LOOKUP=blacklanternsecurity.com&NOMETA=yes&NOATTR=yes&HIDETEXT=yes&HIDEDL=yes",
|
|
10
10
|
json={
|
|
11
11
|
"Results": [
|
|
12
12
|
{
|
|
@@ -91,7 +91,7 @@ class TestBuiltWith(ModuleTestBase):
|
|
|
91
91
|
},
|
|
92
92
|
)
|
|
93
93
|
module_test.httpx_mock.add_response(
|
|
94
|
-
url=
|
|
94
|
+
url="https://api.builtwith.com/redirect1/api.json?KEY=asdf&LOOKUP=blacklanternsecurity.com",
|
|
95
95
|
json={
|
|
96
96
|
"Lookup": "blacklanternsecurity.com",
|
|
97
97
|
"Inbound": [
|
|
@@ -26,7 +26,7 @@ class TestBypass403_collapsethreshold(ModuleTestBase):
|
|
|
26
26
|
async def setup_after_prep(self, module_test):
|
|
27
27
|
respond_args = {"response_data": "alive"}
|
|
28
28
|
|
|
29
|
-
# some of these
|
|
29
|
+
# some of these won't work outside of the module because of the complex logic. This doesn't matter, we just need to get more alerts than the threshold.
|
|
30
30
|
|
|
31
31
|
query_payloads = [
|
|
32
32
|
"%09",
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
|
|
1
3
|
from .base import ModuleTestBase
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
class TestC99(ModuleTestBase):
|
|
7
|
+
module_name = "c99"
|
|
5
8
|
config_overrides = {"modules": {"c99": {"api_key": "asdf"}}}
|
|
6
9
|
|
|
7
10
|
async def setup_before_prep(self, module_test):
|
|
@@ -23,3 +26,126 @@ class TestC99(ModuleTestBase):
|
|
|
23
26
|
|
|
24
27
|
def check(self, module_test, events):
|
|
25
28
|
assert any(e.data == "asdf.blacklanternsecurity.com" for e in events), "Failed to detect subdomain"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestC99AbortThreshold1(TestC99):
|
|
32
|
+
config_overrides = {"modules": {"c99": {"api_key": ["6789", "fdsa", "1234", "4321"]}}}
|
|
33
|
+
|
|
34
|
+
async def setup_before_prep(self, module_test):
|
|
35
|
+
module_test.httpx_mock.add_response(
|
|
36
|
+
url="https://api.c99.nl/randomnumber?key=fdsa&between=1,100&json",
|
|
37
|
+
json={"success": True, "output": 65},
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
self.url_count = {}
|
|
41
|
+
|
|
42
|
+
async def custom_callback(request):
|
|
43
|
+
url = str(request.url)
|
|
44
|
+
try:
|
|
45
|
+
self.url_count[url] += 1
|
|
46
|
+
except KeyError:
|
|
47
|
+
self.url_count[url] = 1
|
|
48
|
+
raise httpx.TimeoutException("timeout")
|
|
49
|
+
|
|
50
|
+
module_test.httpx_mock.add_callback(custom_callback)
|
|
51
|
+
|
|
52
|
+
def check(self, module_test, events):
|
|
53
|
+
assert module_test.module.api_failure_abort_threshold == 13
|
|
54
|
+
assert module_test.module.errored is False
|
|
55
|
+
# assert module_test.module._api_request_failures == 4
|
|
56
|
+
assert module_test.module.api_retries == 4
|
|
57
|
+
assert {e.data for e in events if e.type == "DNS_NAME"} == {"blacklanternsecurity.com"}
|
|
58
|
+
assert self.url_count == {
|
|
59
|
+
"https://api.c99.nl/randomnumber?key=6789&between=1,100&json": 1,
|
|
60
|
+
"https://api.c99.nl/randomnumber?key=4321&between=1,100&json": 1,
|
|
61
|
+
"https://api.c99.nl/randomnumber?key=1234&between=1,100&json": 1,
|
|
62
|
+
"https://api.c99.nl/subdomainfinder?key=fdsa&domain=blacklanternsecurity.com&json": 1,
|
|
63
|
+
"https://api.c99.nl/subdomainfinder?key=6789&domain=blacklanternsecurity.com&json": 1,
|
|
64
|
+
"https://api.c99.nl/subdomainfinder?key=4321&domain=blacklanternsecurity.com&json": 1,
|
|
65
|
+
"https://api.c99.nl/subdomainfinder?key=1234&domain=blacklanternsecurity.com&json": 1,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class TestC99AbortThreshold2(TestC99AbortThreshold1):
|
|
70
|
+
targets = ["blacklanternsecurity.com", "evilcorp.com"]
|
|
71
|
+
|
|
72
|
+
async def setup_before_prep(self, module_test):
|
|
73
|
+
await super().setup_before_prep(module_test)
|
|
74
|
+
await module_test.mock_dns(
|
|
75
|
+
{
|
|
76
|
+
"blacklanternsecurity.com": {"A": ["127.0.0.88"]},
|
|
77
|
+
"evilcorp.com": {"A": ["127.0.0.11"]},
|
|
78
|
+
"evilcorp.net": {"A": ["127.0.0.22"]},
|
|
79
|
+
"evilcorp.co.uk": {"A": ["127.0.0.33"]},
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def check(self, module_test, events):
|
|
84
|
+
assert module_test.module.api_failure_abort_threshold == 13
|
|
85
|
+
assert module_test.module.errored is False
|
|
86
|
+
assert module_test.module._api_request_failures == 8
|
|
87
|
+
assert module_test.module.api_retries == 4
|
|
88
|
+
assert {e.data for e in events if e.type == "DNS_NAME"} == {"blacklanternsecurity.com", "evilcorp.com"}
|
|
89
|
+
assert self.url_count == {
|
|
90
|
+
"https://api.c99.nl/randomnumber?key=6789&between=1,100&json": 1,
|
|
91
|
+
"https://api.c99.nl/randomnumber?key=4321&between=1,100&json": 1,
|
|
92
|
+
"https://api.c99.nl/randomnumber?key=1234&between=1,100&json": 1,
|
|
93
|
+
"https://api.c99.nl/subdomainfinder?key=fdsa&domain=blacklanternsecurity.com&json": 1,
|
|
94
|
+
"https://api.c99.nl/subdomainfinder?key=6789&domain=blacklanternsecurity.com&json": 1,
|
|
95
|
+
"https://api.c99.nl/subdomainfinder?key=4321&domain=blacklanternsecurity.com&json": 1,
|
|
96
|
+
"https://api.c99.nl/subdomainfinder?key=1234&domain=blacklanternsecurity.com&json": 1,
|
|
97
|
+
"https://api.c99.nl/subdomainfinder?key=fdsa&domain=evilcorp.com&json": 1,
|
|
98
|
+
"https://api.c99.nl/subdomainfinder?key=6789&domain=evilcorp.com&json": 1,
|
|
99
|
+
"https://api.c99.nl/subdomainfinder?key=4321&domain=evilcorp.com&json": 1,
|
|
100
|
+
"https://api.c99.nl/subdomainfinder?key=1234&domain=evilcorp.com&json": 1,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class TestC99AbortThreshold3(TestC99AbortThreshold2):
|
|
105
|
+
targets = ["blacklanternsecurity.com", "evilcorp.com", "evilcorp.net"]
|
|
106
|
+
|
|
107
|
+
def check(self, module_test, events):
|
|
108
|
+
assert module_test.module.api_failure_abort_threshold == 13
|
|
109
|
+
assert module_test.module.errored is False
|
|
110
|
+
assert module_test.module._api_request_failures == 12
|
|
111
|
+
assert module_test.module.api_retries == 4
|
|
112
|
+
assert {e.data for e in events if e.type == "DNS_NAME"} == {
|
|
113
|
+
"blacklanternsecurity.com",
|
|
114
|
+
"evilcorp.com",
|
|
115
|
+
"evilcorp.net",
|
|
116
|
+
}
|
|
117
|
+
assert self.url_count == {
|
|
118
|
+
"https://api.c99.nl/randomnumber?key=6789&between=1,100&json": 1,
|
|
119
|
+
"https://api.c99.nl/randomnumber?key=4321&between=1,100&json": 1,
|
|
120
|
+
"https://api.c99.nl/randomnumber?key=1234&between=1,100&json": 1,
|
|
121
|
+
"https://api.c99.nl/subdomainfinder?key=fdsa&domain=blacklanternsecurity.com&json": 1,
|
|
122
|
+
"https://api.c99.nl/subdomainfinder?key=6789&domain=blacklanternsecurity.com&json": 1,
|
|
123
|
+
"https://api.c99.nl/subdomainfinder?key=4321&domain=blacklanternsecurity.com&json": 1,
|
|
124
|
+
"https://api.c99.nl/subdomainfinder?key=1234&domain=blacklanternsecurity.com&json": 1,
|
|
125
|
+
"https://api.c99.nl/subdomainfinder?key=fdsa&domain=evilcorp.com&json": 1,
|
|
126
|
+
"https://api.c99.nl/subdomainfinder?key=6789&domain=evilcorp.com&json": 1,
|
|
127
|
+
"https://api.c99.nl/subdomainfinder?key=4321&domain=evilcorp.com&json": 1,
|
|
128
|
+
"https://api.c99.nl/subdomainfinder?key=1234&domain=evilcorp.com&json": 1,
|
|
129
|
+
"https://api.c99.nl/subdomainfinder?key=fdsa&domain=evilcorp.net&json": 1,
|
|
130
|
+
"https://api.c99.nl/subdomainfinder?key=6789&domain=evilcorp.net&json": 1,
|
|
131
|
+
"https://api.c99.nl/subdomainfinder?key=4321&domain=evilcorp.net&json": 1,
|
|
132
|
+
"https://api.c99.nl/subdomainfinder?key=1234&domain=evilcorp.net&json": 1,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class TestC99AbortThreshold4(TestC99AbortThreshold3):
|
|
137
|
+
targets = ["blacklanternsecurity.com", "evilcorp.com", "evilcorp.net", "evilcorp.co.uk"]
|
|
138
|
+
|
|
139
|
+
def check(self, module_test, events):
|
|
140
|
+
assert module_test.module.api_failure_abort_threshold == 13
|
|
141
|
+
assert module_test.module.errored is True
|
|
142
|
+
assert module_test.module._api_request_failures == 13
|
|
143
|
+
assert module_test.module.api_retries == 4
|
|
144
|
+
assert {e.data for e in events if e.type == "DNS_NAME"} == {
|
|
145
|
+
"blacklanternsecurity.com",
|
|
146
|
+
"evilcorp.com",
|
|
147
|
+
"evilcorp.net",
|
|
148
|
+
"evilcorp.co.uk",
|
|
149
|
+
}
|
|
150
|
+
assert len(self.url_count) == 16
|
|
151
|
+
assert all(v == 1 for v in self.url_count.values())
|