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.

Files changed (270) hide show
  1. bbot/__init__.py +1 -1
  2. bbot/cli.py +3 -7
  3. bbot/core/config/files.py +0 -1
  4. bbot/core/config/logger.py +34 -4
  5. bbot/core/core.py +21 -6
  6. bbot/core/engine.py +9 -8
  7. bbot/core/event/base.py +162 -63
  8. bbot/core/helpers/bloom.py +10 -3
  9. bbot/core/helpers/command.py +9 -8
  10. bbot/core/helpers/depsinstaller/installer.py +89 -32
  11. bbot/core/helpers/depsinstaller/sudo_askpass.py +38 -2
  12. bbot/core/helpers/diff.py +10 -10
  13. bbot/core/helpers/dns/brute.py +18 -14
  14. bbot/core/helpers/dns/dns.py +16 -15
  15. bbot/core/helpers/dns/engine.py +159 -132
  16. bbot/core/helpers/dns/helpers.py +2 -2
  17. bbot/core/helpers/dns/mock.py +26 -8
  18. bbot/core/helpers/files.py +1 -1
  19. bbot/core/helpers/helper.py +7 -4
  20. bbot/core/helpers/interactsh.py +3 -3
  21. bbot/core/helpers/libmagic.py +65 -0
  22. bbot/core/helpers/misc.py +65 -22
  23. bbot/core/helpers/names_generator.py +17 -3
  24. bbot/core/helpers/process.py +0 -20
  25. bbot/core/helpers/regex.py +1 -1
  26. bbot/core/helpers/regexes.py +12 -6
  27. bbot/core/helpers/validators.py +1 -2
  28. bbot/core/helpers/web/client.py +1 -1
  29. bbot/core/helpers/web/engine.py +18 -13
  30. bbot/core/helpers/web/web.py +25 -116
  31. bbot/core/helpers/wordcloud.py +5 -5
  32. bbot/core/modules.py +36 -27
  33. bbot/core/multiprocess.py +58 -0
  34. bbot/core/shared_deps.py +46 -3
  35. bbot/db/sql/models.py +147 -0
  36. bbot/defaults.yml +15 -10
  37. bbot/errors.py +0 -8
  38. bbot/modules/anubisdb.py +2 -2
  39. bbot/modules/apkpure.py +63 -0
  40. bbot/modules/azure_tenant.py +2 -2
  41. bbot/modules/baddns.py +35 -19
  42. bbot/modules/baddns_direct.py +92 -0
  43. bbot/modules/baddns_zone.py +3 -8
  44. bbot/modules/badsecrets.py +4 -3
  45. bbot/modules/base.py +195 -51
  46. bbot/modules/bevigil.py +7 -7
  47. bbot/modules/binaryedge.py +7 -4
  48. bbot/modules/bufferoverrun.py +47 -0
  49. bbot/modules/builtwith.py +6 -10
  50. bbot/modules/bypass403.py +5 -5
  51. bbot/modules/c99.py +10 -7
  52. bbot/modules/censys.py +9 -13
  53. bbot/modules/certspotter.py +5 -3
  54. bbot/modules/chaos.py +9 -7
  55. bbot/modules/code_repository.py +1 -0
  56. bbot/modules/columbus.py +3 -3
  57. bbot/modules/crt.py +5 -3
  58. bbot/modules/deadly/dastardly.py +1 -1
  59. bbot/modules/deadly/ffuf.py +9 -9
  60. bbot/modules/deadly/nuclei.py +3 -3
  61. bbot/modules/deadly/vhost.py +4 -3
  62. bbot/modules/dehashed.py +1 -1
  63. bbot/modules/digitorus.py +1 -1
  64. bbot/modules/dnsbimi.py +145 -0
  65. bbot/modules/dnscaa.py +3 -3
  66. bbot/modules/dnsdumpster.py +4 -4
  67. bbot/modules/dnstlsrpt.py +144 -0
  68. bbot/modules/docker_pull.py +7 -5
  69. bbot/modules/dockerhub.py +2 -2
  70. bbot/modules/dotnetnuke.py +18 -19
  71. bbot/modules/emailformat.py +1 -1
  72. bbot/modules/extractous.py +122 -0
  73. bbot/modules/filedownload.py +9 -7
  74. bbot/modules/fullhunt.py +7 -4
  75. bbot/modules/generic_ssrf.py +5 -5
  76. bbot/modules/github_codesearch.py +3 -2
  77. bbot/modules/github_org.py +4 -4
  78. bbot/modules/github_workflows.py +4 -4
  79. bbot/modules/gitlab.py +2 -5
  80. bbot/modules/google_playstore.py +93 -0
  81. bbot/modules/gowitness.py +48 -50
  82. bbot/modules/hackertarget.py +5 -3
  83. bbot/modules/host_header.py +5 -5
  84. bbot/modules/httpx.py +1 -4
  85. bbot/modules/hunterio.py +3 -9
  86. bbot/modules/iis_shortnames.py +19 -30
  87. bbot/modules/internal/cloudcheck.py +27 -12
  88. bbot/modules/internal/dnsresolve.py +250 -276
  89. bbot/modules/internal/excavate.py +100 -64
  90. bbot/modules/internal/speculate.py +42 -33
  91. bbot/modules/internetdb.py +4 -2
  92. bbot/modules/ip2location.py +3 -5
  93. bbot/modules/ipneighbor.py +1 -1
  94. bbot/modules/ipstack.py +3 -8
  95. bbot/modules/jadx.py +87 -0
  96. bbot/modules/leakix.py +11 -10
  97. bbot/modules/myssl.py +2 -2
  98. bbot/modules/newsletters.py +2 -2
  99. bbot/modules/otx.py +5 -3
  100. bbot/modules/output/asset_inventory.py +7 -7
  101. bbot/modules/output/base.py +1 -1
  102. bbot/modules/output/csv.py +1 -2
  103. bbot/modules/output/http.py +20 -14
  104. bbot/modules/output/mysql.py +51 -0
  105. bbot/modules/output/neo4j.py +7 -2
  106. bbot/modules/output/postgres.py +49 -0
  107. bbot/modules/output/slack.py +0 -1
  108. bbot/modules/output/sqlite.py +29 -0
  109. bbot/modules/output/stdout.py +2 -2
  110. bbot/modules/output/teams.py +107 -6
  111. bbot/modules/paramminer_headers.py +5 -8
  112. bbot/modules/passivetotal.py +13 -13
  113. bbot/modules/portscan.py +32 -6
  114. bbot/modules/postman.py +50 -126
  115. bbot/modules/postman_download.py +220 -0
  116. bbot/modules/rapiddns.py +3 -8
  117. bbot/modules/report/asn.py +11 -11
  118. bbot/modules/robots.py +3 -3
  119. bbot/modules/securitytrails.py +7 -10
  120. bbot/modules/securitytxt.py +128 -0
  121. bbot/modules/shodan_dns.py +7 -9
  122. bbot/modules/sitedossier.py +1 -1
  123. bbot/modules/skymem.py +2 -2
  124. bbot/modules/social.py +2 -1
  125. bbot/modules/subdomaincenter.py +1 -1
  126. bbot/modules/subdomainradar.py +160 -0
  127. bbot/modules/telerik.py +8 -8
  128. bbot/modules/templates/bucket.py +1 -1
  129. bbot/modules/templates/github.py +22 -14
  130. bbot/modules/templates/postman.py +21 -0
  131. bbot/modules/templates/shodan.py +14 -13
  132. bbot/modules/templates/sql.py +95 -0
  133. bbot/modules/templates/subdomain_enum.py +53 -17
  134. bbot/modules/templates/webhook.py +2 -4
  135. bbot/modules/trickest.py +8 -37
  136. bbot/modules/trufflehog.py +18 -3
  137. bbot/modules/url_manipulation.py +3 -3
  138. bbot/modules/urlscan.py +1 -1
  139. bbot/modules/viewdns.py +1 -1
  140. bbot/modules/virustotal.py +8 -30
  141. bbot/modules/wafw00f.py +1 -1
  142. bbot/modules/wayback.py +1 -1
  143. bbot/modules/wpscan.py +17 -11
  144. bbot/modules/zoomeye.py +11 -6
  145. bbot/presets/baddns-thorough.yml +12 -0
  146. bbot/presets/fast.yml +16 -0
  147. bbot/presets/kitchen-sink.yml +1 -0
  148. bbot/presets/spider.yml +4 -0
  149. bbot/presets/subdomain-enum.yml +7 -7
  150. bbot/scanner/manager.py +5 -16
  151. bbot/scanner/preset/args.py +44 -26
  152. bbot/scanner/preset/environ.py +7 -2
  153. bbot/scanner/preset/path.py +7 -4
  154. bbot/scanner/preset/preset.py +36 -23
  155. bbot/scanner/scanner.py +176 -63
  156. bbot/scanner/target.py +236 -434
  157. bbot/scripts/docs.py +1 -1
  158. bbot/test/bbot_fixtures.py +22 -3
  159. bbot/test/conftest.py +132 -100
  160. bbot/test/fastapi_test.py +17 -0
  161. bbot/test/owasp_mastg.apk +0 -0
  162. bbot/test/run_tests.sh +4 -4
  163. bbot/test/test.conf +2 -0
  164. bbot/test/test_step_1/test_bbot_fastapi.py +82 -0
  165. bbot/test/test_step_1/test_bloom_filter.py +2 -0
  166. bbot/test/test_step_1/test_cli.py +138 -64
  167. bbot/test/test_step_1/test_dns.py +392 -70
  168. bbot/test/test_step_1/test_engine.py +17 -17
  169. bbot/test/test_step_1/test_events.py +203 -37
  170. bbot/test/test_step_1/test_helpers.py +64 -28
  171. bbot/test/test_step_1/test_manager_deduplication.py +1 -1
  172. bbot/test/test_step_1/test_manager_scope_accuracy.py +336 -338
  173. bbot/test/test_step_1/test_modules_basic.py +69 -71
  174. bbot/test/test_step_1/test_presets.py +184 -96
  175. bbot/test/test_step_1/test_python_api.py +7 -2
  176. bbot/test/test_step_1/test_regexes.py +35 -5
  177. bbot/test/test_step_1/test_scan.py +39 -5
  178. bbot/test/test_step_1/test_scope.py +5 -4
  179. bbot/test/test_step_1/test_target.py +243 -145
  180. bbot/test/test_step_1/test_web.py +48 -10
  181. bbot/test/test_step_2/module_tests/base.py +17 -20
  182. bbot/test/test_step_2/module_tests/test_module_anubisdb.py +1 -1
  183. bbot/test/test_step_2/module_tests/test_module_apkpure.py +71 -0
  184. bbot/test/test_step_2/module_tests/test_module_asset_inventory.py +0 -1
  185. bbot/test/test_step_2/module_tests/test_module_azure_realm.py +1 -1
  186. bbot/test/test_step_2/module_tests/test_module_baddns.py +6 -6
  187. bbot/test/test_step_2/module_tests/test_module_baddns_direct.py +62 -0
  188. bbot/test/test_step_2/module_tests/test_module_bevigil.py +29 -2
  189. bbot/test/test_step_2/module_tests/test_module_binaryedge.py +4 -2
  190. bbot/test/test_step_2/module_tests/test_module_bucket_amazon.py +2 -2
  191. bbot/test/test_step_2/module_tests/test_module_bucket_azure.py +1 -1
  192. bbot/test/test_step_2/module_tests/test_module_bufferoverrun.py +35 -0
  193. bbot/test/test_step_2/module_tests/test_module_builtwith.py +2 -2
  194. bbot/test/test_step_2/module_tests/test_module_bypass403.py +1 -1
  195. bbot/test/test_step_2/module_tests/test_module_c99.py +126 -0
  196. bbot/test/test_step_2/module_tests/test_module_censys.py +4 -1
  197. bbot/test/test_step_2/module_tests/test_module_cloudcheck.py +4 -0
  198. bbot/test/test_step_2/module_tests/test_module_code_repository.py +11 -1
  199. bbot/test/test_step_2/module_tests/test_module_columbus.py +1 -1
  200. bbot/test/test_step_2/module_tests/test_module_credshed.py +3 -3
  201. bbot/test/test_step_2/module_tests/test_module_dastardly.py +2 -1
  202. bbot/test/test_step_2/module_tests/test_module_dehashed.py +2 -2
  203. bbot/test/test_step_2/module_tests/test_module_digitorus.py +1 -1
  204. bbot/test/test_step_2/module_tests/test_module_discord.py +1 -1
  205. bbot/test/test_step_2/module_tests/test_module_dnsbimi.py +103 -0
  206. bbot/test/test_step_2/module_tests/test_module_dnsbrute.py +9 -10
  207. bbot/test/test_step_2/module_tests/test_module_dnsbrute_mutations.py +1 -2
  208. bbot/test/test_step_2/module_tests/test_module_dnscommonsrv.py +1 -2
  209. bbot/test/test_step_2/module_tests/test_module_dnsdumpster.py +4 -4
  210. bbot/test/test_step_2/module_tests/test_module_dnstlsrpt.py +64 -0
  211. bbot/test/test_step_2/module_tests/test_module_dotnetnuke.py +0 -8
  212. bbot/test/test_step_2/module_tests/test_module_excavate.py +17 -37
  213. bbot/test/test_step_2/module_tests/test_module_extractous.py +54 -0
  214. bbot/test/test_step_2/module_tests/test_module_ffuf_shortnames.py +1 -1
  215. bbot/test/test_step_2/module_tests/test_module_filedownload.py +14 -14
  216. bbot/test/test_step_2/module_tests/test_module_git_clone.py +2 -2
  217. bbot/test/test_step_2/module_tests/test_module_github_org.py +19 -8
  218. bbot/test/test_step_2/module_tests/test_module_github_workflows.py +1 -1
  219. bbot/test/test_step_2/module_tests/test_module_gitlab.py +9 -4
  220. bbot/test/test_step_2/module_tests/test_module_google_playstore.py +83 -0
  221. bbot/test/test_step_2/module_tests/test_module_gowitness.py +4 -4
  222. bbot/test/test_step_2/module_tests/test_module_host_header.py +1 -1
  223. bbot/test/test_step_2/module_tests/test_module_http.py +4 -4
  224. bbot/test/test_step_2/module_tests/test_module_httpx.py +10 -8
  225. bbot/test/test_step_2/module_tests/test_module_hunterio.py +68 -4
  226. bbot/test/test_step_2/module_tests/test_module_jadx.py +55 -0
  227. bbot/test/test_step_2/module_tests/test_module_json.py +24 -11
  228. bbot/test/test_step_2/module_tests/test_module_leakix.py +7 -3
  229. bbot/test/test_step_2/module_tests/test_module_mysql.py +76 -0
  230. bbot/test/test_step_2/module_tests/test_module_myssl.py +1 -1
  231. bbot/test/test_step_2/module_tests/test_module_neo4j.py +1 -1
  232. bbot/test/test_step_2/module_tests/test_module_newsletters.py +6 -6
  233. bbot/test/test_step_2/module_tests/test_module_ntlm.py +7 -7
  234. bbot/test/test_step_2/module_tests/test_module_oauth.py +1 -1
  235. bbot/test/test_step_2/module_tests/test_module_otx.py +1 -1
  236. bbot/test/test_step_2/module_tests/test_module_paramminer_cookies.py +1 -2
  237. bbot/test/test_step_2/module_tests/test_module_paramminer_getparams.py +0 -6
  238. bbot/test/test_step_2/module_tests/test_module_paramminer_headers.py +2 -9
  239. bbot/test/test_step_2/module_tests/test_module_passivetotal.py +3 -1
  240. bbot/test/test_step_2/module_tests/test_module_portscan.py +9 -8
  241. bbot/test/test_step_2/module_tests/test_module_postgres.py +74 -0
  242. bbot/test/test_step_2/module_tests/test_module_postman.py +84 -253
  243. bbot/test/test_step_2/module_tests/test_module_postman_download.py +439 -0
  244. bbot/test/test_step_2/module_tests/test_module_rapiddns.py +93 -1
  245. bbot/test/test_step_2/module_tests/test_module_securitytxt.py +50 -0
  246. bbot/test/test_step_2/module_tests/test_module_shodan_dns.py +20 -1
  247. bbot/test/test_step_2/module_tests/test_module_sitedossier.py +2 -2
  248. bbot/test/test_step_2/module_tests/test_module_smuggler.py +1 -1
  249. bbot/test/test_step_2/module_tests/test_module_social.py +11 -1
  250. bbot/test/test_step_2/module_tests/test_module_speculate.py +2 -6
  251. bbot/test/test_step_2/module_tests/test_module_splunk.py +4 -4
  252. bbot/test/test_step_2/module_tests/test_module_sqlite.py +18 -0
  253. bbot/test/test_step_2/module_tests/test_module_sslcert.py +1 -1
  254. bbot/test/test_step_2/module_tests/test_module_stdout.py +5 -3
  255. bbot/test/test_step_2/module_tests/test_module_subdomaincenter.py +1 -1
  256. bbot/test/test_step_2/module_tests/test_module_subdomainradar.py +208 -0
  257. bbot/test/test_step_2/module_tests/test_module_subdomains.py +1 -1
  258. bbot/test/test_step_2/module_tests/test_module_teams.py +8 -6
  259. bbot/test/test_step_2/module_tests/test_module_telerik.py +1 -1
  260. bbot/test/test_step_2/module_tests/test_module_trufflehog.py +317 -11
  261. bbot/test/test_step_2/module_tests/test_module_wayback.py +1 -1
  262. bbot/test/test_step_2/template_tests/test_template_subdomain_enum.py +135 -0
  263. {bbot-2.0.1.4654rc0.dist-info → bbot-2.3.0.5397rc0.dist-info}/METADATA +48 -18
  264. bbot-2.3.0.5397rc0.dist-info/RECORD +421 -0
  265. {bbot-2.0.1.4654rc0.dist-info → bbot-2.3.0.5397rc0.dist-info}/WHEEL +1 -1
  266. bbot/modules/unstructured.py +0 -163
  267. bbot/test/test_step_2/module_tests/test_module_unstructured.py +0 -102
  268. bbot-2.0.1.4654rc0.dist-info/RECORD +0 -385
  269. {bbot-2.0.1.4654rc0.dist-info → bbot-2.3.0.5397rc0.dist-info}/LICENSE +0 -0
  270. {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 not "2606:4700:4700::1111" in result
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), (result, errors) in results:
42
- result = extract_targets(result)
43
- _results = [r[1] for r in result]
44
- if query == "one.one.one.one" and "1.1.1.1" in _results:
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 _results:
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 not ("AAAA", "2606:4700:4700::1111") in responses
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 not ("A", "1.1.1.1") in responses
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
- assert len(await dnsengine.resolve("google.com", type=rdtype)) > 0
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([x in batch_results["one.one.one.one"] for x in ("1.1.1.1", "1.0.0.1")])
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
- batch_results = [(response.to_text(), response.rdtype.name) for query, (response, errors) in batch_results]
119
- assert len(batch_results) == 3
120
- assert any(answer == "1.0.0.1" and rdtype == "A" for answer, rdtype in batch_results)
121
- assert any(answer == "one.one.one.one." and rdtype == "PTR" for answer, rdtype in batch_results)
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(f"1.1.1.1:PTR") not in dnsengine._dns_cache
126
- assert hash(f"one.one.one.one:A") not in dnsengine._dns_cache
127
- assert hash(f"one.one.one.one:AAAA") not in dnsengine._dns_cache
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(f"1.1.1.1:PTR") not in dnsengine._dns_cache
131
- assert hash(f"one.one.one.one:A") not in dnsengine._dns_cache
132
- assert hash(f"one.one.one.one:AAAA") not in dnsengine._dns_cache
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(f"1.1.1.1:PTR") in dnsengine._dns_cache
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(f"one.one.one.one:A") in dnsengine._dns_cache
138
- assert not hash(f"one.one.one.one:AAAA") in dnsengine._dns_cache
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(f"one.one.one.one:AAAA") in dnsengine._dns_cache
142
- assert not hash(f"one.one.one.one:A") in dnsengine._dns_cache
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 not "a-record" in resolved_hosts_event2.tags
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
- # wildcards
192
- wildcard_domains = await dnsengine.is_wildcard_domain("asdf.github.io")
193
- assert hash("github.io") in dnsengine._wildcard_cache
194
- assert hash("asdf.github.io") in dnsengine._wildcard_cache
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
- wildcard_rdtypes = await dnsengine.is_wildcard("blacklanternsecurity.github.io")
202
- assert "A" in wildcard_rdtypes
203
- assert "SRV" not in wildcard_rdtypes
204
- assert wildcard_rdtypes["A"] == (True, "github.io")
205
- assert hash("github.io") in dnsengine._wildcard_cache
206
- assert len(dnsengine._wildcard_cache[hash("github.io")]) > 0
207
- dnsengine._wildcard_cache.clear()
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
- wildcard_rdtypes = await dnsengine.is_wildcard("asdf.asdf.asdf.github.io")
210
- assert "A" in wildcard_rdtypes
211
- assert "SRV" not in wildcard_rdtypes
212
- assert wildcard_rdtypes["A"] == (True, "github.io")
213
- assert hash("github.io") in dnsengine._wildcard_cache
214
- assert not hash("asdf.github.io") in dnsengine._wildcard_cache
215
- assert not hash("asdf.asdf.github.io") in dnsengine._wildcard_cache
216
- assert not hash("asdf.asdf.asdf.github.io") in dnsengine._wildcard_cache
217
- assert len(dnsengine._wildcard_cache[hash("github.io")]) > 0
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) == 3
274
- assert 1 == len([e for e in events if e.type == "SCAN"])
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) == 3
320
- assert 1 == len([e for e in events if e.type == "SCAN"])
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 ouptut
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) == 5
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
- def test_dns_helpers():
470
- assert service_record("") == False
471
- assert service_record("localhost") == False
472
- assert service_record("www.example.com") == False
473
- assert service_record("www.example.com", "SRV") == True
474
- assert service_record("_custom._service.example.com", "SRV") == True
475
- assert service_record("_custom._service.example.com", "A") == False
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) == True
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 == False
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 == True
92
- assert yield_errored == False
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 == False
103
- assert yield_errored == True
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 == True
117
- assert return_finished == False
118
- assert return_cancelled == True
119
- assert return_errored == False
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 == True
132
- assert return_finished == True
133
- assert return_cancelled == False
134
- assert return_errored == False
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 == True
144
- assert return_finished == False
145
- assert return_cancelled == False
146
- assert return_errored == True
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()