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.
- bbot/__init__.py +1 -1
- bbot/core/event/base.py +64 -4
- bbot/core/helpers/diff.py +10 -7
- bbot/core/helpers/helper.py +5 -1
- bbot/core/helpers/misc.py +48 -11
- bbot/core/helpers/regex.py +4 -0
- bbot/core/helpers/regexes.py +45 -8
- bbot/core/helpers/url.py +21 -5
- bbot/core/helpers/web/client.py +25 -5
- bbot/core/helpers/web/engine.py +9 -1
- bbot/core/helpers/web/envelopes.py +352 -0
- bbot/core/helpers/web/web.py +10 -2
- bbot/core/helpers/yara_helper.py +50 -0
- bbot/core/modules.py +23 -7
- bbot/defaults.yml +26 -1
- bbot/modules/base.py +4 -2
- bbot/modules/{deadly/dastardly.py → dastardly.py} +1 -1
- bbot/modules/{deadly/ffuf.py → ffuf.py} +1 -1
- bbot/modules/ffuf_shortnames.py +1 -1
- bbot/modules/httpx.py +14 -0
- bbot/modules/hunt.py +24 -6
- bbot/modules/internal/aggregate.py +1 -0
- bbot/modules/internal/excavate.py +356 -197
- bbot/modules/lightfuzz/lightfuzz.py +203 -0
- bbot/modules/lightfuzz/submodules/__init__.py +0 -0
- bbot/modules/lightfuzz/submodules/base.py +312 -0
- bbot/modules/lightfuzz/submodules/cmdi.py +106 -0
- bbot/modules/lightfuzz/submodules/crypto.py +474 -0
- bbot/modules/lightfuzz/submodules/nosqli.py +183 -0
- bbot/modules/lightfuzz/submodules/path.py +154 -0
- bbot/modules/lightfuzz/submodules/serial.py +179 -0
- bbot/modules/lightfuzz/submodules/sqli.py +187 -0
- bbot/modules/lightfuzz/submodules/ssti.py +39 -0
- bbot/modules/lightfuzz/submodules/xss.py +191 -0
- bbot/modules/{deadly/nuclei.py → nuclei.py} +1 -1
- bbot/modules/paramminer_headers.py +2 -0
- bbot/modules/reflected_parameters.py +80 -0
- bbot/modules/{deadly/vhost.py → vhost.py} +2 -2
- bbot/presets/web/lightfuzz-heavy.yml +16 -0
- bbot/presets/web/lightfuzz-light.yml +20 -0
- bbot/presets/web/lightfuzz-medium.yml +14 -0
- bbot/presets/web/lightfuzz-superheavy.yml +13 -0
- bbot/presets/web/lightfuzz-xss.yml +21 -0
- bbot/presets/web/paramminer.yml +8 -5
- bbot/scanner/preset/args.py +26 -0
- bbot/scanner/scanner.py +6 -0
- bbot/test/test_step_1/test__module__tests.py +1 -1
- bbot/test/test_step_1/test_helpers.py +7 -0
- bbot/test/test_step_1/test_presets.py +2 -2
- bbot/test/test_step_1/test_web.py +20 -0
- bbot/test/test_step_1/test_web_envelopes.py +343 -0
- bbot/test/test_step_2/module_tests/test_module_excavate.py +404 -29
- bbot/test/test_step_2/module_tests/test_module_httpx.py +29 -0
- bbot/test/test_step_2/module_tests/test_module_hunt.py +18 -1
- bbot/test/test_step_2/module_tests/test_module_lightfuzz.py +1947 -0
- bbot/test/test_step_2/module_tests/test_module_paramminer_getparams.py +4 -1
- bbot/test/test_step_2/module_tests/test_module_paramminer_headers.py +46 -2
- bbot/test/test_step_2/module_tests/test_module_reflected_parameters.py +226 -0
- bbot/wordlists/paramminer_parameters.txt +0 -8
- {bbot-2.4.2.dist-info → bbot-2.4.2.6590rc0.dist-info}/METADATA +2 -1
- {bbot-2.4.2.dist-info → bbot-2.4.2.6590rc0.dist-info}/RECORD +64 -42
- {bbot-2.4.2.dist-info → bbot-2.4.2.6590rc0.dist-info}/LICENSE +0 -0
- {bbot-2.4.2.dist-info → bbot-2.4.2.6590rc0.dist-info}/WHEEL +0 -0
- {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": {
|
|
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)
|