The WordPress landscape is dotted with powerful plugins, but sometimes features can backfire when they open the door to attackers. One such case is CVE-2022-3384, a high-impact vulnerability found in the popular Ultimate Member plugin (up to and including version 2.5.). This post will walk you through what happened, how it works, and how you (or a threat actor) could exploit it. At the end, you’ll find key references for further reading.

What is Ultimate Member?

Ultimate Member is a widely-used plugin that helps WordPress sites handle user profiles and memberships. It’s installed on over 200,000 sites, making any security issue a big deal.

Vulnerability: Remote Code Execution (RCE)

- CVE: CVE-2022-3384
- Impact: Authenticated admins can execute certain PHP functions like phpinfo(), leading to information disclosure or function abuse.

The Root Cause

The plugin includes a method called populate_dropdown_options which is supposed to help dynamically generate dropdown options for forms. However, it takes user-supplied input and passes it straight to PHP's call_user_func() without sufficient validation.

Here’s what the code roughly looks like

// Vulnerable code inside ultimate-member/includes/core/class-fields.php

public function populate_dropdown_options() {
    $source = $_POST['source']; // User input
    $args = isset($_POST['args']) ? $_POST['args'] : array(); // Parameters (not really used)
    // Here's the bad call — $source is user-controlled
    $options = call_user_func($source, $args);
    // ...further processing...
}


call_user_func() allows for dynamic invocation of PHP functions. When the function name can be set by the user ($source), that’s a big risk!

Exploitation: Step by Step

An attacker needs to be logged in as an admin. That's important: regular users or visitors can't exploit this. If compromised admin credentials, a rogue plugin, or a malicious admin is involved, they could escalate quickly.

Why only certain functions?

Normally, you’d hope to call something evil like system('id'), but only function names, not user-supplied arguments, get passed through (because of parameter handling in the code).

So, you’re restricted to “parameterless” PHP functions, or functions that don't require complex input. The favorite here is phpinfo(), which dumps tons of sensitive server info.

Here’s a simple curl POST request an attacker could use to trigger the vulnerability

curl -i -s -k -X $'POST' \
    -H $'Cookie: wordpress_admin_cookie_goes_here' \
    -H $'Content-Type: application/x-www-form-urlencoded' \
    --data-binary $'source=phpinfo' \
    'https://target-site.com/wp-admin/admin-ajax.php?action=um_admin_field_populate';

Real-World Impact

- Information Disclosure: Attackers can harvest sensitive server info, making further attacks easier.
- Privilege Abuse: If an attacker obtains admin credentials via phishing or leaks, they can easily leverage this for privilege escalation.
- Persistence: Attackers could hunt for additional misconfigurations or debug settings to further compromise the system.

A dangerous pattern

// BAD PRACTICE: Passing user input directly to call_user_func
call_user_func($_POST['source']);

A safe alternative

// GOOD PRACTICE: Use a whitelist
$allowed_functions = ['get_some_data', 'another_safe_func'];
$func = $_POST['source'];
if (in_array($func, $allowed_functions, true)) {
    call_user_func($func);
}

Mitigation

- Upgrade now: Ultimate Member has patched this as of v2.5.1. Update immediately!

References

- Original Advisory on Wordfence  
- WPScan Vulnerability Database Entry  
- CVE-2022-3384 at MITRE  
- Official Ultimate Member Changelog

Conclusion

CVE-2022-3384 shows why even “small” coding patterns — like making a PHP callback too dynamic — can have BIG security consequences. Even though this particular exploit “only” works for admins and doesn’t allow arbitrary code with custom parameters, it’s still a powerful reminder to validate and whitelist all user inputs.

If you’re running Ultimate Member, upgrade now, review your users, and look for bad patterns in your own code. Don’t let convenience outweigh security!


*Have questions or want to see more deep dives like this? Leave a comment below!*

Timeline

Published on: 11/29/2022 21:15:00 UTC
Last modified on: 12/01/2022 20:28:00 UTC