Multiple vulnerabilities in WordPress IgniteUp/Coming Soon and Maintenance Mode plugin.

The WordPress IgniteUp/Coming Soon and Maintenance Mode plugin, which has 30,000+ active installations, was prone to multiple vulnerabilities in version 3.4 and below that could lead to arbitrary file deletion, stored XSS, information disclosure, HTML injection in email and CSRF, among a few other issues.

Reference

CVE-2019-17234, CVE-2019-17235, CVE-2019-17236, CVE-2019-17237

Arbitrary File Deletion

In the “includes/class-coming-soon-creator.php” script, the plugin registers the deleteTemplate function via the admin_init hook, which can be triggered by any user, authenticated or not:

add_action('admin_init', array($this, 'deleteTemplate'));
...
...
public function deleteTemplate()
{
   if (!isset($_POST['delete_template']) || empty($_POST['delete_template']))
      return;
   $folder_name = $_POST['delete_template'];
   $path = dirname(CSCS_FILE) . '/includes/templates/';
   array_map('unlink', glob($path . $folder_name . '/*.*'));
   rmdir($path . $folder_name);
   unlink($path . '/' . $folder_name . '.php');
   header('Location: ' . $_SERVER['REQUEST_URI']);
}

The function takes the $_POST['delete_template'] input and deletes the corresponding template files and folders. Because it doesn’t check the user capabilities and lacks a security nonce, an unauthenticated attacker could execute that function to delete any file or folder on the website.

HTML injection in email messages & CSRF

IgniteUp has a contact form that can be used by both authenticated and unauthenticated users, via the WordPress AJAX API (wp_ajax_nopriv_* and wp_ajax_* hooks):

add_action('wp_ajax_nopriv_contact_form', array($this, 'sendContactForm'));
add_action('wp_ajax_contact_form', array($this, 'sendContactForm'));
...
...
public function sendContactForm()
{
   $name = trim($_REQUEST['contact_name']);
   $email = trim($_REQUEST['contact_email']);
   $subject = isset($_REQUEST['contact_subject']) ? trim($_REQUEST['contact_subject']) : NULL;
   $message = trim($_REQUEST['contact_message']);
   // Create the email and send the message
   $cs_receive_email_addr = CSCS_GENEROPTION_PREFIX . 'receive_email_addr';
   $admin_email = get_bloginfo('admin_email');
   $to = get_option($cs_receive_email_addr, $admin_email);
   $email_subject = sprintf(__('%1$s Sent you a Message via %2$s Contact Form', CSCS_TEXT_DOMAIN), $name, get_bloginfo('name'));
   $subjectHTML = isset($subject) ? "<h3>" . nl2br(str_replace("\'", "'", $subject)) . "</h3>" : '';
   $email_body = "<html><body>" .
      $subjectHTML .
      "<p>" . nl2br(str_replace("\'", "'", $message)) . "</p><hr>" .
      "<h4>" . __('This message was sent to you via IgniteUp Contact Form', CSCS_TEXT_DOMAIN) . "</h4>" .
      "<p>" . __('Name:', CSCS_TEXT_DOMAIN) . " $name<br>" .
      __('Email:', CSCS_TEXT_DOMAIN) . " $email</p>" .
      "<p>" . __('You can reply the sender directly by replying to this email.', CSCS_TEXT_DOMAIN) . "</p>" .
      "</body></html>";
   $url = get_bloginfo('url');
   $status = wp_mail($to, $email_subject, $email_body, array("Reply-To: $email", "MIME-Version: 1.0", "Content-Type: text/html; charset=UTF-8"));
   echo json_encode(array('success' => $status));
   wp_die();
}

The function doesn’t validate or sanitize the user input. Because IgniteUp forwards all email messages sent from the contact form to the admin mailbox using HTML format, an unauthenticated attacker could inject HTML code into the email body, for instance, a link pointing to a malicious login page URL in an attempt to steal the administrator credentials:

A CSRF attack could also be used against an authenticated administrator in order to trick the victim into performing an undesired action.

Stored XSS

The plugin registers the subscribeEmail function via the WordPress AJAX API:

add_action('wp_ajax_nopriv_subscribe_email', array($this, 'subscribeEmail'));
add_action('wp_ajax_subscribe_email', array($this, 'subscribeEmail'));
...
...
public function subscribeEmail()
{
   $email = isset($_REQUEST['cs_email']) ? trim($_REQUEST['cs_email']) : '';
   $errorInput = CSAdminOptions::getDefaultStrings('alert_error_invalid_email');
   if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
      echo json_encode(array('status' => FALSE, 'error' => TRUE, 'message' => $errorInput));
      wp_die();
   }
   $name = '';
   if (!empty($_REQUEST['cs_name']))
      $name = $_REQUEST['cs_name'];

The function doesn’t validate the $_REQUEST['cs_name'] before saving it. It will be echoed, unsanitized, in the “includes/views/admin-subscribers.php” script:

<?php foreach ($subs as $sub): ?>
   <tr>
      <th class="check-column">
         <input type="checkbox" name="subscriber[]" value="<?php echo $sub->id; ?>">
       </th>
       <td><?php echo empty($sub->name) ? _e('UNKNOWN', CSCS_TEXT_DOMAIN) : $sub->name; ?></td>
       <td><?php echo $sub->email; ?></td>
       <td class="alignright"><a class="button" href="<?php echo admin_url('admin.php?page=cscs_subscribers&action=trash&subscriber%5B%5D=') . $sub->id; ?>"><?php _e('Remove', CSCS_TEXT_DOMAIN); ?></a></td>
    </tr>
<?php endforeach; ?>

An unauthenticated attacker could inject JavaScript code in the WordPress admin back-end:

In addition, the attacker could inject HTML code in the email body similarly to the above HTML injection vulnerability.

Information Disclosure

The plugin registers the createCsvFile and createBccFile functions via the same admin_init hook:

add_action('admin_init', array($this, 'createCsvFile'));
add_action('admin_init', array($this, 'createBccFile'));
...
...
public function createCsvFile()
{
   if (!isset($_GET['rockython_createcsv']) || !isset($_GET['sub']))
      return;
   global $wpdb;
   $subs = $wpdb->get_results("SELECT * FROM " . CSCS_DBTABLE_PREFIX . CSCS_DBTABLE_SUBSCRIPTS);
   $csv_array = array();
   $csv_array[] = array('Name', 'Email');

   foreach ($subs as $sub) :
      $csv_array[] = array(!empty($sub->name) ? $sub->email : '', !empty($sub->email) ? $sub->email : '');
   endforeach;
   $this->convertToCsv($csv_array, 'igniteup_subscribers_' . time() . '.csv', ',');
   exit();
}

public function createBccFile()
{
   if (!isset($_GET['rockython_createbcc']) || !isset($_GET['sub']))
      return;

   $textTitle = 'igniteup_subscribers_' . time() . '.txt';
   global $wpdb;
   $subs = $wpdb->get_results("SELECT * FROM " . CSCS_DBTABLE_PREFIX . CSCS_DBTABLE_SUBSCRIPTS);

   $bccArray = array();
   foreach ($subs as $reg) :
      $bccArray[] = $reg->name . ' <' . $reg->email . '>';
   endforeach;
   header('Content-type: text/plain; charset=utf-8');
   header('Content-Disposition: attachement; filename="' . $textTitle . '";');
   echo implode(", ", $bccArray);
   exit();
}

The two functions will return the subscribers email address in different formats. Here too, they don’t check the user capabilities and lack a security nonce. An unauthenticated attacker could download all email addresses.

Additional Issues

In the same script, the removeSubscribers and activateTemplate functions can too be accessed by an unauthenticated user and can be used to delete subscribers or switch the plugin’s template.

Timeline

The vulnerabilities were reported to the wordpress.org team on September 20, 2019 and a new version 3.4.1 was released on November 08, 2019.

Recommendations

Update as soon as possible if you have version 3.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 vulnerability.

Stay informed about the latest vulnerabilities in WordPress plugins and themes: @nintechnet