bbot 2.4.2.6638rc0__py3-none-any.whl → 2.4.2.6653rc0__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/deadly/medusa.py +235 -0
- bbot/test/test_step_2/module_tests/test_module_medusa.py +50 -0
- {bbot-2.4.2.6638rc0.dist-info → bbot-2.4.2.6653rc0.dist-info}/METADATA +1 -1
- {bbot-2.4.2.6638rc0.dist-info → bbot-2.4.2.6653rc0.dist-info}/RECORD +8 -6
- {bbot-2.4.2.6638rc0.dist-info → bbot-2.4.2.6653rc0.dist-info}/LICENSE +0 -0
- {bbot-2.4.2.6638rc0.dist-info → bbot-2.4.2.6653rc0.dist-info}/WHEEL +0 -0
- {bbot-2.4.2.6638rc0.dist-info → bbot-2.4.2.6653rc0.dist-info}/entry_points.txt +0 -0
bbot/__init__.py
CHANGED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from bbot.modules.base import BaseModule
|
|
3
|
+
from bbot.errors import WordlistError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class medusa(BaseModule):
|
|
7
|
+
watched_events = ["PROTOCOL"]
|
|
8
|
+
produced_events = ["VULNERABILITY"]
|
|
9
|
+
flags = ["active", "aggressive", "deadly"]
|
|
10
|
+
per_host_only = True
|
|
11
|
+
meta = {
|
|
12
|
+
"description": "Medusa SNMP bruteforcing with v1, v2c and R/W check.",
|
|
13
|
+
"created_date": "2025-05-16",
|
|
14
|
+
"author": "@christianfl",
|
|
15
|
+
}
|
|
16
|
+
scope_distance_modifier = None
|
|
17
|
+
|
|
18
|
+
options = {
|
|
19
|
+
"snmp_wordlist": "https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Discovery/SNMP/common-snmp-community-strings.txt",
|
|
20
|
+
"snmp_versions": ["1", "2C"], # Only 1 and 2C are available with medusa 2.3.
|
|
21
|
+
"wait_microseconds": 200,
|
|
22
|
+
"timeout_s": 5,
|
|
23
|
+
"threads": 5,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
options_desc = {
|
|
27
|
+
"snmp_wordlist": "Wordlist url for SNMP community strings, newline separated (default https://raw.githubusercontent.com/danielmiessler/SecLists/refs/heads/master/Discovery/SNMP/snmp.txt)",
|
|
28
|
+
"snmp_versions": "List of SNMP versions to attempt against the SNMP server (default ['1', '2C'])",
|
|
29
|
+
"wait_microseconds": "Wait time after every SNMP request in microseconds (default 200)",
|
|
30
|
+
"timeout_s": "Wait time for the SNMP response(s) once at the end of all attempts (default 5)",
|
|
31
|
+
"threads": "Number of communities to be tested concurrently (default 5)",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
deps_ansible = [
|
|
35
|
+
{
|
|
36
|
+
"name": "Install build dependencies",
|
|
37
|
+
"package": {
|
|
38
|
+
"name": [
|
|
39
|
+
"autoconf",
|
|
40
|
+
"automake",
|
|
41
|
+
"libtool",
|
|
42
|
+
"gcc",
|
|
43
|
+
"make",
|
|
44
|
+
],
|
|
45
|
+
"state": "present",
|
|
46
|
+
},
|
|
47
|
+
"become": True,
|
|
48
|
+
"ignore_errors": True,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "Get medusa repo",
|
|
52
|
+
"git": {
|
|
53
|
+
"repo": "https://github.com/jmk-foofus/medusa",
|
|
54
|
+
"dest": "#{BBOT_TEMP}/medusa/gitrepo",
|
|
55
|
+
"version": "2.3", # Newest stable, 2025-05-15
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
# The git repo will be copied because during build, files and subfolders get created. That prevents the Ansible git module to cache the repo.
|
|
60
|
+
"name": "Copy medusa repo",
|
|
61
|
+
"copy": {
|
|
62
|
+
"src": "#{BBOT_TEMP}/medusa/gitrepo/",
|
|
63
|
+
"dest": "#{BBOT_TEMP}/medusa/workdir/",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"name": "Build medusa: autoreconf",
|
|
68
|
+
"command": {
|
|
69
|
+
"chdir": "#{BBOT_TEMP}/medusa/workdir",
|
|
70
|
+
"cmd": "autoreconf -f -i",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "Build medusa: configure",
|
|
75
|
+
"command": {
|
|
76
|
+
"chdir": "#{BBOT_TEMP}/medusa/workdir",
|
|
77
|
+
"cmd": "./configure --prefix=#{BBOT_TEMP}/medusa/build",
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "Build medusa: make",
|
|
82
|
+
"command": {
|
|
83
|
+
"chdir": "#{BBOT_TEMP}/medusa/workdir",
|
|
84
|
+
"cmd": "make",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"name": "Build medusa: make install",
|
|
89
|
+
"command": {
|
|
90
|
+
"chdir": "#{BBOT_TEMP}/medusa/workdir",
|
|
91
|
+
"cmd": "make install",
|
|
92
|
+
"creates": "#{BBOT_TEMP}/medusa/build/bin/medusa",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"name": "Install medusa",
|
|
97
|
+
"copy": {
|
|
98
|
+
"src": "#{BBOT_TEMP}/medusa/build/bin/medusa",
|
|
99
|
+
"dest": "#{BBOT_TOOLS}/",
|
|
100
|
+
"mode": "u+x,g+x,o+x",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
async def setup(self):
|
|
106
|
+
# Try to cache wordlist
|
|
107
|
+
try:
|
|
108
|
+
self.snmp_wordlist_path = await self.helpers.wordlist(self.config.get("snmp_wordlist"))
|
|
109
|
+
except WordlistError as e:
|
|
110
|
+
return False, f"Error retrieving wordlist: {e}"
|
|
111
|
+
|
|
112
|
+
self.password_match_regex = re.compile(r"Password:\s*(\S+)")
|
|
113
|
+
self.success_indicator_match_regex = re.compile(r"\[([^\]]+)\]\s*$")
|
|
114
|
+
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
async def filter_event(self, event):
|
|
118
|
+
handled_protocols = ["snmp"] # Could be extended later
|
|
119
|
+
|
|
120
|
+
protocol = event.data["protocol"].lower()
|
|
121
|
+
if not protocol in handled_protocols:
|
|
122
|
+
return False, f"service {protocol} is currently not supported. Only SNMP."
|
|
123
|
+
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
async def handle_event(self, event):
|
|
127
|
+
host = str(event.host)
|
|
128
|
+
port = str(event.port)
|
|
129
|
+
protocol = event.data["protocol"].lower()
|
|
130
|
+
|
|
131
|
+
if protocol == "snmp":
|
|
132
|
+
snmp_versions = self.config.get("snmp_versions")
|
|
133
|
+
|
|
134
|
+
# Medusa must be called for each SNMP version separately after each run finished.
|
|
135
|
+
for snmp_version in snmp_versions:
|
|
136
|
+
command = await self.construct_command(host, port, protocol, snmp_version)
|
|
137
|
+
|
|
138
|
+
result = await self.run_process(command)
|
|
139
|
+
|
|
140
|
+
if result.stderr:
|
|
141
|
+
# Medusa outputs to stderr if a readonly community was found in WRITE mode
|
|
142
|
+
# That's intended behavior
|
|
143
|
+
self.info(f"Medusa stderr: {result.stderr}")
|
|
144
|
+
|
|
145
|
+
async for message in self.parse_output(result.stdout, snmp_version):
|
|
146
|
+
vuln_event = self.create_vuln_event("CRITICAL", message, event)
|
|
147
|
+
await self.emit_event(vuln_event)
|
|
148
|
+
|
|
149
|
+
# else: Medusa supports various protocols which could in theory be implemented later on.
|
|
150
|
+
|
|
151
|
+
async def parse_output(self, output, protocol_version):
|
|
152
|
+
for line in output.splitlines():
|
|
153
|
+
# Print original Medusa output
|
|
154
|
+
self.info(line)
|
|
155
|
+
|
|
156
|
+
if "FOUND" in line:
|
|
157
|
+
# Some credential was guessed
|
|
158
|
+
password_match = self.password_match_regex.search(line)
|
|
159
|
+
password = password_match.group(1) if password_match else None
|
|
160
|
+
|
|
161
|
+
success_indicator_match = self.success_indicator_match_regex.search(line)
|
|
162
|
+
success_indicator = success_indicator_match.group(1) if success_indicator_match else None
|
|
163
|
+
|
|
164
|
+
# Medusa in WRITE mode shows "ERROR" if a readonly community was found. Replace with "READ"
|
|
165
|
+
mode = "R/W" if success_indicator == "success" else "READ" if success_indicator == "ERROR" else "MODE?"
|
|
166
|
+
|
|
167
|
+
message = f"VALID [SNMPV{protocol_version}] CREDENTIALS FOUND: {password} [{mode}]"
|
|
168
|
+
|
|
169
|
+
yield message
|
|
170
|
+
|
|
171
|
+
async def construct_command(self, host, port, protocol, protocol_version):
|
|
172
|
+
# -b Suppress startup banner
|
|
173
|
+
# -v Set verbosity level (4 = Show only errors and credentials)
|
|
174
|
+
# -R Number of attempted retries
|
|
175
|
+
# -M Medusa module to execute (SNMP)
|
|
176
|
+
# -T Number of concurrent hosts
|
|
177
|
+
# -t Number of concurrent login attempts
|
|
178
|
+
# -h Target hostname or ip address
|
|
179
|
+
# -u Username to test (Empty for SNMP)
|
|
180
|
+
# -P Wordlist for passwords
|
|
181
|
+
# -m Module specific parameters:
|
|
182
|
+
# TIMEOUT:<number> Sets the number of seconds to wait for the UDP responses (default: 5 sec).
|
|
183
|
+
# SEND_DELAY:<number> Sets the number of microseconds to wait between sending queries (default: 200 usec).
|
|
184
|
+
# VERSION:<1|2C> Set the SNMP client version.
|
|
185
|
+
# ACCESS:<READ|WRITE> Set level of access to test for with the community string. ("WRITE" does include "READ")
|
|
186
|
+
|
|
187
|
+
# Example command to bruteforce SNMP:
|
|
188
|
+
#
|
|
189
|
+
# medusa -b -v 4 -R 1 -M snmp -T 1 -t 1 -h 127.0.0.1 -u '' -P communities.txt -m VERSION:2C -m SEND_DELAY:1000000 -m ACCESS:WRITE -m TIMEOUT:10
|
|
190
|
+
|
|
191
|
+
cmd = [
|
|
192
|
+
"medusa",
|
|
193
|
+
"-b",
|
|
194
|
+
"-v",
|
|
195
|
+
4,
|
|
196
|
+
"-R",
|
|
197
|
+
1,
|
|
198
|
+
"-M",
|
|
199
|
+
protocol,
|
|
200
|
+
"-T",
|
|
201
|
+
1,
|
|
202
|
+
"-t",
|
|
203
|
+
self.config.get("threads"),
|
|
204
|
+
"-h",
|
|
205
|
+
host,
|
|
206
|
+
"-u",
|
|
207
|
+
"''",
|
|
208
|
+
"-P",
|
|
209
|
+
self.snmp_wordlist_path,
|
|
210
|
+
"-m",
|
|
211
|
+
f"VERSION:{protocol_version}",
|
|
212
|
+
"-m",
|
|
213
|
+
f"SEND_DELAY:{self.config.get('wait_microseconds')}",
|
|
214
|
+
"-m",
|
|
215
|
+
"ACCESS:WRITE",
|
|
216
|
+
"-m",
|
|
217
|
+
f"TIMEOUT:{self.config.get('timeout_s')}",
|
|
218
|
+
]
|
|
219
|
+
|
|
220
|
+
return cmd
|
|
221
|
+
|
|
222
|
+
def create_vuln_event(self, severity, description, source_event):
|
|
223
|
+
host = str(source_event.host)
|
|
224
|
+
port = str(source_event.port)
|
|
225
|
+
|
|
226
|
+
return self.make_event(
|
|
227
|
+
{
|
|
228
|
+
"severity": severity,
|
|
229
|
+
"host": host,
|
|
230
|
+
"port": port,
|
|
231
|
+
"description": description,
|
|
232
|
+
},
|
|
233
|
+
"VULNERABILITY",
|
|
234
|
+
source_event,
|
|
235
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from .base import ModuleTestBase, tempwordlist
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@pytest.fixture
|
|
6
|
+
def mock_medusa_run_process(monkeypatch):
|
|
7
|
+
async def fake_run_process(self, cmd):
|
|
8
|
+
class FakeResult:
|
|
9
|
+
stdout = "ACCOUNT FOUND: [snmp] Host: 127.0.0.1 User: (null) Password: public [ERROR]\n"
|
|
10
|
+
stderr = (
|
|
11
|
+
"ERROR: [snmp.mod] Error processing SNMP response (1).\n"
|
|
12
|
+
"ERROR: [snmp.mod] Community string appears to have only READ access.\n"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
return FakeResult()
|
|
16
|
+
|
|
17
|
+
from bbot.modules.base import BaseModule
|
|
18
|
+
|
|
19
|
+
monkeypatch.setattr(BaseModule, "run_process", fake_run_process)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.mark.usefixtures("mock_medusa_run_process")
|
|
23
|
+
class TestMedusa(ModuleTestBase):
|
|
24
|
+
targets = ["127.0.0.1"]
|
|
25
|
+
temp_snmp_wordlist = tempwordlist(["public", "private, admin"])
|
|
26
|
+
config_overrides = {
|
|
27
|
+
"modules": {
|
|
28
|
+
"medusa": {
|
|
29
|
+
"snmp_versions": ["2C"],
|
|
30
|
+
"timeout_s": 1,
|
|
31
|
+
"snmp_wordlist": str(temp_snmp_wordlist),
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async def setup_after_prep(self, module_test):
|
|
37
|
+
protocol_data = {"host": str(self.targets[0]), "protocol": "snmp", "port": 161}
|
|
38
|
+
|
|
39
|
+
protocol_event = module_test.scan.make_event(
|
|
40
|
+
protocol_data,
|
|
41
|
+
"PROTOCOL",
|
|
42
|
+
parent=module_test.scan.root_event,
|
|
43
|
+
)
|
|
44
|
+
await module_test.module.emit_event(protocol_event)
|
|
45
|
+
|
|
46
|
+
def check(self, module_test, events):
|
|
47
|
+
vuln_events = [e for e in events if e.type == "VULNERABILITY"]
|
|
48
|
+
|
|
49
|
+
assert len(vuln_events) == 1
|
|
50
|
+
assert "VALID [SNMPV2C] CREDENTIALS FOUND: public [READ]" in vuln_events[0].data["description"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: bbot
|
|
3
|
-
Version: 2.4.2.
|
|
3
|
+
Version: 2.4.2.6653rc0
|
|
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=ry8GG3RKeilQSD_tDYqXnGcvjTen69aqRznkuDSezrE,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
|
|
@@ -83,6 +83,7 @@ bbot/modules/code_repository.py,sha256=x70Z45VnNNMF8BPkHfGWZXsZXw_fStGB3y0-8jbP1
|
|
|
83
83
|
bbot/modules/credshed.py,sha256=HAF5wgRGKIIpdMAe4mIAtkZRLmFYjMFyXtjjst6RJ20,4203
|
|
84
84
|
bbot/modules/crt.py,sha256=6Zm90VKXwYYN6Sab0gwwhTARrtnQIqALJTVtFWMMTGk,1369
|
|
85
85
|
bbot/modules/crt_db.py,sha256=xaIm2457_xGJjnKss73l1HpPn7pLPHksVzejsimTfZA,2198
|
|
86
|
+
bbot/modules/deadly/medusa.py,sha256=44psluRg9VFo6WNLKlnTtH66afqhrOF0STBbLhFkHCE,8859
|
|
86
87
|
bbot/modules/dehashed.py,sha256=0lzcqMEgwRmprwurZ2-8Y8aOO4KTueJgpY_vh0DWQwA,5155
|
|
87
88
|
bbot/modules/digitorus.py,sha256=XQY0eAQrA7yo8S57tGncP1ARud-yG4LiWxx5VBYID34,1027
|
|
88
89
|
bbot/modules/dnsbimi.py,sha256=A4cqhvhytmEEd-tY4CgFwMLbsVtMjkRY9238Aj8aVtU,6921
|
|
@@ -378,6 +379,7 @@ bbot/test/test_step_2/module_tests/test_module_jadx.py,sha256=qTBfDc_Iv03n8iGdyL
|
|
|
378
379
|
bbot/test/test_step_2/module_tests/test_module_json.py,sha256=gmlqge5ZJpjVMGs7OLZBsNlSFTTrKnKjIZMIU23o8VQ,3350
|
|
379
380
|
bbot/test/test_step_2/module_tests/test_module_leakix.py,sha256=DQaQsL4ewpuYeygp-sgcvdeOSzvHq77_eYjKcgebS7A,1817
|
|
380
381
|
bbot/test/test_step_2/module_tests/test_module_lightfuzz.py,sha256=g8rPTtjPe90ZkjCEMlNUC2fraqzZu4XK_0GaA7sGI9A,77463
|
|
382
|
+
bbot/test/test_step_2/module_tests/test_module_medusa.py,sha256=vYoAyMf0LbIXCoUzLycOISZtF7M58E30WjuLuqxDiCg,1671
|
|
381
383
|
bbot/test/test_step_2/module_tests/test_module_mysql.py,sha256=4wAPjbjhlxmOkEhQnIQIBC2BLEaE57TX6lChGZ3zLsU,2630
|
|
382
384
|
bbot/test/test_step_2/module_tests/test_module_myssl.py,sha256=zRJ1sOEespWtBx2jA07bW5sHD1XQ9pV0PtHtGogo7Gs,1531
|
|
383
385
|
bbot/test/test_step_2/module_tests/test_module_neo4j.py,sha256=pUUaqxBsF6s11dEDhrETpvlR2pqiUcc0uvH8Z5GvVUQ,1332
|
|
@@ -450,8 +452,8 @@ bbot/wordlists/raft-small-extensions-lowercase_CLEANED.txt,sha256=ZSIVebs7ptMvHx
|
|
|
450
452
|
bbot/wordlists/top_open_ports_nmap.txt,sha256=LmdFYkfapSxn1pVuQC2LkOIY2hMLgG-Xts7DVtYzweM,42727
|
|
451
453
|
bbot/wordlists/valid_url_schemes.txt,sha256=0B_VAr9Dv7aYhwi6JSBDU-3M76vNtzN0qEC_RNLo7HE,3310
|
|
452
454
|
bbot/wordlists/wordninja_dns.txt.gz,sha256=DYHvvfW0TvzrVwyprqODAk4tGOxv5ezNmCPSdPuDUnQ,570241
|
|
453
|
-
bbot-2.4.2.
|
|
454
|
-
bbot-2.4.2.
|
|
455
|
-
bbot-2.4.2.
|
|
456
|
-
bbot-2.4.2.
|
|
457
|
-
bbot-2.4.2.
|
|
455
|
+
bbot-2.4.2.6653rc0.dist-info/LICENSE,sha256=GzeCzK17hhQQDNow0_r0L8OfLpeTKQjFQwBQU7ZUymg,32473
|
|
456
|
+
bbot-2.4.2.6653rc0.dist-info/METADATA,sha256=Cgg94SHZf_ydnUFJcQf5dDnsegOM8OqviLvP7p3-H94,18308
|
|
457
|
+
bbot-2.4.2.6653rc0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
458
|
+
bbot-2.4.2.6653rc0.dist-info/entry_points.txt,sha256=cWjvcU_lLrzzJgjcjF7yeGuRA_eDS8pQ-kmPUAyOBfo,38
|
|
459
|
+
bbot-2.4.2.6653rc0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|