The WordPress Simple 301 Redirects Addon Bulk Uploader plugin, which has 20,000+ active installations, was prone to a unauthenticated option changes vulnerability that could allow an attacker to redirect all pages and posts of the blog to a malicious website, as well as an authenticated options export/deletion vulnerability.
Reference
CVE-2019-15818
Summary
Simple 301 Redirects Addon Bulk Uploader is an addon to Simple 301 Redirects (which is required to run it), and allows users to import redirected URLs from a CSV file.
Unauthenticated option changes
In the main script “simple-301-bulk-uploader.php” line 240, the plugin checks if the $_POST['submit_bulk_301']
variable is set, whenever the plugin loads. If it is set, it will call “save_bulk_redirects” and pass on to the function the $_FILES['301_bulk_redirects']
variable:
// if submitted, process the data if ( isset( $_POST['submit_bulk_301'] ) ) { $report = $bulk_redirect_plugin->save_bulk_redirects( $_FILES['301_bulk_redirects'], (int) $_POST['auto_detect_end_line'] ); }
Line 114, “save_bulk_redirects” will check if the file is a CSV and will update the plugin settings with its content using the “update_option” function line 170.
/* save the redirects from the options page to the database */ public function save_bulk_redirects( $data, $auto_detect = 0 ) { // Get Current Redirects $current_redirects = get_option( '301_redirects' ); // Get CSV File $allowedExts = array( "csv" ); $temp = explode( ".", $data["name"] ); $extension = end( $temp ); $report = ''; $mime_types = array( 'application/csv', 'application/excel', 'application/ms-excel', 'application/x-excel', 'application/vnd.ms-excel', 'application/vnd.msexcel', 'application/octet-stream', 'application/data', 'application/x-csv', 'application/txt', 'plain/text', 'text/anytext', 'text/csv', 'text/x-csv', 'text/plain', 'text/comma-separated-values' ); if ( in_array( $data["type"], $mime_types ) && ( $data["size"] < 10000000 ) && in_array( $extension, $allowedExts ) ) { if ( $data["error"] > 0 ) { $report .= "Return Code: " . $data["error"] . "<br>"; } else { $report .= "Upload: " . $data["name"] . "<br />"; $report .= "Size: " . ( $data["size"] / 1024 ) . " kB<br /><br />"; $row = 1; if ( $auto_detect == 1 ) { ini_set( 'auto_detect_line_endings', true ); } if ( ( $handle = fopen( $data["tmp_name"], "r" ) ) !== false ) { while( ( $data = fgetcsv( $handle, 1000, "," ) ) !== false ) { $num = count( $data ); $row ++; if ( ! isset( $current_redirects[ $data[0] ] ) && $data[1] !== '' ) { $current_redirects[ $data[0] ] = $data[1]; $report .= "<strong>" . $data[0] . "</strong> " . __( 'was added to redirect to', 's301r-bulk-uploader' ) . $data[1] . "<br />"; } elseif ( ! isset( $current_redirects[ $data[0] ] ) && $data[1] !== '' ) { $report .= "<span style='color: red'><strong>" . $data[0] . "</strong> " . __( 'is missing a corresponding URL to redirect to', 's301r-bulk-uploader' ) . ".</span><br />"; } else { $report .= "<span style='color: red'><strong>" . $data[0] . "</strong> " . __( 'already exists and was not added', 's301r-bulk-uploader' ) . ".</span><br />"; } } fclose( $handle ); update_option( '301_redirects', $current_redirects );
Because there’s no capability check and no security nonce, an unauthenticated attacker can upload a CSV file to redirect all pages to a malicious website (cross-domain redirection is allowed). The redirection is performed via the “Location:” header:
$ curl https://example.org/ -I HTTP/1.1 301 Moved Permanently Server: nginx/1.14.2 Date: Sat, 27 Jul 2019 13:51:14 GMT Content-Type: text/html; charset=UTF-8 Connection: keep-alive Location: https://evil.com/
Authenticated options export/deletion
In the same “simple-301-bulk-uploader.php” script, lines 234 and 237, the plugin uses the admin_action_*
hook to load 2 functions: export_bulk_redirects
and clear_301_redirects
// Create export action add_action( 'admin_action_bulk301export', array( $bulk_redirect_plugin, 'export_bulk_redirects' ) ); // Create clear action add_action( 'admin_action_bulk301clearlist', array( $bulk_redirect_plugin, 'clear_301_redirects' ) );
Here too, there’s no capability check and no security nonce either:
/* Export redirects to CSV */ public function export_bulk_redirects() { // Get Current Redirects $current_redirects = get_option( '301_redirects' ); header( 'Content-Type: application/excel' ); header( 'Content-Disposition: attachment; filename="301_redirects.csv"' ); $data = array(); foreach ( $current_redirects as $old_url => $new_url ) { $data[] = array( $old_url, $new_url ); } $fp = fopen( 'php://output', 'w' ); foreach ( $data as $line ) { fputcsv( $fp, $line ); } fclose( $fp ); } /* Clear 301 Redirects list */ public function clear_301_redirects() { update_option( '301_redirects', '' ); header( "Location: " . $_SERVER['HTTP_REFERER'] ); }
Because the admin_action_*
hook can be triggered by any logged-in user, accessing the following URLs would export and clear the plugin settings:
wp-admin/admin.php?action=bulk301export wp-admin/admin.php?action=bulk301clearlist
Timeline
The vulnerability was discovered and reported to the wordpress.org team on July 27, 2019.
Recommendations
Update as soon as possible if you have version 1.2.4 or below installed.
If you are using our web application firewall for WordPress, NinjaFirewall WP Edition (free) and NinjaFirewall WP+ Edition (premium), you are protected against this type of vulnerability.
Stay informed about the latest vulnerabilities in WordPress plugins and themes: @nintechnet