CVE-2026-0910

wpForo Forum <= 2.4.13 - Authenticated (Subscriber+) PHP Object Injection

highDeserialization of Untrusted Data
8.8
CVSS Score
8.8
CVSS Score
high
Severity
2.4.14
Patched in
2d
Time to patch

Description

The wpForo Forum plugin for WordPress is vulnerable to PHP Object Injection in all versions up to, and including, 2.4.13 via deserialization of untrusted input in the 'wpforo_display_array_data' function. This makes it possible for authenticated attackers, with Subscriber-level access and above, to inject a PHP Object. No known POP chain is present in the vulnerable software, which means this vulnerability has no impact unless another plugin or theme containing a POP chain is installed on the site. If a POP chain is present via an additional plugin or theme installed on the target system, it may allow the attacker to perform actions like delete arbitrary files, retrieve sensitive data, or execute code depending on the POP chain present.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.4.13
PublishedFebruary 10, 2026
Last updatedFebruary 11, 2026
Affected pluginwpforo

What Changed in the Fix

Changes introduced in v2.4.14

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan targets a PHP Object Injection vulnerability in **wpForo Forum (<= 2.4.13)**. The vulnerability resides in the `wpforo_display_array_data` function, which performs deserialization on untrusted data from user meta or cookies. ### 1. Vulnerability Summary * **Vulnerability:** PHP…

Show full research plan

This research plan targets a PHP Object Injection vulnerability in wpForo Forum (<= 2.4.13). The vulnerability resides in the wpforo_display_array_data function, which performs deserialization on untrusted data from user meta or cookies.

1. Vulnerability Summary

  • Vulnerability: PHP Object Injection
  • Sink: wpforo_display_array_data() (likely using maybe_unserialize() or unserialize() internally).
  • Vulnerable Functionality: This function is a helper used to render array data (User Data, User Meta, Cookies) for display in the admin tools or front-end profile sections (e.g., GDPR data access or debug views).
  • Source: User-controlled data stored in WordPress User Meta or sent via HTTP Cookies.
  • Constraint: A POP chain is required to achieve Remote Code Execution (RCE) or other high-impact actions.

2. Attack Vector Analysis

  • Endpoint: The wpForo front-end profile page or a specific AJAX action that invokes the "Debug" or "Data Display" features.
  • HTTP Parameter: $_COOKIE (specifically cookies prefixed with wpforo_, such as wpforo_read_topics) or metadata updated via the profile.
  • Authentication: Subscriber-level access is sufficient.
  • Target Sink Usage: In admin/tools-tabs/debug.php, the function is called as:
    echo wpforo_display_array_data( $_COOKIE, $keys );
    
    Where $keys include wpforo_read_topics.

3. Code Flow

  1. The plugin registers a way for users to view their own data (often via a "Profile" -> "Account" or "Privacy" tab).
  2. The application invokes a controller that gathers the user's information.
  3. The controller calls wpforo_display_array_data($data, $keys).
  4. Inside wpforo_display_array_data(), the code iterates over the $data array.
  5. For each value, it calls maybe_unserialize($value).
  6. If $value is a malicious serialized string provided in a cookie (like wpforo_read_topics), an object is instantiated, triggering its __wakeup() or __destruct() magic methods.

4. Nonce Acquisition Strategy

The vulnerability is primarily triggered via direct page loads or AJAX requests that process cookies.

  1. Check for Nonce: Determine if the profile/debug view requires a nonce.
  2. Shortcode: wpForo uses the [wpforo] shortcode on its main page.
  3. Extraction:
    • Navigate to the forum homepage (default /community/).
    • Use browser_eval to find localized data.
    • Search for wpforo_vars or similar objects:
      // Example search
      window.wpforo_vars?.nonce 
      
  4. GDPR/Privacy Path: If the trigger is in the Account/Privacy tab, create a page to ensure the UI is loaded:
    • wp post create --post_type=page --post_status=publish --post_content='[wpforo]'

5. Exploitation Strategy

Step 1: Payload Selection

Since no POP chain is inherent to wpForo, use a generic stdClass for verification or a common WordPress core chain (like Requests_Utility_FilteredIterator if available) to trigger an observable effect (like an error or a request).

  • Verification Payload: O:8:"stdClass":1:{s:3:"poc";s:7:"success";}
  • Encoding: The payload must be URL-encoded as it will be placed in a cookie.

Step 2: Trigger via Cookie (Recommended)

  1. Identify the cookie prefix. The wpforo_prefix() function usually returns wp_ or wpforo_.
  2. Set the cookie wpforo_read_topics to the serialized payload.
  3. Request the profile "Debug" or "Account Data" view.

Request Template:

GET /community/?wpforo=profile&view=account HTTP/1.1
Host: target.local
Cookie: wpforo_read_topics=O%3A8%3A%22stdClass%22%3A1%3A%7Bs%3A3%3A%22poc%22%3Bs%3A7%3A%22success%22%3B%7D; [WORDPRESS_AUTH_COOKIES]

Step 3: Trigger via User Meta

  1. Update the user's nickname or another meta field via the profile settings to a serialized string.
  2. Navigate to the page that displays "User Meta Data" using wpforo_display_array_data.

6. Test Data Setup

  1. Install wpForo 2.4.13.
  2. Create a Subscriber User:
    • wp user create attacker attacker@example.com --role=subscriber --user_pass=password
  3. Identify the Forum Page: Find the page containing [wpforo].
  4. Enable Debug/Tools (if necessary): Check if wpforo settings allow members to see their own data logs.

7. Expected Results

  • Successful Injection: The maybe_unserialize function will process the cookie. If a POP chain is used, the associated action (file deletion, DNS request, etc.) will occur.
  • Observation: If an invalid object is injected, it may cause a "Catchable fatal error" or "Object of class stdClass could not be converted to string" error if the plugin tries to echo the result of wpforo_display_array_data.

8. Verification Steps

  1. Monitor Logs: Check the PHP error log for messages related to unserialize().
  2. Verify Deserialization: Use a class that logs its execution to a file or database.
  3. Trace with WP-CLI:
    • wp eval "echo maybe_unserialize('O:8:\"stdClass\":1:{s:3:\"poc\";s:7:\"success\";}')->poc;" (to confirm behavior).

9. Alternative Approaches

  • AJAX Endpoint: Search for wp_ajax_wpforo_get_member_data or similar. If it calls the vulnerable function without proper capability checks, it can be exploited via a POST request to admin-ajax.php.
  • Server Info View: If the "Server" view in debug.php is accessible to non-admins due to a logic flaw, it might process other inputs (though it primarily uses $_SERVER).
  • User Data View: Target the wpfu parameter in debug.php. If a Subscriber can access this tab, they can supply their own ID and trigger the processing of their own (maliciously crafted) meta.
Research Findings
Static analysis — not yet PoC-verified

Summary

The wpForo Forum plugin is vulnerable to PHP Object Injection via the wpforo_display_array_data function, which performs deserialization on untrusted input from cookies and user metadata. Authenticated attackers with Subscriber-level access can exploit this by providing a crafted PHP serialized object, which can lead to remote code execution if a suitable POP chain is available on the target system.

Vulnerable Code

// From admin/tools-tabs/debug.php (implementation of wpforo_display_array_data logic around line 195)
foreach( $array as $k => $v ) {
    if( ( ! empty( $keys ) && ! in_array( $k, $keys ) && strpos( (string) $k, 'capabilities' ) === false ) || $k == 'user_pass' ) continue;
    if( is_serialized( $v ) || is_array( $v ) ) {
        $v      = ( is_array( $v ) ) ? $v : @unserialize( $v );
        $v_html = '';
        if( is_array( $v ) && ! empty( $v ) ) {
            foreach( $v as $kk => $vv ) {
                if( is_serialized( $vv ) || is_array( $vv ) ) {
                    $v_html = '';
                    $vv     = ( is_array( $vv ) ) ? $vv : unserialize( $vv );

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wpforo/2.4.13/admin/tools-tabs/debug.php /home/deploy/wp-safety.org/data/plugin-versions/wpforo/2.4.14/admin/tools-tabs/debug.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wpforo/2.4.13/admin/tools-tabs/debug.php	2025-12-10 09:11:26.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wpforo/2.4.14/admin/tools-tabs/debug.php	2026-01-26 10:09:02.000000000 +0000
@@ -195,13 +195,13 @@
 	foreach( $array as $k => $v ) {
 		if( ( ! empty( $keys ) && ! in_array( $k, $keys ) && strpos( (string) $k, 'capabilities' ) === false ) || $k == 'user_pass' ) continue;
 		if( is_serialized( $v ) || is_array( $v ) ) {
-			$v      = ( is_array( $v ) ) ? $v : @unserialize( $v );
+			$v      = ( is_array( $v ) ) ? $v : @unserialize( $v, [ 'allowed_classes' => false ] );
 			$v_html = '';
 			if( is_array( $v ) && ! empty( $v ) ) {
 				foreach( $v as $kk => $vv ) {
 					if( is_serialized( $vv ) || is_array( $vv ) ) {
 						$v_html = '';
-						$vv     = ( is_array( $vv ) ) ? $vv : unserialize( $vv );
+						$vv     = ( is_array( $vv ) ) ? $vv : unserialize( $vv, [ 'allowed_classes' => false ] );
 						if( $k === wpforo_prefix( 'read_topics' ) ) {
 							$v_html .= "<span class='wpf-sb-data'>" . count( $vv ) . "</span> ";
 						} else {

Exploit Outline

The exploit targets the wpForo front-end profile or debug functionality that displays user-controlled data. 1. Authentication: The attacker authenticates as a Subscriber-level user. 2. Payload Preparation: The attacker crafts a PHP serialized object payload designed to leverage an existing POP chain (from other installed plugins/themes). 3. Injection Vector: The attacker sets a cookie containing the serialized payload, specifically targeting keys known to be processed by wpforo_display_array_data, such as 'wpforo_read_topics' (often prefixed based on site settings). Alternatively, user metadata fields can be updated via the profile settings with serialized data. 4. Execution Trigger: The attacker navigates to the 'Account' or 'Privacy' tab within their wpForo profile (e.g., `/?wpforo=profile&view=account`). When this page loads, the plugin calls wpforo_display_array_data on the malicious cookie or metadata, triggering unserialize() and the subsequent execution of the object's magic methods.

Check if your site is affected.

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