Helper to execute an exploit, auxiliary, or post module as a background job via RPC. Includes polling logic for exploit sessions.
(
module_type: str,
module_name: str, # Can be full path or base name
module_options: Dict[str, Any],
payload_spec: Optional[Union[str, Dict[str, Any]]] = None # Payload name or {name: ..., options: ...}
)
| 452 | raise ValueError(f"Failed to set option '{k}' to '{original_value}': {e}") from e |
| 453 | |
| 454 | async def _execute_module_rpc( |
| 455 | module_type: str, |
| 456 | module_name: str, # Can be full path or base name |
| 457 | module_options: Dict[str, Any], |
| 458 | payload_spec: Optional[Union[str, Dict[str, Any]]] = None # Payload name or {name: ..., options: ...} |
| 459 | ) -> Dict[str, Any]: |
| 460 | """ |
| 461 | Helper to execute an exploit, auxiliary, or post module as a background job via RPC. |
| 462 | Includes polling logic for exploit sessions. |
| 463 | """ |
| 464 | client = get_msf_client() |
| 465 | module_obj = await _get_module_object(module_type, module_name) # Handles path variants |
| 466 | full_module_path = getattr(module_obj, 'fullname', f"{module_type}/{module_name}") # Get canonical name |
| 467 | |
| 468 | await _set_module_options(module_obj, module_options) |
| 469 | |
| 470 | payload_obj_to_pass = None |
| 471 | payload_name_for_log = None |
| 472 | payload_options_for_log = None |
| 473 | |
| 474 | # Prepare payload if needed (primarily for exploits, also used by start_listener) |
| 475 | if module_type == 'exploit' and payload_spec: |
| 476 | if isinstance(payload_spec, str): |
| 477 | payload_name_for_log = payload_spec |
| 478 | # Passing name string directly is supported by exploit.execute |
| 479 | payload_obj_to_pass = payload_name_for_log |
| 480 | logger.info(f"Executing {full_module_path} with payload '{payload_name_for_log}' (passed as string).") |
| 481 | elif isinstance(payload_spec, dict) and 'name' in payload_spec: |
| 482 | payload_name = payload_spec['name'] |
| 483 | payload_options = payload_spec.get('options', {}) |
| 484 | payload_name_for_log = payload_name |
| 485 | payload_options_for_log = payload_options |
| 486 | try: |
| 487 | payload_obj = await _get_module_object('payload', payload_name) |
| 488 | await _set_module_options(payload_obj, payload_options) |
| 489 | payload_obj_to_pass = payload_obj # Pass the configured payload object |
| 490 | logger.info(f"Executing {full_module_path} with configured payload object for '{payload_name}'.") |
| 491 | except (ValueError, MsfRpcError) as e: |
| 492 | logger.error(f"Failed to prepare payload object for '{payload_name}': {e}") |
| 493 | return {"status": "error", "message": f"Failed to prepare payload '{payload_name}': {e}"} |
| 494 | else: |
| 495 | logger.warning(f"Invalid payload_spec format: {payload_spec}. Expected string or dict with 'name'.") |
| 496 | return {"status": "error", "message": "Invalid payload specification format."} |
| 497 | |
| 498 | logger.info(f"Executing module {full_module_path} as background job via RPC...") |
| 499 | try: |
| 500 | if module_type == 'exploit': |
| 501 | exec_result = await asyncio.to_thread(lambda: module_obj.execute(payload=payload_obj_to_pass)) |
| 502 | else: # auxiliary, post |
| 503 | exec_result = await asyncio.to_thread(lambda: module_obj.execute()) |
| 504 | |
| 505 | logger.info(f"RPC execute() result for {full_module_path}: {exec_result}") |
| 506 | |
| 507 | # --- Process Execution Result --- |
| 508 | if not isinstance(exec_result, dict): |
| 509 | logger.error(f"Unexpected result type from {module_type} execution: {type(exec_result)} - {exec_result}") |
| 510 | return {"status": "error", "message": f"Unexpected result from module execution: {exec_result}", "module": full_module_path} |
| 511 |
no test coverage detected