bbot 2.1.2.5223rc0__py3-none-any.whl → 2.1.2.5232rc0__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 CHANGED
@@ -1,4 +1,4 @@
1
1
  # version placeholder (replaced by poetry-dynamic-versioning)
2
- __version__ = "v2.1.2.5223rc"
2
+ __version__ = "v2.1.2.5232rc"
3
3
 
4
4
  from .scanner import Scanner, Preset
@@ -0,0 +1,145 @@
1
+ # bimi.py
2
+ #
3
+ # Checks for and parses common BIMI DNS TXT records, e.g. default._bimi.target.domain
4
+ #
5
+ # Example TXT record: "v=BIMI1; l=https://example.com/brand/logo.svg; a=https://example.com/brand/certificate.pem"
6
+ #
7
+ # BIMI records may contain a link to an SVG format brand authorised image, which may be useful for:
8
+ # 1. Sub-domain or otherwise unknown content hosting locations
9
+ # 2. Brand impersonation
10
+ # 3. May not be formatted/stripped of metadata correctly leading to some (low value probably) information exposure
11
+ #
12
+ # BIMI records may also contain a link to a PEM format X.509 VMC certificate, which may be similarly useful.
13
+ #
14
+ # We simply extract any URL's as URL_UNVERIFIED, no further parsing or download is done by this module in order to remain passive.
15
+ #
16
+ # The domain portion of any URL's is also passively checked and added as appropriate, for additional inspection by other modules.
17
+ #
18
+ # Files may be downloaded by other modules which respond to URL_UNVERIFIED events, if you have configured bbot to do so.
19
+ #
20
+ # NOTE: .svg file extensions are filtered from inclusion by default, modify "url_extension_blacklist" appropriately if you want the .svg image to be considered for download.
21
+ #
22
+ # NOTE: use the "filedownload" module if you to download .svg and .pem files. .pem will be downloaded by defaut, .svg will require a customised configuration for that module.
23
+ #
24
+ # The domain portion of any URL_UNVERIFIED's will be extracted by the various internal modules if .svg is not filtered.
25
+ #
26
+
27
+ from bbot.modules.base import BaseModule
28
+ from bbot.core.helpers.dns.helpers import service_record
29
+
30
+ import re
31
+
32
+ # Handle "v=BIMI1; l=; a=;" == RFC conformant explicit declination to publish, e.g. useful on a sub-domain if you don't want the sub-domain to have a BIMI logo, yet your registered domain does?
33
+ # Handle "v=BIMI1; l=; a=" == RFC non-conformant explicit declination to publish
34
+ # Handle "v=BIMI1; l=;" == RFC non-conformant explicit declination to publish
35
+ # Handle "v=BIMI1; l=" == RFC non-conformant explicit declination to publish
36
+ # Handle "v=BIMI1;" == RFC non-conformant explicit declination to publish
37
+ # Handle "v=BIMI1" == RFC non-conformant explicit declination to publish
38
+ # Handle "v=BIMI1;l=https://bimi.entrust.net/example.com/logo.svg;"
39
+ # Handle "v=BIMI1; l=https://bimi.entrust.net/example.com/logo.svg;"
40
+ # Handle "v=BIMI1;l=https://bimi.entrust.net/example.com/logo.svg;a=https://bimi.entrust.net/example.com/certchain.pem"
41
+ # Handle "v=BIMI1; l=https://bimi.entrust.net/example.com/logo.svg;a=https://bimi.entrust.net/example.com/certchain.pem;"
42
+ _bimi_regex = r"^v=(?P<v>BIMI1);* *(l=(?P<l>https*://[^;]*|)|);*( *a=((?P<a>https://[^;]*|)|);*)*$"
43
+ bimi_regex = re.compile(_bimi_regex, re.I)
44
+
45
+
46
+ class dnsbimi(BaseModule):
47
+ watched_events = ["DNS_NAME"]
48
+ produced_events = ["URL_UNVERIFIED", "RAW_DNS_RECORD"]
49
+ flags = ["subdomain-enum", "cloud-enum", "passive", "safe"]
50
+ meta = {
51
+ "description": "Check DNS_NAME's for BIMI records to find image and certificate hosting URL's",
52
+ "author": "@colin-stubbs",
53
+ "created_date": "2024-11-15",
54
+ }
55
+ options = {
56
+ "emit_raw_dns_records": False,
57
+ "emit_urls": True,
58
+ "selectors": "default,email,mail,bimi",
59
+ }
60
+ options_desc = {
61
+ "emit_raw_dns_records": "Emit RAW_DNS_RECORD events",
62
+ "emit_urls": "Emit URL_UNVERIFIED events",
63
+ "selectors": "CSV list of BIMI selectors to check",
64
+ }
65
+
66
+ async def setup(self):
67
+ self.emit_raw_dns_records = self.config.get("emit_raw_dns_records", False)
68
+ self.emit_urls = self.config.get("emit_urls", True)
69
+ self._selectors = self.config.get("selectors", "").replace(", ", ",").split(",")
70
+
71
+ return await super().setup()
72
+
73
+ def _incoming_dedup_hash(self, event):
74
+ # dedupe by parent
75
+ parent_domain = self.helpers.parent_domain(event.data)
76
+ return hash(parent_domain), "already processed parent domain"
77
+
78
+ async def filter_event(self, event):
79
+ if "_wildcard" in str(event.host).split("."):
80
+ return False, "event is wildcard"
81
+
82
+ # there's no value in inspecting service records
83
+ if service_record(event.host) == True:
84
+ return False, "service record detected"
85
+
86
+ return True
87
+
88
+ async def inspectBIMI(self, event, domain):
89
+ parent_domain = self.helpers.parent_domain(event.data)
90
+ rdtype = "TXT"
91
+
92
+ for selector in self._selectors:
93
+ tags = ["bimi-record", f"bimi-{selector}"]
94
+ hostname = f"{selector}._bimi.{parent_domain}"
95
+
96
+ r = await self.helpers.resolve_raw(hostname, type=rdtype)
97
+
98
+ if r:
99
+ raw_results, errors = r
100
+
101
+ for answer in raw_results:
102
+ if self.emit_raw_dns_records:
103
+ await self.emit_event(
104
+ {
105
+ "host": hostname,
106
+ "type": rdtype,
107
+ "answer": answer.to_text(),
108
+ },
109
+ "RAW_DNS_RECORD",
110
+ parent=event,
111
+ tags=tags.append(f"{rdtype.lower()}-record"),
112
+ context=f"{rdtype} lookup on {hostname} produced {{event.type}}",
113
+ )
114
+
115
+ # we need to strip surrounding quotes and whitespace, as well as fix TXT data that may have been split across two different rdata's
116
+ # e.g. we will get a single string, but within that string we may have two parts such as:
117
+ # answer = '"part 1 that was really long" "part 2 that did not fit in part 1"'
118
+ s = answer.to_text().strip('"').strip().replace('" "', "")
119
+
120
+ bimi_match = bimi_regex.search(s)
121
+
122
+ if bimi_match and bimi_match.group("v") and "bimi" in bimi_match.group("v").lower():
123
+ if bimi_match.group("l") and bimi_match.group("l") != "":
124
+ if self.emit_urls:
125
+ await self.emit_event(
126
+ bimi_match.group("l"),
127
+ "URL_UNVERIFIED",
128
+ parent=event,
129
+ tags=tags.append("bimi-location"),
130
+ )
131
+
132
+ if bimi_match.group("a") and bimi_match.group("a") != "":
133
+ if self.emit_urls:
134
+ await self.emit_event(
135
+ bimi_match.group("a"),
136
+ "URL_UNVERIFIED",
137
+ parent=event,
138
+ tags=tags.append("bimi-authority"),
139
+ )
140
+
141
+ async def handle_event(self, event):
142
+ await self.inspectBIMI(event, event.host)
143
+
144
+
145
+ # EOF
@@ -0,0 +1,103 @@
1
+ from .base import ModuleTestBase
2
+
3
+ raw_bimi_txt_default = (
4
+ '"v=BIMI1;l=https://bimi.test.localdomain/logo.svg; a=https://bimi.test.localdomain/certificate.pem"'
5
+ )
6
+ raw_bimi_txt_nondefault = '"v=BIMI1; l=https://nondefault.thirdparty.tld/brand/logo.svg;a=https://nondefault.thirdparty.tld/brand/certificate.pem;"'
7
+
8
+
9
+ class TestBIMI(ModuleTestBase):
10
+ targets = ["test.localdomain"]
11
+ modules_overrides = ["dnsbimi", "speculate"]
12
+ config_overrides = {
13
+ "modules": {"dnsbimi": {"emit_raw_dns_records": True, "selectors": "default,nondefault"}},
14
+ }
15
+
16
+ async def setup_after_prep(self, module_test):
17
+ await module_test.mock_dns(
18
+ {
19
+ "test.localdomain": {
20
+ "A": ["127.0.0.11"],
21
+ },
22
+ "bimi.test.localdomain": {
23
+ "A": ["127.0.0.22"],
24
+ },
25
+ "_bimi.test.localdomain": {
26
+ "A": ["127.0.0.33"],
27
+ },
28
+ "default._bimi.test.localdomain": {
29
+ "A": ["127.0.0.44"],
30
+ "TXT": [raw_bimi_txt_default],
31
+ },
32
+ "nondefault._bimi.test.localdomain": {
33
+ "A": ["127.0.0.44"],
34
+ "TXT": [raw_bimi_txt_nondefault],
35
+ },
36
+ "_bimi.default._bimi.test.localdomain": {
37
+ "A": ["127.0.0.44"],
38
+ "TXT": [raw_bimi_txt_default],
39
+ },
40
+ "_bimi.nondefault._bimi.test.localdomain": {
41
+ "A": ["127.0.0.44"],
42
+ "TXT": [raw_bimi_txt_default],
43
+ },
44
+ "default._bimi.default._bimi.test.localdomain": {
45
+ "A": ["127.0.0.44"],
46
+ "TXT": [raw_bimi_txt_default],
47
+ },
48
+ "nondefault._bimi.nondefault._bimi.test.localdomain": {
49
+ "A": ["127.0.0.44"],
50
+ "TXT": [raw_bimi_txt_nondefault],
51
+ },
52
+ }
53
+ )
54
+
55
+ def check(self, module_test, events):
56
+ assert any(
57
+ e.type == "RAW_DNS_RECORD"
58
+ and e.data["host"] == "default._bimi.test.localdomain"
59
+ and e.data["type"] == "TXT"
60
+ and e.data["answer"] == raw_bimi_txt_default
61
+ for e in events
62
+ ), "Failed to emit RAW_DNS_RECORD"
63
+ assert any(
64
+ e.type == "RAW_DNS_RECORD"
65
+ and e.data["host"] == "nondefault._bimi.test.localdomain"
66
+ and e.data["type"] == "TXT"
67
+ and e.data["answer"] == raw_bimi_txt_nondefault
68
+ for e in events
69
+ ), "Failed to emit RAW_DNS_RECORD"
70
+
71
+ assert any(
72
+ e.type == "DNS_NAME" and e.data == "bimi.test.localdomain" for e in events
73
+ ), "Failed to emit DNS_NAME"
74
+
75
+ # This should be filtered by a default BBOT configuration
76
+ assert not any(str(e.data) == "https://nondefault.thirdparty.tld/brand/logo.svg" for e in events)
77
+
78
+ # This should not be filtered by a default BBOT configuration
79
+ assert any(
80
+ e.type == "URL_UNVERIFIED" and e.data == "https://bimi.test.localdomain/certificate.pem" for e in events
81
+ ), "Failed to emit URL_UNVERIFIED"
82
+
83
+ # These should be filtered simply due to distance
84
+ assert not any(str(e.data) == "https://nondefault.thirdparty.tld/brand/logo.svg" for e in events)
85
+ assert not any(str(e.data) == "https://nondefault.thirdparty.tld/certificate.pem" for e in events)
86
+
87
+ # These should have been filtered via filter_event()
88
+ assert not any(
89
+ e.type == "RAW_DNS_RECORD" and e.data["host"] == "default._bimi.default._bimi.test.localdomain"
90
+ for e in events
91
+ ), "Unwanted recursion occurring"
92
+ assert not any(
93
+ e.type == "RAW_DNS_RECORD" and e.data["host"] == "nondefault._bimi.nondefault._bimi.test.localdomain"
94
+ for e in events
95
+ ), "Unwanted recursion occurring"
96
+ assert not any(
97
+ e.type == "RAW_DNS_RECORD" and e.data["host"] == "nondefault._bimi.default._bimi.test.localdomain"
98
+ for e in events
99
+ ), "Unwanted recursion occurring"
100
+ assert not any(
101
+ e.type == "RAW_DNS_RECORD" and e.data["host"] == "default._bimi.nondefault._bimi.test.localdomain"
102
+ for e in events
103
+ ), "Unwanted recursion occurring"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bbot
3
- Version: 2.1.2.5223rc0
3
+ Version: 2.1.2.5232rc0
4
4
  Summary: OSINT automation for hackers.
5
5
  Home-page: https://github.com/blacklanternsecurity/bbot
6
6
  License: GPL-3.0
@@ -1,4 +1,4 @@
1
- bbot/__init__.py,sha256=aekooHcHziO6lPgvwep-hyBz_rH9rrpnNKqtzZdp7Qg,130
1
+ bbot/__init__.py,sha256=YSTPQiPA_XqIwvnL1uYS3Icpqeb_ARNyplI6Pk4f37Y,130
2
2
  bbot/cli.py,sha256=7S3a4eB-Dl8yodc5WC-927Z30CNlLl9EXimGvIVypJo,10434
3
3
  bbot/core/__init__.py,sha256=l255GJE_DvUnWvrRb0J5lG-iMztJ8zVvoweDOfegGtI,46
4
4
  bbot/core/config/__init__.py,sha256=zYNw2Me6tsEr8hOOkLb4BQ97GB7Kis2k--G81S8vofU,342
@@ -86,6 +86,7 @@ bbot/modules/deadly/nuclei.py,sha256=fH6oayOhPyd3z0edOvluef3K64FQ8JuQCcb_729u73M
86
86
  bbot/modules/deadly/vhost.py,sha256=lHKK5Gn9cOSeJT-8XvNnOAG97cB_zXJE1_j07KLKTqI,5462
87
87
  bbot/modules/dehashed.py,sha256=enDarOzlY84R4_ctp2fLVNLmjocaCh1j1x8nIKwEdHY,5064
88
88
  bbot/modules/digitorus.py,sha256=nZIuJhDhX152yP_bDH0BFTswHYdm1YVDYN56Sdm0u7k,1021
89
+ bbot/modules/dnsbimi.py,sha256=vuyZJGTv7gkpqpFXrGcfdeHanR96ukSRDS3CpvGeOMI,6920
89
90
  bbot/modules/dnsbrute.py,sha256=Y2bSbG2IcwIJID1FSQ6Qe9fdpWwG7GIO-wVQw7MdQFM,2439
90
91
  bbot/modules/dnsbrute_mutations.py,sha256=bOJidK_oKZe87u8e9t0mEFnyuBi93UiNsQvpZYvhzZg,6939
91
92
  bbot/modules/dnscaa.py,sha256=TC7uWps0xBlRXCQHxwfIORKDsojnOGXUeIWCOBvlMl8,4981
@@ -295,6 +296,7 @@ bbot/test/test_step_2/module_tests/test_module_dastardly.py,sha256=rQ7WerNqZxxxV
295
296
  bbot/test/test_step_2/module_tests/test_module_dehashed.py,sha256=YVsTEFEPchahDT7q_8BofMsH4tYQlN-G1QtAxq6YUn0,3626
296
297
  bbot/test/test_step_2/module_tests/test_module_digitorus.py,sha256=81mNwDb4WLUibstUSD8TowSJB3B5DBneS2LWimie9y4,1613
297
298
  bbot/test/test_step_2/module_tests/test_module_discord.py,sha256=Z66fGb-kkdZTQfUh6WZiM35Ad-gDyvwxlA7mUUB2vnQ,1838
299
+ bbot/test/test_step_2/module_tests/test_module_dnsbimi.py,sha256=Ag24Bcm4MFxgUwvXXubPGE6mLMBsJGwfhCQ6paK-rRU,4391
298
300
  bbot/test/test_step_2/module_tests/test_module_dnsbrute.py,sha256=33j93ayz7Ul4Fvg7plf6H4LKcUgFuEH4SlAXnNkEmD0,5153
299
301
  bbot/test/test_step_2/module_tests/test_module_dnsbrute_mutations.py,sha256=Faojc5UHkUP2lMeWukUjLOU6tQSL3HdeepjuVIcs4rY,3902
300
302
  bbot/test/test_step_2/module_tests/test_module_dnscaa.py,sha256=5JaAYt-oFGON8Gc4xJNyc2UtjCp97OEiaJrvD04VHQM,2751
@@ -402,8 +404,8 @@ bbot/wordlists/raft-small-extensions-lowercase_CLEANED.txt,sha256=ruUQwVfia1_m2u
402
404
  bbot/wordlists/top_open_ports_nmap.txt,sha256=LmdFYkfapSxn1pVuQC2LkOIY2hMLgG-Xts7DVtYzweM,42727
403
405
  bbot/wordlists/valid_url_schemes.txt,sha256=VciB-ww0y-O8Ii1wpTR6rJzGDiC2r-dhVsIJApS1ZYU,3309
404
406
  bbot/wordlists/wordninja_dns.txt.gz,sha256=DYHvvfW0TvzrVwyprqODAk4tGOxv5ezNmCPSdPuDUnQ,570241
405
- bbot-2.1.2.5223rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
406
- bbot-2.1.2.5223rc0.dist-info/METADATA,sha256=twDbdgmWLHvXj0d5DNc6B-Q6ioy4pNN0dOVdpU2fGgA,17109
407
- bbot-2.1.2.5223rc0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
408
- bbot-2.1.2.5223rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
409
- bbot-2.1.2.5223rc0.dist-info/RECORD,,
407
+ bbot-2.1.2.5232rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
408
+ bbot-2.1.2.5232rc0.dist-info/METADATA,sha256=GL4dxSfsZJj9tk0IrOJNINpmPg9n9mOQq5Q9GQv9MAE,17109
409
+ bbot-2.1.2.5232rc0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
410
+ bbot-2.1.2.5232rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
411
+ bbot-2.1.2.5232rc0.dist-info/RECORD,,