MCPcopy Create free account
hub / github.com/GH05TCREW/MetasploitMCP / run_command_safely

Function run_command_safely

MetasploitMCP.py:218–297  ·  view source on GitHub ↗

Safely run a command on a Metasploit console and return the output. Relies primarily on detecting the MSF prompt for command completion. Args: console: The Metasploit console object (MsfConsole). cmd: The command to run. execution_timeout: Optional specific time

(console: MsfConsole, cmd: str, execution_timeout: Optional[int] = None)

Source from the content-addressed store, hash-verified

216 # else: logger.debug("No console ID obtained, skipping destruction.")
217
218async def run_command_safely(console: MsfConsole, cmd: str, execution_timeout: Optional[int] = None) -> str:
219 """
220 Safely run a command on a Metasploit console and return the output.
221 Relies primarily on detecting the MSF prompt for command completion.
222
223 Args:
224 console: The Metasploit console object (MsfConsole).
225 cmd: The command to run.
226 execution_timeout: Optional specific timeout for this command's execution phase.
227
228 Returns:
229 The command output as a string.
230 """
231 if not (hasattr(console, 'write') and hasattr(console, 'read')):
232 logger.error(f"Console object {type(console)} lacks required methods (write, read).")
233 raise TypeError("Unsupported console object type for command execution.")
234
235 try:
236 logger.debug(f"Running console command: {cmd}")
237 await asyncio.to_thread(lambda: console.write(cmd + '\n'))
238
239 output_buffer = b"" # Read as bytes to handle potential encoding issues and prompt matching
240 start_time = asyncio.get_event_loop().time()
241
242 # Determine read timeout - use inactivity timeout as fallback
243 read_timeout = execution_timeout or (LONG_CONSOLE_READ_TIMEOUT if cmd.strip().startswith(("run", "exploit", "check")) else DEFAULT_CONSOLE_READ_TIMEOUT)
244 check_interval = 0.1 # Seconds between reads
245 last_data_time = start_time
246
247 while True:
248 await asyncio.sleep(check_interval)
249 current_time = asyncio.get_event_loop().time()
250
251 # Check overall timeout first
252 if (current_time - start_time) > read_timeout:
253 logger.warning(f"Overall timeout ({read_timeout}s) reached for console command '{cmd}'.")
254 break
255
256 # Read available data
257 try:
258 chunk_result = await asyncio.to_thread(lambda: console.read())
259 # console.read() returns {'data': '...', 'prompt': '...', 'busy': bool}
260 chunk_data = chunk_result.get('data', '').encode('utf-8', errors='replace') # Ensure bytes
261
262 # Handle the prompt - ensure it's bytes for pattern matching
263 prompt_str = chunk_result.get('prompt', '')
264 prompt_bytes = prompt_str.encode('utf-8', errors='replace') if isinstance(prompt_str, str) else prompt_str
265 except Exception as read_err:
266 logger.warning(f"Error reading from console during command '{cmd}': {read_err}")
267 await asyncio.sleep(0.5) # Wait a bit before retrying or timing out
268 continue
269
270 if chunk_data:
271 # logger.debug(f"Read chunk (bytes): {chunk_data[:100]}...") # Log sparingly
272 output_buffer += chunk_data
273 last_data_time = current_time # Reset inactivity timer
274
275 # Primary Completion Check: Did we receive the prompt?

Calls 2

writeMethod · 0.45
readMethod · 0.45