bbot 2.4.2__py3-none-any.whl → 2.4.2.6590rc0__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 (64) hide show
  1. bbot/__init__.py +1 -1
  2. bbot/core/event/base.py +64 -4
  3. bbot/core/helpers/diff.py +10 -7
  4. bbot/core/helpers/helper.py +5 -1
  5. bbot/core/helpers/misc.py +48 -11
  6. bbot/core/helpers/regex.py +4 -0
  7. bbot/core/helpers/regexes.py +45 -8
  8. bbot/core/helpers/url.py +21 -5
  9. bbot/core/helpers/web/client.py +25 -5
  10. bbot/core/helpers/web/engine.py +9 -1
  11. bbot/core/helpers/web/envelopes.py +352 -0
  12. bbot/core/helpers/web/web.py +10 -2
  13. bbot/core/helpers/yara_helper.py +50 -0
  14. bbot/core/modules.py +23 -7
  15. bbot/defaults.yml +26 -1
  16. bbot/modules/base.py +4 -2
  17. bbot/modules/{deadly/dastardly.py → dastardly.py} +1 -1
  18. bbot/modules/{deadly/ffuf.py → ffuf.py} +1 -1
  19. bbot/modules/ffuf_shortnames.py +1 -1
  20. bbot/modules/httpx.py +14 -0
  21. bbot/modules/hunt.py +24 -6
  22. bbot/modules/internal/aggregate.py +1 -0
  23. bbot/modules/internal/excavate.py +356 -197
  24. bbot/modules/lightfuzz/lightfuzz.py +203 -0
  25. bbot/modules/lightfuzz/submodules/__init__.py +0 -0
  26. bbot/modules/lightfuzz/submodules/base.py +312 -0
  27. bbot/modules/lightfuzz/submodules/cmdi.py +106 -0
  28. bbot/modules/lightfuzz/submodules/crypto.py +474 -0
  29. bbot/modules/lightfuzz/submodules/nosqli.py +183 -0
  30. bbot/modules/lightfuzz/submodules/path.py +154 -0
  31. bbot/modules/lightfuzz/submodules/serial.py +179 -0
  32. bbot/modules/lightfuzz/submodules/sqli.py +187 -0
  33. bbot/modules/lightfuzz/submodules/ssti.py +39 -0
  34. bbot/modules/lightfuzz/submodules/xss.py +191 -0
  35. bbot/modules/{deadly/nuclei.py → nuclei.py} +1 -1
  36. bbot/modules/paramminer_headers.py +2 -0
  37. bbot/modules/reflected_parameters.py +80 -0
  38. bbot/modules/{deadly/vhost.py → vhost.py} +2 -2
  39. bbot/presets/web/lightfuzz-heavy.yml +16 -0
  40. bbot/presets/web/lightfuzz-light.yml +20 -0
  41. bbot/presets/web/lightfuzz-medium.yml +14 -0
  42. bbot/presets/web/lightfuzz-superheavy.yml +13 -0
  43. bbot/presets/web/lightfuzz-xss.yml +21 -0
  44. bbot/presets/web/paramminer.yml +8 -5
  45. bbot/scanner/preset/args.py +26 -0
  46. bbot/scanner/scanner.py +6 -0
  47. bbot/test/test_step_1/test__module__tests.py +1 -1
  48. bbot/test/test_step_1/test_helpers.py +7 -0
  49. bbot/test/test_step_1/test_presets.py +2 -2
  50. bbot/test/test_step_1/test_web.py +20 -0
  51. bbot/test/test_step_1/test_web_envelopes.py +343 -0
  52. bbot/test/test_step_2/module_tests/test_module_excavate.py +404 -29
  53. bbot/test/test_step_2/module_tests/test_module_httpx.py +29 -0
  54. bbot/test/test_step_2/module_tests/test_module_hunt.py +18 -1
  55. bbot/test/test_step_2/module_tests/test_module_lightfuzz.py +1947 -0
  56. bbot/test/test_step_2/module_tests/test_module_paramminer_getparams.py +4 -1
  57. bbot/test/test_step_2/module_tests/test_module_paramminer_headers.py +46 -2
  58. bbot/test/test_step_2/module_tests/test_module_reflected_parameters.py +226 -0
  59. bbot/wordlists/paramminer_parameters.txt +0 -8
  60. {bbot-2.4.2.dist-info → bbot-2.4.2.6590rc0.dist-info}/METADATA +2 -1
  61. {bbot-2.4.2.dist-info → bbot-2.4.2.6590rc0.dist-info}/RECORD +64 -42
  62. {bbot-2.4.2.dist-info → bbot-2.4.2.6590rc0.dist-info}/LICENSE +0 -0
  63. {bbot-2.4.2.dist-info → bbot-2.4.2.6590rc0.dist-info}/WHEEL +0 -0
  64. {bbot-2.4.2.dist-info → bbot-2.4.2.6590rc0.dist-info}/entry_points.txt +0 -0
@@ -194,7 +194,10 @@ class TestParamminer_Getparams_xmlspeculative(Paramminer_Headers):
194
194
  targets = ["http://127.0.0.1:8888/"]
195
195
  modules_overrides = ["httpx", "excavate", "paramminer_getparams"]
196
196
  config_overrides = {
197
- "modules": {"paramminer_getparams": {"wordlist": tempwordlist(["data", "common"]), "recycle_words": False}}
197
+ "modules": {
198
+ "excavate": {"speculate_params": True},
199
+ "paramminer_getparams": {"wordlist": tempwordlist(["data", "common"]), "recycle_words": False},
200
+ }
198
201
  }
199
202
  getparam_extract_xml = """
200
203
  <data>
@@ -27,7 +27,7 @@ class Paramminer_Headers(ModuleTestBase):
27
27
  """
28
28
 
29
29
  async def setup_after_prep(self, module_test):
30
- module_test.scan.modules["paramminer_headers"].rand_string = lambda *args, **kwargs: "AAAAAAAAAAAAAA"
30
+ module_test.scan.modules["paramminer_headers"].helpers.rand_string = lambda *args, **kwargs: "AAAAAAAAAAAAAA"
31
31
  module_test.monkeypatch.setattr(
32
32
  helper.HttpCompare, "gen_cache_buster", lambda *args, **kwargs: {"AAAAAA": "1"}
33
33
  )
@@ -108,7 +108,7 @@ class TestParamminer_Headers_extract(Paramminer_Headers):
108
108
  """
109
109
 
110
110
  async def setup_after_prep(self, module_test):
111
- module_test.scan.modules["paramminer_headers"].rand_string = lambda *args, **kwargs: "AAAAAAAAAAAAAA"
111
+ module_test.scan.modules["paramminer_headers"].helpers.rand_string = lambda *args, **kwargs: "AAAAAAAAAAAAAA"
112
112
  module_test.monkeypatch.setattr(
113
113
  helper.HttpCompare, "gen_cache_buster", lambda *args, **kwargs: {"AAAAAA": "1"}
114
114
  )
@@ -153,3 +153,47 @@ class TestParamminer_Headers_extract_norecycle(TestParamminer_Headers_extract):
153
153
  assert not excavate_extracted_web_parameter, (
154
154
  "Excavate extract WEB_PARAMETER despite disabling parameter extraction"
155
155
  )
156
+
157
+
158
+ class TestParamminer_Headers_NoCookieRetention(Paramminer_Headers):
159
+ async def setup_after_prep(self, module_test):
160
+ module_test.scan.modules["paramminer_headers"].helpers.rand_string = lambda *args, **kwargs: "AAAAAAAAAAAAAA"
161
+ module_test.monkeypatch.setattr(
162
+ helper.HttpCompare, "gen_cache_buster", lambda *args, **kwargs: {"AAAAAA": "1"}
163
+ )
164
+
165
+ expect_args = {"headers": {"tracestate": "AAAAAAAAAAAAAA"}}
166
+ respond_args = {"response_data": self.headers_body_match}
167
+ module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args)
168
+
169
+ headers_body_with_cookie = """
170
+ <html>
171
+ <title>the title</title>
172
+ <body>
173
+ <p>Hello with cookie!</p>';
174
+ </body>
175
+ </html>
176
+ """
177
+ expect_args = {"headers": {"Cookie": "test_cookie=cookie_value; AAAAAAAAAAAAAA=AAAAAAAAAAAAAA"}}
178
+ respond_args_with_cookie_body_change = {"response_data": headers_body_with_cookie}
179
+ module_test.set_expect_requests(expect_args=expect_args, respond_args=respond_args_with_cookie_body_change)
180
+
181
+ respond_args_default = {
182
+ "response_data": self.headers_body,
183
+ "headers": {"set-cookie": "test_cookie=cookie_value"},
184
+ }
185
+ module_test.set_expect_requests(respond_args=respond_args_default)
186
+
187
+ def check(self, module_test, events):
188
+ found_web_parameter = False
189
+ found_web_parameter_false_positive = False
190
+
191
+ for e in events:
192
+ if e.type == "WEB_PARAMETER":
193
+ if "[Paramminer] Header: [tracestate]" in e.data["description"]:
194
+ found_web_parameter = True
195
+ if "junkword1" in e.data["description"]:
196
+ found_web_parameter_false_positive = True
197
+
198
+ assert found_web_parameter, "WEB_PARAMETER event was not emitted"
199
+ assert not found_web_parameter_false_positive, "WEB_PARAMETER event was emitted with false positive"
@@ -0,0 +1,226 @@
1
+ from .base import ModuleTestBase, tempwordlist
2
+ from werkzeug.wrappers import Response
3
+ import re
4
+
5
+ from .test_module_paramminer_getparams import TestParamminer_Getparams
6
+ from .test_module_paramminer_headers import helper
7
+
8
+
9
+ class TestReflected_parameters_fromexcavate(ModuleTestBase):
10
+ targets = ["http://127.0.0.1:8888"]
11
+ modules_overrides = ["httpx", "reflected_parameters", "excavate"]
12
+
13
+ def request_handler(self, request):
14
+ normal_block = '<html><a href="/?reflected=foo">foo</a></html>'
15
+ qs = str(request.query_string.decode())
16
+ if "reflected=" in qs:
17
+ value = qs.split("=")[1]
18
+ if "&" in value:
19
+ value = value.split("&")[0]
20
+ reflected_block = f'<html><a href="/?reflected={value}"></a></html>'
21
+ return Response(reflected_block, status=200)
22
+ else:
23
+ return Response(normal_block, status=200)
24
+
25
+ async def setup_after_prep(self, module_test):
26
+ expect_args = re.compile("/")
27
+ module_test.set_expect_requests_handler(expect_args=expect_args, request_handler=self.request_handler)
28
+
29
+ def check(self, module_test, events):
30
+ assert any(
31
+ e.type == "FINDING"
32
+ and e.data["description"]
33
+ == "[GETPARAM] Parameter value reflected in response body. Name: [reflected] Source Module: [excavate] Original Value: [foo]"
34
+ for e in events
35
+ )
36
+
37
+
38
+ class TestReflected_parameters_headers(TestReflected_parameters_fromexcavate):
39
+ modules_overrides = ["httpx", "reflected_parameters", "excavate", "paramminer_headers"]
40
+ config_overrides = {
41
+ "modules": {
42
+ "paramminer_headers": {"wordlist": tempwordlist(["junkword1", "tracestate"]), "recycle_words": True}
43
+ }
44
+ }
45
+
46
+ def request_handler(self, request):
47
+ headers = {k.lower(): v for k, v in request.headers.items()}
48
+ if "tracestate" in headers:
49
+ reflected_value = headers["tracestate"]
50
+ reflected_block = f"<html><div>{reflected_value}</div></html>"
51
+ return Response(reflected_block, status=200)
52
+ else:
53
+ return Response("<html><div></div></html>", status=200)
54
+
55
+ def check(self, module_test, events):
56
+ assert any(
57
+ e.type == "FINDING"
58
+ and e.data["description"]
59
+ == "[HEADER] Parameter value reflected in response body. Name: [tracestate] Source Module: [paramminer_headers]"
60
+ for e in events
61
+ )
62
+
63
+
64
+ class TestReflected_parameters_fromparamminer(TestParamminer_Getparams):
65
+ modules_overrides = ["httpx", "paramminer_getparams", "reflected_parameters"]
66
+
67
+ def request_handler(self, request):
68
+ normal_block = "<html></html>"
69
+ qs = str(request.query_string.decode())
70
+ if "id=" in qs:
71
+ value = qs.split("=")[1]
72
+ if "&" in value:
73
+ value = value.split("&")[0]
74
+ reflected_block = f'<html><a href="/?id={value}"></a></html>'
75
+ return Response(reflected_block, status=200)
76
+ else:
77
+ return Response(normal_block, status=200)
78
+
79
+ async def setup_after_prep(self, module_test):
80
+ module_test.scan.modules["paramminer_getparams"].rand_string = lambda *args, **kwargs: "AAAAAAAAAAAAAA"
81
+ module_test.monkeypatch.setattr(
82
+ helper.HttpCompare, "gen_cache_buster", lambda *args, **kwargs: {"AAAAAA": "1"}
83
+ )
84
+
85
+ expect_args = re.compile("/")
86
+ module_test.set_expect_requests_handler(expect_args=expect_args, request_handler=self.request_handler)
87
+
88
+ def check(self, module_test, events):
89
+ assert any(
90
+ e.type == "FINDING"
91
+ and "[GETPARAM] Parameter value reflected in response body. Name: [id] Source Module: [paramminer_getparams]"
92
+ in e.data["description"]
93
+ for e in events
94
+ )
95
+
96
+
97
+ class TestReflected_parameters_with_canary(TestReflected_parameters_fromexcavate):
98
+ def request_handler(self, request):
99
+ normal_block = '<html><a href="/?reflected=foo">foo</a></html>'
100
+ qs = str(request.query_string.decode())
101
+ if qs:
102
+ # Split the query string into key-value pairs
103
+ params = qs.split("&")
104
+ # Construct the reflected block with all parameters
105
+ reflected_block = '<html><a href="/?'
106
+ reflected_block += "&".join(params)
107
+ reflected_block += '"></a></html>'
108
+ return Response(reflected_block, status=200)
109
+ else:
110
+ return Response(normal_block, status=200)
111
+
112
+ def check(self, module_test, events):
113
+ # Ensure no findings are emitted when the canary is reflected
114
+ assert not any(e.type == "FINDING" for e in events)
115
+
116
+
117
+ class TestReflected_parameters_cookies(TestReflected_parameters_fromexcavate):
118
+ modules_overrides = ["httpx", "reflected_parameters", "excavate", "paramminer_cookies"]
119
+ config_overrides = {
120
+ "modules": {
121
+ "paramminer_cookies": {"wordlist": tempwordlist(["junkword1", "testcookie"]), "recycle_words": True}
122
+ }
123
+ }
124
+
125
+ def request_handler(self, request):
126
+ cookies = request.cookies
127
+ if "testcookie" in cookies:
128
+ reflected_value = cookies["testcookie"]
129
+ reflected_block = f"<html><div>{reflected_value}</div></html>"
130
+ return Response(reflected_block, status=200)
131
+ else:
132
+ return Response("<html><div></div></html>", status=200)
133
+
134
+ def check(self, module_test, events):
135
+ assert any(
136
+ e.type == "FINDING"
137
+ and e.data["description"]
138
+ == "[COOKIE] Parameter value reflected in response body. Name: [testcookie] Source Module: [paramminer_cookies]"
139
+ for e in events
140
+ )
141
+
142
+
143
+ class TestReflected_parameters_postparams(TestReflected_parameters_fromexcavate):
144
+ modules_overrides = ["httpx", "reflected_parameters", "excavate"]
145
+
146
+ def request_handler(self, request):
147
+ form_data = request.form
148
+ if "testparam" in form_data:
149
+ reflected_value = form_data["testparam"]
150
+ reflected_block = f"<html><div>{reflected_value}</div></html>"
151
+ return Response(reflected_block, status=200)
152
+ else:
153
+ form_html = """
154
+ <html>
155
+ <body>
156
+ <form action="/" method="post">
157
+ <input type="text" name="testparam" value="default_value">
158
+ <input type="submit" value="Submit">
159
+ </form>
160
+ </body>
161
+ </html>
162
+ """
163
+ return Response(form_html, status=200)
164
+
165
+ def check(self, module_test, events):
166
+ assert any(
167
+ e.type == "FINDING"
168
+ and e.data["description"]
169
+ == "[POSTPARAM] Parameter value reflected in response body. Name: [testparam] Source Module: [excavate] Original Value: [default_value]"
170
+ for e in events
171
+ )
172
+
173
+
174
+ class TestReflected_parameters_bodyjson(TestReflected_parameters_fromexcavate):
175
+ modules_overrides = ["httpx", "reflected_parameters", "excavate"]
176
+
177
+ def request_handler(self, request):
178
+ # Ensure the request is expecting JSON data
179
+ if request.content_type == "application/json":
180
+ json_data = request.json
181
+ if "username" in json_data:
182
+ reflected_value = json_data["username"]
183
+ reflected_block = f"<html><div>{reflected_value}</div></html>"
184
+ return Response(reflected_block, status=200)
185
+ # Provide an HTML page with a jQuery AJAX call
186
+ jsonajax_extract_html = """
187
+ <html>
188
+ <script>
189
+ function doLogin(e) {
190
+ e.preventDefault();
191
+ var username = $("#usernamefield").val();
192
+ var password = $("#passwordfield").val();
193
+ $.ajax({
194
+ url: '/api/auth',
195
+ type: 'POST',
196
+ contentType: 'application/json',
197
+ data: JSON.stringify({ username: username, password: password }),
198
+ success: function (r) {
199
+ window.location.replace("/demo");
200
+ },
201
+ error: function (r) {
202
+ if (r.status == 401) {
203
+ notify("Access denied");
204
+ } else {
205
+ notify(r.responseText);
206
+ }
207
+ }
208
+ });
209
+ }
210
+ </script>
211
+ <form action=/ method=GET><input type=text name="novalue"><button type=submit class=button>Submit</button></form>
212
+ </html>
213
+ """
214
+ return Response(jsonajax_extract_html, status=200)
215
+
216
+ async def setup_after_prep(self, module_test):
217
+ expect_args = re.compile("/")
218
+ module_test.set_expect_requests_handler(expect_args=expect_args, request_handler=self.request_handler)
219
+
220
+ def check(self, module_test, events):
221
+ assert any(
222
+ e.type == "FINDING"
223
+ and e.data["description"]
224
+ == "[BODYJSON] Parameter value reflected in response body. Name: [username] Source Module: [excavate]"
225
+ for e in events
226
+ )
@@ -100,7 +100,6 @@ modify
100
100
  rename
101
101
  reset
102
102
  shell
103
- utm_content
104
103
  toggle
105
104
  adm
106
105
  cfg
@@ -616,7 +615,6 @@ replace
616
615
  read
617
616
  project
618
617
  Post
619
- PHPSESSID
620
618
  nid
621
619
  md5
622
620
  map
@@ -1532,10 +1530,6 @@ unstick
1532
1530
  unsecuresubmit
1533
1531
  unbookmark
1534
1532
  ua
1535
- utm_source
1536
- utm_campaign
1537
- utm_medium
1538
- utm_term
1539
1533
  typ
1540
1534
  tv
1541
1535
  tree
@@ -4754,7 +4748,6 @@ pPage
4754
4748
  pName
4755
4749
  pMail
4756
4750
  pDesc
4757
- p4ssw0rD
4758
4751
  p3
4759
4752
  p2p
4760
4753
  p2index
@@ -5701,7 +5694,6 @@ dstendport
5701
5694
  dstbeginport
5702
5695
  dscp
5703
5696
  dryrun
5704
- droptables
5705
5697
  drilldown
5706
5698
  dragtable
5707
5699
  dragdroporder
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bbot
3
- Version: 2.4.2
3
+ Version: 2.4.2.6590rc0
4
4
  Summary: OSINT automation for hackers.
5
5
  License: GPL-3.0
6
6
  Keywords: python,cli,automation,osint,threat-intel,intelligence,neo4j,scanner,python-library,hacking,recursion,pentesting,recon,command-line-tool,bugbounty,subdomains,security-tools,subdomain-scanner,osint-framework,attack-surface,subdomain-enumeration,osint-tool
@@ -438,6 +438,7 @@ For details, see [Configuration](https://www.blacklanternsecurity.com/bbot/Stabl
438
438
  - [List of Modules](https://www.blacklanternsecurity.com/bbot/Stable/modules/list_of_modules)
439
439
  - [Nuclei](https://www.blacklanternsecurity.com/bbot/Stable/modules/nuclei)
440
440
  - [Custom YARA Rules](https://www.blacklanternsecurity.com/bbot/Stable/modules/custom_yara_rules)
441
+ - [Lightfuzz](https://www.blacklanternsecurity.com/bbot/Stable/modules/lightfuzz)
441
442
  - **Misc**
442
443
  - [Contribution](https://www.blacklanternsecurity.com/bbot/Stable/contribution)
443
444
  - [Release History](https://www.blacklanternsecurity.com/bbot/Stable/release_history)