Introduction
A path traversal flaw in the Advanced Members for ACF WordPress plugin allows any authenticated user with Subscriber privileges to delete arbitrary files on the server, including critical configuration files that can lead to full site takeover. Tracked as CVE-2026-3243 with a CVSS score of 8.8, this vulnerability highlights how a single unsanitized input in a file handling function can undermine an entire WordPress installation.
Advanced Members for ACF is a lightweight membership plugin developed by danbilabs, designed specifically for sites using Advanced Custom Fields (ACF). The plugin has a small footprint with approximately 30 active installations, but its integration with ACF places it in the broader WordPress ecosystem where even niche plugins can introduce significant risk if they handle file operations insecurely.
Technical Information
Root Cause: Unsanitized File Paths in create_crop
The vulnerability resides in the create_crop function within class-avatar.php. The core issue is straightforward: user supplied file paths from the $image_data['file'] parameter were concatenated directly into a filesystem path with zero sanitization.
In the vulnerable version (1.2.4), the code looked like this:
// VULNERABLE (v1.2.4, line 641) $file = amem()->files->upload_temp . DIRECTORY_SEPARATOR . $image_data['file'];
An attacker could supply a value like ../../wp-config.php as the file parameter. The plugin would resolve this to a path outside the intended avatar upload directory and subsequently delete the targeted file. This is a textbook CWE-22 (Path Traversal) vulnerability.
Compounding Factor: Open REST API Endpoints
The severity of this vulnerability was amplified by the REST API permission model. In the vulnerable version, all three REST API routes (/upload, /crop, /cancelCrop) used a completely open permission callback:
function(){ return true; }
This meant that the endpoints were accessible to any caller, including unauthenticated visitors. Combined with the path traversal in create_crop, this created a situation where the barrier to exploitation was remarkably low.
Attack Flow
-
Obtain authentication: The attacker registers or compromises a WordPress account with at least Subscriber level access. Given the open permission callbacks on the REST API, even unauthenticated access may have been possible in some configurations.
-
Craft a malicious request: The attacker sends a request to the crop or cancel crop REST API endpoint, supplying a path traversal payload in the file parameter (e.g.,
../../wp-config.php). -
File deletion occurs: The plugin concatenates the unsanitized input with the upload temp directory path, resolving to the target file. The plugin then deletes the file as part of its normal avatar processing workflow.
-
Escalation to RCE: With
wp-config.phpdeleted, WordPress enters its installation setup state. The attacker can then reconfigure the site with their own database credentials and gain full administrative control, effectively achieving remote code execution.
The attack vector is network based (AV:N) and requires no user interaction (UI:N), making it suitable for automated exploitation once the attacker has a valid session.
Additional Vulnerable Patterns
The same unsanitized path concatenation pattern existed in the rest_api_cancel_crop_callback() method, where the $params['file'] parameter was used to construct a file path for deletion without any validation. The $image_data['id'] value, used to generate output filenames, also lacked sanitization.
The vulnerability was discovered by researcher Muhammad Yudha (DJ).
Patch Information
The vulnerability was fully patched in version 1.2.6, released via the WordPress plugin repository (changeset 3492372). A partial fix was attempted in version 1.2.5 (changeset 3479725), but it was incomplete. By comparing the vulnerable source (tag 1.2.4) against the patched source (tag 1.2.6), we can identify five distinct fixes that were applied.
1. Path Traversal Sanitization on File Inputs
The patch applies wp_basename() to strip all directory components and then uses a regex to remove any remaining slash characters:
// PATCHED (v1.2.6) $safe_file = wp_basename( $image_data['file'] ); $safe_file = preg_replace('/[\/\\\\]/', '', $safe_file); $file = amem()->files->upload_temp . DIRECTORY_SEPARATOR . $safe_file;
This same two step sanitization pattern was also applied to the $image_data['id'] value and to the $params['file'] parameter inside rest_api_cancel_crop_callback().
2. realpath() Containment Check
As a defense in depth measure, the patch introduces a realpath() check that confirms the resolved destination file path stays within the expected avatar upload directory:
// PATCHED (v1.2.6) — added after new_file is constructed $avatar_base = realpath($this->get_avatar_dir()); if ( $avatar_base ) { $real_dir_path = realpath(dirname($new_file)); if ( $real_dir_path ) { $real_path = $real_dir_path . DIRECTORY_SEPARATOR . wp_basename($new_file); if ( strpos($real_path, $avatar_base) !== 0 ) { wp_send_json(__('Invalid file path', 'advanced-members'), 400); } } }
If the resolved path does not start with the avatar base directory, the request is rejected with a 400 error.
3. REST API Permission Callback Hardened
The open function(){ return true; } callbacks were replaced with a dedicated rest_api_permission_check() method that verifies a WordPress nonce:
// PATCHED (v1.2.6) 'permission_callback' => [$this, 'rest_api_permission_check'], // New method: public function rest_api_permission_check(\WP_REST_Request $request) { $nonce = $request->get_header('X-AMem-Avatar-Nonce'); if ( empty($nonce) ) { return false; } return (bool) wp_verify_nonce($nonce, 'amem-avatar'); }
4. User Ownership Validation
The vulnerable code trusted the user supplied uid parameter without verifying that the requesting user was operating on their own avatar. The patch adds explicit ownership checks: default avatar operations are restricted to administrators, logged in users can only modify their own avatar (unless they are admins), and unauthenticated callers are blocked from specifying numeric user IDs.
5. SSRF Mitigation
When the source image file was not found locally, create_crop() would fall back to fetching it from a user supplied URL using wp_remote_get(). The patch switches this to wp_safe_remote_get(), which applies WordPress's built in protections against Server Side Request Forgery. The absolute server filesystem path previously leaked in the JSON response was also replaced with a relative path.
Users should update to at least version 1.2.6, or preferably the latest available version (currently 1.2.7).
Affected Systems and Versions
| Component | Details |
|---|---|
| Plugin | Advanced Members for ACF |
| Vulnerable Versions | All versions up to and including 1.2.5 |
| Partially Patched | Version 1.2.5 (incomplete fix) |
| Fully Patched | Version 1.2.6 |
| Latest Available | Version 1.2.7 |
| Platform | WordPress |
| Required Privilege | Subscriber level access or above |
Version 1.2.5 should not be considered a safe endpoint, as the patch applied in that release was incomplete. Only version 1.2.6 and later contain the full set of fixes.
Vendor Security History
Prior to CVE-2026-3243, the Advanced Members for ACF plugin did not have a documented history of security vulnerabilities. However, the scope of fixes applied in version 1.2.6 (open REST API callbacks, lack of nonce verification, missing ownership validation, SSRF exposure, and server path leakage) suggests that security was not a primary design consideration in earlier versions. The vendor's response to the disclosure was notably proactive: danbilabs released version 1.2.6 with comprehensive hardening measures and followed up with version 1.2.7 to address additional Plugin Check Program coding standards.
References
- NVD: CVE-2026-3243
- Wordfence Threat Intel: Advanced Members for ACF Arbitrary File Deletion
- Advanced Members for ACF on WordPress.org
- Vulnerable Source: class-avatar.php (tag 1.2.4)
- Patched Source: class-avatar.php (tag 1.2.6)
- Vulnerable Source: class-avatar.php Line 266
- Vulnerable Source: class-avatar.php Line 57
- Patched Source: class-avatar.php (trunk) Line 710
- Changeset 3479725 (Partial Fix, v1.2.5)
- Changeset 3492372 (Full Fix, v1.2.6)



