bbot 2.4.2.6706rc0__py3-none-any.whl → 2.5.0.6715rc0__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/modules/lightfuzz/lightfuzz.py +1 -1
- bbot/modules/lightfuzz/submodules/serial.py +11 -1
- bbot/modules/lightfuzz/submodules/sqli.py +1 -0
- bbot/modules/lightfuzz/submodules/xss.py +4 -4
- bbot/presets/web/lightfuzz-heavy.yml +1 -1
- bbot/presets/web/lightfuzz-medium.yml +1 -1
- bbot/presets/web/lightfuzz-superheavy.yml +1 -1
- bbot/test/test_step_2/module_tests/test_module_lightfuzz.py +2 -130
- {bbot-2.4.2.6706rc0.dist-info → bbot-2.5.0.6715rc0.dist-info}/METADATA +1 -1
- {bbot-2.4.2.6706rc0.dist-info → bbot-2.5.0.6715rc0.dist-info}/RECORD +14 -15
- bbot/modules/lightfuzz/submodules/nosqli.py +0 -183
- {bbot-2.4.2.6706rc0.dist-info → bbot-2.5.0.6715rc0.dist-info}/LICENSE +0 -0
- {bbot-2.4.2.6706rc0.dist-info → bbot-2.5.0.6715rc0.dist-info}/WHEEL +0 -0
- {bbot-2.4.2.6706rc0.dist-info → bbot-2.5.0.6715rc0.dist-info}/entry_points.txt +0 -0
bbot/__init__.py
CHANGED
|
@@ -11,7 +11,7 @@ class lightfuzz(BaseModule):
|
|
|
11
11
|
|
|
12
12
|
options = {
|
|
13
13
|
"force_common_headers": False,
|
|
14
|
-
"enabled_submodules": ["sqli", "cmdi", "xss", "path", "ssti", "crypto", "serial"
|
|
14
|
+
"enabled_submodules": ["sqli", "cmdi", "xss", "path", "ssti", "crypto", "serial"],
|
|
15
15
|
"disable_post": False,
|
|
16
16
|
}
|
|
17
17
|
options_desc = {
|
|
@@ -153,10 +153,20 @@ class serial(BaseLightfuzz):
|
|
|
153
153
|
error in response.text for error in general_errors
|
|
154
154
|
) # ensure the 200 is not actually an error
|
|
155
155
|
):
|
|
156
|
+
|
|
157
|
+
def get_title(text):
|
|
158
|
+
soup = self.lightfuzz.helpers.beautifulsoup(text, "html.parser")
|
|
159
|
+
if soup and soup.title and soup.title.string:
|
|
160
|
+
return f"'{self.lightfuzz.helpers.truncate_string(soup.title.string, 50)}'"
|
|
161
|
+
return ""
|
|
162
|
+
|
|
163
|
+
baseline_title = get_title(payload_baseline.baseline.text)
|
|
164
|
+
probe_title = get_title(response.text)
|
|
165
|
+
|
|
156
166
|
self.results.append(
|
|
157
167
|
{
|
|
158
168
|
"type": "FINDING",
|
|
159
|
-
"description": f"POSSIBLE Unsafe Deserialization. {self.metadata()} Technique: [Error Resolution] Serialization Payload: [{type}]",
|
|
169
|
+
"description": f"POSSIBLE Unsafe Deserialization. {self.metadata()} Technique: [Error Resolution (Baseline: [{payload_baseline.baseline.status_code}] {baseline_title} -> Probe: [{status_code}] {probe_title})] Serialization Payload: [{type}]",
|
|
160
170
|
}
|
|
161
171
|
)
|
|
162
172
|
# if the first case doesn't match, we check for a telltale error string like "java.io.optionaldataexception" in the response.
|
|
@@ -111,6 +111,7 @@ class sqli(BaseLightfuzz):
|
|
|
111
111
|
single_quote[3].status_code != 429
|
|
112
112
|
and double_single_quote[3].status_code != 429
|
|
113
113
|
and http_compare.baseline.status_code != 429
|
|
114
|
+
and http_compare.baseline.status_code != 403 # Ensure the baseline status code is not 403
|
|
114
115
|
): # prevent false positives from rate limiting
|
|
115
116
|
# if the code changed in the single quote probe, and the code is NOT the same between that and the double single quote probe, SQL injection is indicated
|
|
116
117
|
if "code" in single_quote[1] and (
|
|
@@ -142,14 +142,14 @@ class xss(BaseLightfuzz):
|
|
|
142
142
|
break
|
|
143
143
|
|
|
144
144
|
if in_tag_attribute:
|
|
145
|
-
in_tag_attribute_probe = f'{random_string}"'
|
|
146
|
-
in_tag_attribute_match = f'{random_string}"'
|
|
145
|
+
in_tag_attribute_probe = f'{random_string}"z'
|
|
146
|
+
in_tag_attribute_match = f'{random_string}"z'
|
|
147
147
|
await self.check_probe(
|
|
148
148
|
cookies, in_tag_attribute_probe, in_tag_attribute_match, "Tag Attribute"
|
|
149
149
|
) # After reflection in the HTTP response, did the quote survive without url-encoding or other sanitization/escaping?
|
|
150
150
|
|
|
151
|
-
in_tag_attribute_probe = f'{random_string}"'
|
|
152
|
-
in_tag_attribute_match = f'"{random_string}""'
|
|
151
|
+
in_tag_attribute_probe = f'{random_string}"z'
|
|
152
|
+
in_tag_attribute_match = f'"{random_string}""z'
|
|
153
153
|
await self.check_probe(
|
|
154
154
|
cookies, in_tag_attribute_probe, in_tag_attribute_match, "Tag Attribute (autoquote)"
|
|
155
155
|
) # After reflection in the HTTP response, did the quote survive without url-encoding or other sanitization/escaping (and account for auto-quoting)
|
|
@@ -8,6 +8,6 @@ config:
|
|
|
8
8
|
modules:
|
|
9
9
|
lightfuzz:
|
|
10
10
|
force_common_headers: True # Fuzz common headers like X-Forwarded-For even if they're not observed on the target
|
|
11
|
-
enabled_submodules: [cmdi,crypto,
|
|
11
|
+
enabled_submodules: [cmdi,crypto,path,serial,sqli,ssti,xss]
|
|
12
12
|
excavate:
|
|
13
13
|
speculate_params: True # speculate potential parameters extracted from JSON/XML web responses
|
|
@@ -639,132 +639,6 @@ class Test_Lightfuzz_urlencoding(Test_Lightfuzz_xss_injs):
|
|
|
639
639
|
assert xss_finding_emitted, "In Javascript XSS FINDING not emitted"
|
|
640
640
|
|
|
641
641
|
|
|
642
|
-
class Test_Lightfuzz_nosqli_quoteescape(ModuleTestBase):
|
|
643
|
-
targets = ["http://127.0.0.1:8888"]
|
|
644
|
-
modules_overrides = ["httpx", "lightfuzz", "excavate"]
|
|
645
|
-
config_overrides = {
|
|
646
|
-
"interactsh_disable": True,
|
|
647
|
-
"modules": {
|
|
648
|
-
"lightfuzz": {
|
|
649
|
-
"enabled_submodules": ["nosqli"],
|
|
650
|
-
}
|
|
651
|
-
},
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
def request_handler(self, request):
|
|
655
|
-
normal_block = """
|
|
656
|
-
<section class="search-filters">
|
|
657
|
-
<label>Refine your search:</label>
|
|
658
|
-
<a class="filter-category" href="/?category=Pets">Pets</a>
|
|
659
|
-
</section>
|
|
660
|
-
"""
|
|
661
|
-
|
|
662
|
-
qs = str(request.query_string.decode())
|
|
663
|
-
if "category=" in qs:
|
|
664
|
-
value = qs.split("=")[1]
|
|
665
|
-
if "&" in value:
|
|
666
|
-
value = value.split("&")[0]
|
|
667
|
-
if value == "Pets%27":
|
|
668
|
-
return Response("JSON ERROR!", status=500)
|
|
669
|
-
elif value == "Pets%5C%27":
|
|
670
|
-
return Response("No results", status=200)
|
|
671
|
-
elif value == "Pets%27%20%26%26%200%20%26%26%20%27x":
|
|
672
|
-
return Response("No results", status=200)
|
|
673
|
-
elif value == "Pets%27%20%26%26%201%20%26%26%20%27x":
|
|
674
|
-
return Response('{"category":"Pets","entries":["dog","cat","bird"]}', status=200)
|
|
675
|
-
else:
|
|
676
|
-
return Response("No results", status=200)
|
|
677
|
-
return Response(normal_block, status=200)
|
|
678
|
-
|
|
679
|
-
async def setup_after_prep(self, module_test):
|
|
680
|
-
module_test.scan.modules["lightfuzz"].helpers.rand_string = lambda *args, **kwargs: "AAAAAAAAAAAAAA"
|
|
681
|
-
expect_args = re.compile("/")
|
|
682
|
-
module_test.set_expect_requests_handler(expect_args=expect_args, request_handler=self.request_handler)
|
|
683
|
-
|
|
684
|
-
def check(self, module_test, events):
|
|
685
|
-
nosqli_finding_emitted = False
|
|
686
|
-
finding_count = 0
|
|
687
|
-
for e in events:
|
|
688
|
-
if e.type == "FINDING":
|
|
689
|
-
finding_count += 1
|
|
690
|
-
if (
|
|
691
|
-
"Possible NoSQL Injection. Parameter: [category] Parameter Type: [GETPARAM] Original Value: [Pets] Detection Method: [Quote/Escaped Quote + Conditional Affect]"
|
|
692
|
-
in e.data["description"]
|
|
693
|
-
):
|
|
694
|
-
nosqli_finding_emitted = True
|
|
695
|
-
assert nosqli_finding_emitted, "NoSQLi FINDING not emitted"
|
|
696
|
-
assert finding_count == 1, "Unexpected FINDING events reported"
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
class Test_Lightfuzz_nosqli_negation(Test_Lightfuzz_nosqli_quoteescape):
|
|
700
|
-
def request_handler(self, request):
|
|
701
|
-
form_block = """
|
|
702
|
-
<form method="POST" action="">
|
|
703
|
-
<label for="username">Username:</label>
|
|
704
|
-
<input type="text" id="username" name="username" required>
|
|
705
|
-
<br>
|
|
706
|
-
<label for="password">Password:</label>
|
|
707
|
-
<input type="password" id="password" name="password" required>
|
|
708
|
-
<br>
|
|
709
|
-
<button type="submit">Login</button>
|
|
710
|
-
</form>
|
|
711
|
-
"""
|
|
712
|
-
if request.method == "GET":
|
|
713
|
-
return Response(form_block, status=200)
|
|
714
|
-
|
|
715
|
-
if "username[$ne]" in request.form.keys() and "password[$ne]" in request.form.keys():
|
|
716
|
-
return Response("Welcome, testuser1!", status=200)
|
|
717
|
-
if "username[$eq]" in request.form.keys() and "password[$eq]" in request.form.keys():
|
|
718
|
-
return Response("Invalid Username or Password!", status=200)
|
|
719
|
-
else:
|
|
720
|
-
return Response("Invalid Username or Password!", status=200)
|
|
721
|
-
|
|
722
|
-
def check(self, module_test, events):
|
|
723
|
-
nosqli_finding_emitted = False
|
|
724
|
-
finding_count = 0
|
|
725
|
-
for e in events:
|
|
726
|
-
if e.type == "FINDING":
|
|
727
|
-
finding_count += 1
|
|
728
|
-
if (
|
|
729
|
-
"Possible NoSQL Injection. Parameter: [password] Parameter Type: [POSTPARAM] Detection Method: [Parameter Name Operator Injection - Negation ([$ne])] Differences: [body]"
|
|
730
|
-
in e.data["description"]
|
|
731
|
-
):
|
|
732
|
-
nosqli_finding_emitted = True
|
|
733
|
-
assert nosqli_finding_emitted, "NoSQLi FINDING not emitted"
|
|
734
|
-
assert finding_count == 2, "Unexpected FINDING events reported"
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
class Test_Lightfuzz_nosqli_negation_falsepositive(Test_Lightfuzz_nosqli_quoteescape):
|
|
738
|
-
def request_handler(self, request):
|
|
739
|
-
form_block = """
|
|
740
|
-
<form method="POST" action="">
|
|
741
|
-
<label for="username">Username:</label>
|
|
742
|
-
<input type="text" id="username" name="username" required>
|
|
743
|
-
<br>
|
|
744
|
-
<label for="password">Password:</label>
|
|
745
|
-
<input type="password" id="password" name="password" required>
|
|
746
|
-
<br>
|
|
747
|
-
<button type="submit">Login</button>
|
|
748
|
-
</form>
|
|
749
|
-
"""
|
|
750
|
-
if request.method == "GET":
|
|
751
|
-
return Response(form_block, status=200)
|
|
752
|
-
|
|
753
|
-
if "username[$ne]" in request.form.keys() and "password[$ne]" in request.form.keys():
|
|
754
|
-
return Response("missing username or password", status=500)
|
|
755
|
-
if "username[$eq]" in request.form.keys() and "password[$eq]" in request.form.keys():
|
|
756
|
-
return Response("missing username or password", status=500)
|
|
757
|
-
else:
|
|
758
|
-
return Response("Invalid Username or Password!", status=200)
|
|
759
|
-
|
|
760
|
-
def check(self, module_test, events):
|
|
761
|
-
finding_count = 0
|
|
762
|
-
for e in events:
|
|
763
|
-
if e.type == "FINDING":
|
|
764
|
-
finding_count += 1
|
|
765
|
-
assert finding_count == 0, "False positive FINDING emitted"
|
|
766
|
-
|
|
767
|
-
|
|
768
642
|
# SQLI Single Quote/Two Single Quote (getparam)
|
|
769
643
|
class Test_Lightfuzz_sqli(ModuleTestBase):
|
|
770
644
|
targets = ["http://127.0.0.1:8888"]
|
|
@@ -1040,8 +914,6 @@ class Test_Lightfuzz_sqli_cookies(Test_Lightfuzz_sqli):
|
|
|
1040
914
|
</html>
|
|
1041
915
|
"""
|
|
1042
916
|
|
|
1043
|
-
print("@@@@@???")
|
|
1044
|
-
print(request.cookies)
|
|
1045
917
|
if request.cookies.get("test") is not None:
|
|
1046
918
|
header_value = request.cookies.get("test")
|
|
1047
919
|
|
|
@@ -1226,7 +1098,7 @@ class Test_Lightfuzz_serial_errorresolution(ModuleTestBase):
|
|
|
1226
1098
|
if e.type == "FINDING":
|
|
1227
1099
|
if (
|
|
1228
1100
|
e.data["description"]
|
|
1229
|
-
== "POSSIBLE Unsafe Deserialization. Parameter: [TextBox1] Parameter Type: [POSTPARAM] Technique: [Error Resolution] Serialization Payload: [dotnet_base64]"
|
|
1101
|
+
== "POSSIBLE Unsafe Deserialization. Parameter: [TextBox1] Parameter Type: [POSTPARAM] Technique: [Error Resolution (Baseline: [500] -> Probe: [200] )] Serialization Payload: [dotnet_base64]"
|
|
1230
1102
|
):
|
|
1231
1103
|
lightfuzz_serial_detect_errorresolution = True
|
|
1232
1104
|
|
|
@@ -1326,7 +1198,7 @@ class Test_Lightfuzz_serial_errorresolution_existingvalue_valid(Test_Lightfuzz_s
|
|
|
1326
1198
|
excavate_detect_serialization_value = True
|
|
1327
1199
|
if (
|
|
1328
1200
|
e.data["description"]
|
|
1329
|
-
== "POSSIBLE Unsafe Deserialization. Parameter: [TextBox1] Parameter Type: [POSTPARAM] Original Value: [AAEAAAD/////AQAAAAAAAAAGAQAAAAdndXN0YXZvCw==] Technique: [Error Resolution] Serialization Payload: [dotnet_base64]"
|
|
1201
|
+
== "POSSIBLE Unsafe Deserialization. Parameter: [TextBox1] Parameter Type: [POSTPARAM] Original Value: [AAEAAAD/////AQAAAAAAAAAGAQAAAAdndXN0YXZvCw==] Technique: [Error Resolution (Baseline: [500] -> Probe: [200] )] Serialization Payload: [dotnet_base64]"
|
|
1330
1202
|
):
|
|
1331
1203
|
lightfuzz_serial_detect_errorresolution = True
|
|
1332
1204
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: bbot
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0.6715rc0
|
|
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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
bbot/__init__.py,sha256=
|
|
1
|
+
bbot/__init__.py,sha256=yccQ2-fVLMX5ykoO0zFuYDxnjbewc43wdlTr_8NF3yw,163
|
|
2
2
|
bbot/cli.py,sha256=1QJbANVw9Q3GFM92H2QRV2ds5756ulm08CDZwzwPpeI,11888
|
|
3
3
|
bbot/core/__init__.py,sha256=l255GJE_DvUnWvrRb0J5lG-iMztJ8zVvoweDOfegGtI,46
|
|
4
4
|
bbot/core/config/__init__.py,sha256=zYNw2Me6tsEr8hOOkLb4BQ97GB7Kis2k--G81S8vofU,342
|
|
@@ -133,17 +133,16 @@ bbot/modules/ipneighbor.py,sha256=b_0IhorihFLtXJZEz57EGXjXW30gIOEzzVgz2GFvM3A,15
|
|
|
133
133
|
bbot/modules/ipstack.py,sha256=j_S8WMNqQuSQgBT7AX4tO70fgbWuRYrpsS3tVsu_hn4,2200
|
|
134
134
|
bbot/modules/jadx.py,sha256=8ncbK5i3B6i253qJ5tuD3-cPl4S8qqRvnTVINFTjtvQ,3084
|
|
135
135
|
bbot/modules/leakix.py,sha256=4sKghkId5cX_eVqTSFsqdNzX5JmUemr-U7gs8PfKVVA,1561
|
|
136
|
-
bbot/modules/lightfuzz/lightfuzz.py,sha256=
|
|
136
|
+
bbot/modules/lightfuzz/lightfuzz.py,sha256=REs0wkwpvrL4bZAg8V1Ui3KFyvgWVtDSk8jP_fFcaCQ,8911
|
|
137
137
|
bbot/modules/lightfuzz/submodules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
138
138
|
bbot/modules/lightfuzz/submodules/base.py,sha256=PvMEoEqrgAmp63JufaPe9GhOn7ay3oBvvZCuFIzuwaI,12619
|
|
139
139
|
bbot/modules/lightfuzz/submodules/cmdi.py,sha256=-9pL7Yh7VVCObxuS6Qu2cKEJBstfk0oXIkMyntg8xDk,5043
|
|
140
140
|
bbot/modules/lightfuzz/submodules/crypto.py,sha256=mLWsMbcox9oruNjfdOaLmT7ePMH15K8JN9K5AIB8f8o,22560
|
|
141
|
-
bbot/modules/lightfuzz/submodules/nosqli.py,sha256=K0TlBtpfeBH72q01a3TCQnt9OsznA9kfRYVTe7Vmers,9399
|
|
142
141
|
bbot/modules/lightfuzz/submodules/path.py,sha256=cvfna9P5Cicmc3p3BrzlY0PG1slcvJkeMzZu4i2nwO0,7744
|
|
143
|
-
bbot/modules/lightfuzz/submodules/serial.py,sha256=
|
|
144
|
-
bbot/modules/lightfuzz/submodules/sqli.py,sha256=
|
|
142
|
+
bbot/modules/lightfuzz/submodules/serial.py,sha256=Vry3J0Bs3QJqgVPzxhDFmEZQt4FYzLmpOYUHr9tCgR4,9118
|
|
143
|
+
bbot/modules/lightfuzz/submodules/sqli.py,sha256=HX0wP-aVn02zzBDujpLgzXPos7w_eiSiALTNCN2O_Bo,8597
|
|
145
144
|
bbot/modules/lightfuzz/submodules/ssti.py,sha256=Pib49rXFuf567msnlec-A1Tnvolw4aILjqn7INLWQTY,1413
|
|
146
|
-
bbot/modules/lightfuzz/submodules/xss.py,sha256=
|
|
145
|
+
bbot/modules/lightfuzz/submodules/xss.py,sha256=BZz1_nqzV8dqJptpoqZEMdVBdtZHmRae3HWo3S9yzIc,9507
|
|
147
146
|
bbot/modules/myssl.py,sha256=DoMF7o6MxIrcglCrC-W3nM-GPcyJRM4PlGdKfnOlIvs,942
|
|
148
147
|
bbot/modules/newsletters.py,sha256=1Q4JjShPsxHJ-by2CbGfCvEt80blUGPX0hxQIzB_a9M,2630
|
|
149
148
|
bbot/modules/ntlm.py,sha256=EGmb4k3YC_ZuHIU3mGUZ4yaMjE35wVQQSv8HwTsQJzY,4391
|
|
@@ -237,10 +236,10 @@ bbot/presets/web/dirbust-heavy.yml,sha256=NDqu7p0Hx1RsZCVnaEWRgI_iL9O0io-tvWerxJ
|
|
|
237
236
|
bbot/presets/web/dirbust-light.yml,sha256=5zSANdjKfYh49kFlsElYY2G6acVrZFzDCEkyqwU6oOQ,203
|
|
238
237
|
bbot/presets/web/dotnet-audit.yml,sha256=FViiccDXG08P3INNe06bLPeatejbw8Kb1HW5xgdUJNU,520
|
|
239
238
|
bbot/presets/web/iis-shortnames.yml,sha256=EcYKMpl-cI8Xb79_u4wQS42yFXxDpLH9OqINcFUXoTE,176
|
|
240
|
-
bbot/presets/web/lightfuzz-heavy.yml,sha256=
|
|
239
|
+
bbot/presets/web/lightfuzz-heavy.yml,sha256=zb7DPT-tf5MxTXkVpHn8cx2YwpbaGwZOST8vbhAOWZ0,459
|
|
241
240
|
bbot/presets/web/lightfuzz-light.yml,sha256=pkjTa5ULeOhCgRYPAoJR-cVfyhErT3I1aqmWGHTIgBk,899
|
|
242
|
-
bbot/presets/web/lightfuzz-medium.yml,sha256=
|
|
243
|
-
bbot/presets/web/lightfuzz-superheavy.yml,sha256=
|
|
241
|
+
bbot/presets/web/lightfuzz-medium.yml,sha256=LUB1Em4EQnIB387E3EiUw4AOIW9kzz3ItMb3xBBYnzE,490
|
|
242
|
+
bbot/presets/web/lightfuzz-superheavy.yml,sha256=3EaiNUCxsRqcrkhMcB70pMkMGT8BplikzSxfosprWTk,850
|
|
244
243
|
bbot/presets/web/lightfuzz-xss.yml,sha256=LMe968_iKyQhnm1nPh6zXDeNyDum2_MPkLg7ukqr93A,680
|
|
245
244
|
bbot/presets/web/paramminer.yml,sha256=8n-aDzufrZdtIlZwI0yh4-rQiwU1FPODYwmyra3l-1M,393
|
|
246
245
|
bbot/presets/web-basic.yml,sha256=6YWSYclbuf9yr8-gILDpLvOUj5QjP4rlarm5_d5iBFw,79
|
|
@@ -378,7 +377,7 @@ bbot/test/test_step_2/module_tests/test_module_ipstack.py,sha256=C0Le03UqvShpATo
|
|
|
378
377
|
bbot/test/test_step_2/module_tests/test_module_jadx.py,sha256=qTBfDc_Iv03n8iGdyLm6kBaKeEdSxFYeKj5xL1PmyF0,2391
|
|
379
378
|
bbot/test/test_step_2/module_tests/test_module_json.py,sha256=gmlqge5ZJpjVMGs7OLZBsNlSFTTrKnKjIZMIU23o8VQ,3350
|
|
380
379
|
bbot/test/test_step_2/module_tests/test_module_leakix.py,sha256=DQaQsL4ewpuYeygp-sgcvdeOSzvHq77_eYjKcgebS7A,1817
|
|
381
|
-
bbot/test/test_step_2/module_tests/test_module_lightfuzz.py,sha256=
|
|
380
|
+
bbot/test/test_step_2/module_tests/test_module_lightfuzz.py,sha256=W8IrU5WEBESR8xZUNVHWzLqjDVuVj2m_5q0xiGZwYAo,72050
|
|
382
381
|
bbot/test/test_step_2/module_tests/test_module_medusa.py,sha256=vYoAyMf0LbIXCoUzLycOISZtF7M58E30WjuLuqxDiCg,1671
|
|
383
382
|
bbot/test/test_step_2/module_tests/test_module_mysql.py,sha256=4wAPjbjhlxmOkEhQnIQIBC2BLEaE57TX6lChGZ3zLsU,2630
|
|
384
383
|
bbot/test/test_step_2/module_tests/test_module_myssl.py,sha256=zRJ1sOEespWtBx2jA07bW5sHD1XQ9pV0PtHtGogo7Gs,1531
|
|
@@ -452,8 +451,8 @@ bbot/wordlists/raft-small-extensions-lowercase_CLEANED.txt,sha256=ZSIVebs7ptMvHx
|
|
|
452
451
|
bbot/wordlists/top_open_ports_nmap.txt,sha256=LmdFYkfapSxn1pVuQC2LkOIY2hMLgG-Xts7DVtYzweM,42727
|
|
453
452
|
bbot/wordlists/valid_url_schemes.txt,sha256=0B_VAr9Dv7aYhwi6JSBDU-3M76vNtzN0qEC_RNLo7HE,3310
|
|
454
453
|
bbot/wordlists/wordninja_dns.txt.gz,sha256=DYHvvfW0TvzrVwyprqODAk4tGOxv5ezNmCPSdPuDUnQ,570241
|
|
455
|
-
bbot-2.
|
|
456
|
-
bbot-2.
|
|
457
|
-
bbot-2.
|
|
458
|
-
bbot-2.
|
|
459
|
-
bbot-2.
|
|
454
|
+
bbot-2.5.0.6715rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
|
|
455
|
+
bbot-2.5.0.6715rc0.dist-info/METADATA,sha256=RwysEf_RUW3X7xCtPJoRvVHFtoZBYaKOfg6Rs2XMkUI,18308
|
|
456
|
+
bbot-2.5.0.6715rc0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
457
|
+
bbot-2.5.0.6715rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
|
|
458
|
+
bbot-2.5.0.6715rc0.dist-info/RECORD,,
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
from .base import BaseLightfuzz
|
|
2
|
-
from bbot.errors import HttpCompareError
|
|
3
|
-
import urllib.parse
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class nosqli(BaseLightfuzz):
|
|
7
|
-
"""
|
|
8
|
-
Detects NoSQL injection vulnerabilities.
|
|
9
|
-
|
|
10
|
-
Techniques:
|
|
11
|
-
|
|
12
|
-
* Quote Injection Analysis:
|
|
13
|
-
- Injects single quotes and escaped single quotes into parameters
|
|
14
|
-
- Compares response differences between the two to detect NoSQL parsing
|
|
15
|
-
- Uses baseline comparison to validate findings and reduce false positives
|
|
16
|
-
|
|
17
|
-
* Operator Injection:
|
|
18
|
-
- Tests MongoDB-style operator injection using [$eq] and [$ne]
|
|
19
|
-
- Modifies parameter names to include operators
|
|
20
|
-
- Detects behavioral changes in application responses
|
|
21
|
-
|
|
22
|
-
Validation of findings is achieved using confirmation probes to rule out unstable endpoints
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
friendly_name = "NoSQL Injection"
|
|
26
|
-
|
|
27
|
-
async def fuzz(self):
|
|
28
|
-
cookies = self.event.data.get("assigned_cookies", {})
|
|
29
|
-
probe_value = self.incoming_probe_value(populate_empty=True)
|
|
30
|
-
quote_probe_baseline = None
|
|
31
|
-
try:
|
|
32
|
-
quote_probe_baseline = self.compare_baseline(
|
|
33
|
-
self.event.data["type"], probe_value, cookies, additional_params_populate_empty=True
|
|
34
|
-
)
|
|
35
|
-
except HttpCompareError as e:
|
|
36
|
-
self.verbose(f"Encountered HttpCompareError Sending Compare Baseline: {e}")
|
|
37
|
-
|
|
38
|
-
if quote_probe_baseline:
|
|
39
|
-
try:
|
|
40
|
-
# send the with a single quote, and then another with an escaped single quote
|
|
41
|
-
(
|
|
42
|
-
single_quote_comparison,
|
|
43
|
-
single_quote_diff_reasons,
|
|
44
|
-
single_quote_reflection,
|
|
45
|
-
single_quote_response,
|
|
46
|
-
) = await self.compare_probe(
|
|
47
|
-
quote_probe_baseline,
|
|
48
|
-
self.event.data["type"],
|
|
49
|
-
f"{probe_value}'",
|
|
50
|
-
cookies,
|
|
51
|
-
additional_params_populate_empty=True,
|
|
52
|
-
)
|
|
53
|
-
(
|
|
54
|
-
escaped_single_quote_comparison,
|
|
55
|
-
escaped_single_quote_diff_reasons,
|
|
56
|
-
escaped_single_quote_reflection,
|
|
57
|
-
escaped_single_quote_response,
|
|
58
|
-
) = await self.compare_probe(
|
|
59
|
-
quote_probe_baseline,
|
|
60
|
-
self.event.data["type"],
|
|
61
|
-
rf"{probe_value}\'",
|
|
62
|
-
cookies,
|
|
63
|
-
additional_params_populate_empty=True,
|
|
64
|
-
)
|
|
65
|
-
if not single_quote_comparison and single_quote_response and escaped_single_quote_response:
|
|
66
|
-
# if the single quote probe changed the code or body, and the escaped single quote probe did not cause the same change, injection is possible
|
|
67
|
-
if ("code" in single_quote_diff_reasons or "body" in single_quote_diff_reasons) and (
|
|
68
|
-
single_quote_diff_reasons != escaped_single_quote_diff_reasons
|
|
69
|
-
):
|
|
70
|
-
self.verbose(
|
|
71
|
-
"Initial heuristic indicates possible NoSQL Injection, sending confirmation probes"
|
|
72
|
-
)
|
|
73
|
-
confirm_baseline = self.compare_baseline(
|
|
74
|
-
self.event.data["type"],
|
|
75
|
-
urllib.parse.quote(f"{probe_value}' && 0 && 'x", safe=""),
|
|
76
|
-
cookies,
|
|
77
|
-
additional_params_populate_empty=True,
|
|
78
|
-
skip_urlencoding=True,
|
|
79
|
-
)
|
|
80
|
-
(
|
|
81
|
-
confirmation_probe_false_comparison,
|
|
82
|
-
confirmation_probe_false_diff_reasons,
|
|
83
|
-
confirmation_probe_false_reflection,
|
|
84
|
-
confirmation_probe_false_response,
|
|
85
|
-
) = await self.compare_probe(
|
|
86
|
-
confirm_baseline,
|
|
87
|
-
self.event.data["type"],
|
|
88
|
-
urllib.parse.quote(f"{probe_value}' && 1 && 'x", safe=""),
|
|
89
|
-
cookies,
|
|
90
|
-
additional_params_populate_empty=True,
|
|
91
|
-
skip_urlencoding=True,
|
|
92
|
-
)
|
|
93
|
-
if confirmation_probe_false_response:
|
|
94
|
-
if not confirmation_probe_false_comparison and confirmation_probe_false_diff_reasons != [
|
|
95
|
-
"header"
|
|
96
|
-
]:
|
|
97
|
-
(
|
|
98
|
-
final_confirm_comparison,
|
|
99
|
-
final_confirm_diff_reasons,
|
|
100
|
-
final_confirm_reflection,
|
|
101
|
-
final_confirm_response,
|
|
102
|
-
) = await self.compare_probe(
|
|
103
|
-
confirm_baseline,
|
|
104
|
-
self.event.data["type"],
|
|
105
|
-
urllib.parse.quote(f"{probe_value}' && 0 && 'x", safe=""),
|
|
106
|
-
cookies,
|
|
107
|
-
additional_params_populate_empty=True,
|
|
108
|
-
skip_urlencoding=True,
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
if final_confirm_response and final_confirm_comparison:
|
|
112
|
-
self.results.append(
|
|
113
|
-
{
|
|
114
|
-
"type": "FINDING",
|
|
115
|
-
"description": f"Possible NoSQL Injection. {self.metadata()} Detection Method: [Quote/Escaped Quote + Conditional Affect] Differences: [{'.'.join(confirmation_probe_false_diff_reasons)}]",
|
|
116
|
-
}
|
|
117
|
-
)
|
|
118
|
-
else:
|
|
119
|
-
self.verbose(
|
|
120
|
-
"Aborted reporting Possible NoSQL Injection, due to unstable/inconsistent responses"
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
except HttpCompareError as e:
|
|
124
|
-
self.verbose(f"Encountered HttpCompareError Sending Compare Probe: {e}")
|
|
125
|
-
|
|
126
|
-
# Comparison operator injection
|
|
127
|
-
if self.event.data["type"] in ["POSTPARAM", "GETPARAM"]:
|
|
128
|
-
nosqli_negation_baseline = None
|
|
129
|
-
|
|
130
|
-
try:
|
|
131
|
-
nosqli_negation_baseline = self.compare_baseline(
|
|
132
|
-
self.event.data["type"],
|
|
133
|
-
f"{probe_value}'",
|
|
134
|
-
cookies,
|
|
135
|
-
additional_params_populate_empty=True,
|
|
136
|
-
parameter_name_suffix="[$eq]",
|
|
137
|
-
parameter_name_suffix_additional_params="[$eq]",
|
|
138
|
-
)
|
|
139
|
-
except HttpCompareError as e:
|
|
140
|
-
self.verbose(f"Encountered HttpCompareError Sending Compare Baseline: {e}")
|
|
141
|
-
|
|
142
|
-
if nosqli_negation_baseline:
|
|
143
|
-
try:
|
|
144
|
-
(
|
|
145
|
-
nosqli_negate_comparison,
|
|
146
|
-
nosqli_negate_diff_reasons,
|
|
147
|
-
nosqli_negate_reflection,
|
|
148
|
-
nosqli_negate_response,
|
|
149
|
-
) = await self.compare_probe(
|
|
150
|
-
nosqli_negation_baseline,
|
|
151
|
-
self.event.data["type"],
|
|
152
|
-
f"{probe_value}'",
|
|
153
|
-
cookies,
|
|
154
|
-
additional_params_populate_empty=True,
|
|
155
|
-
parameter_name_suffix="[$ne]",
|
|
156
|
-
parameter_name_suffix_additional_params="[$ne]",
|
|
157
|
-
)
|
|
158
|
-
if nosqli_negate_response:
|
|
159
|
-
if not nosqli_negate_comparison and nosqli_negate_diff_reasons != ["header"]:
|
|
160
|
-
# If we are about to report a finding, rule out a false positive from unstable URL by sending another probe with the baseline values, and ensure those dont also come back as different
|
|
161
|
-
(
|
|
162
|
-
nosqli_negate_comfirm_comparison,
|
|
163
|
-
nosqli_negate_confirm_diff_reasons,
|
|
164
|
-
nosqli_negate_confirm_reflection,
|
|
165
|
-
nosqli_negate_confirm_response,
|
|
166
|
-
) = await self.compare_probe(
|
|
167
|
-
nosqli_negation_baseline,
|
|
168
|
-
self.event.data["type"],
|
|
169
|
-
f"{probe_value}'",
|
|
170
|
-
cookies,
|
|
171
|
-
additional_params_populate_empty=True,
|
|
172
|
-
parameter_name_suffix="[$eq]",
|
|
173
|
-
parameter_name_suffix_additional_params="[$eq]",
|
|
174
|
-
)
|
|
175
|
-
if nosqli_negate_comfirm_comparison:
|
|
176
|
-
self.results.append(
|
|
177
|
-
{
|
|
178
|
-
"type": "FINDING",
|
|
179
|
-
"description": f"Possible NoSQL Injection. {self.metadata()} Detection Method: [Parameter Name Operator Injection - Negation ([$ne])] Differences: [{'.'.join(nosqli_negate_diff_reasons)}]",
|
|
180
|
-
}
|
|
181
|
-
)
|
|
182
|
-
except HttpCompareError as e:
|
|
183
|
-
self.verbose(f"Encountered HttpCompareError Sending Compare Probe: {e}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|