Saturday, November 21, 2015

PHP static code analysis vs ~1000 top wordpress plugins = 103 vulnerable plugins found


░▒▓█ Introduction


I've been making php static code analysis tool for a while and few months ago I ran it against ~1000 (more or less) top wordpress plugins.

Scanning results were manually verified in my spare time and delivered to official plugins@wordpress.org from 04.07.2015 to 31.08.2015. Most of reported plugins are already patched, some are not. Vulnerable and not patched plugins are already removed from official wordpress plugin repository.

░▒▓█ Results


103 plugins vulnerable with more than 4.000.000 active installations in total (~30.000.000 downloads)

List of reported plugins (original reports contain verification/reproduce sections and urls to plugin wordpress repository entries, where you can also verify changelog) :

  • Cross-Site Scripting (XSS) in Duplicator 0.5.24 [original report - Sat, 15 Aug 2015]
  • Cross-Site Scripting (XSS) in All In One WP Security 3.9.7 [original report - Thu, 13 Aug 2015]
  • Cross-Site Scripting (XSS) in AddThis 5.0.12 [original report - Tue, 11 Aug 2015]
  • Cross-Site Scripting (XSS) in Display Widgets 2.03 [original report - Tue, 11 Aug 2015]
  • Blind SQL injection and XSS in SEO SearchTerms Tagging 2 1.535 [original report - Wed, 8 Jul 2015] NOT PATCHED
  • Blind SQL injection in Pretty Link Lite 1.6.7 [original report - Wed, 8 Jul 2015]
  • Blind SQL injection in WP Statistics 9.4 [original report - Thu, 9 Jul 2015]
  • Cross-Site Scripting (XSS) in My Page Order 4.3 [original report - Thu, 13 Aug 2015] NOT PATCHED
  • Cross-Site Scripting (XSS) in Category Order and Taxonomy Terms Order 1.4.4 [original report - Tue, 18 Aug 2015]
  • Persistent Cross-Site Scripting (XSS) in WP Social Bookmarking Light 1.7.9 [original report - Wed, 19 Aug 2015]
  • Cross-Site Scripting (XSS) in WP Google Fonts v3.1.3 [original report - Wed, 19 Aug 2015]
  • Cross-Site Scripting (XSS) in Easy Table 1.5.2 [original report - Mon, 10 Aug 2015]
  • Cross-Site Scripting (XSS) in My Category Order 4.3 [original report - Thu, 13 Aug 2015] NOT PATCHED
  • Cross-Site Scripting (XSS) in CKEditor for WordPress 4.5.3 [original report - Mon, 31 Aug 2015]
  • Blind SQL injection in Huge IT Slider 2.8.6 [original report - Wed, 22 Jul 2015]
  • Cross-Site Scripting (XSS) in Dynamic Widgets 1.5.10 [original report - Tue, 11 Aug 2015]
  • Cross-Site Scripting (XSS) in Google Language Translator 4.0.9 [original report - Thu, 13 Aug 2015]
  • Cross-Site Scripting (XSS) in JW Player 6 Plugin for Wordpress 2.1.14 [original report - Wed, 19 Aug 2015] NOT PATCHED
  • Persistent Cross-Site Scripting (XSS) in Floating Social Media Icon 2.1 [original report - Wed, 19 Aug 2015]
  • Blind SQL injections in Contact Form Builder 1.0.24 [original report - Tue, 7 Jul 2015]
  • Arbitrary file upload and Cross-Site Scripting (XSS) in Slideshow Gallery 1.5.3 [original report - Thu, 20 Aug 2015]
  • Blind SQL injection in Master Slider 2.5.1 [original report - Thu, 20 Aug 2015]
  • Blind SQL injection and Reflected XSS in WP RSS Multi Importer 3.15 [original report - Wed, 8 Jul 2015] NOT PATCHED
  • Cross-Site Scripting (XSS) in Add Link to Facebook 2.2.7 [original report - Thu, 13 Aug 2015] NOT PATCHED
  • Blind SQL injection in 404 to 301 2.0.2 [original report - Thu, 20 Aug 2015]
  • Cross-Site Scripting (XSS) in Alpine PhotoTile for Instagram 1.2.7.5 [original report - Thu, 20 Aug 2015]
  • Cross-Site Scripting (XSS) in Huge IT Image Gallery 1.5.1 [original report - Thu, 20 Aug 2015]
  • Persistent Cross-Site Scripting (XSS) in Visitor Maps and Who's Online 1.5.8.6 [original report - Thu, 20 Aug 2015]
  • Cross-Site Scripting (XSS) in WP Google Map Plugin 2.3.9 [original report - Thu, 20 Aug 2015]
  • Cross-Site Scripting (XSS) in WP Job Manager 1.23.7 [original report - Thu, 20 Aug 2015]
  • SQL injection (+XSS) in Easy Social Icons 1.2.3.1 [original report - Wed, 22 Jul 2015]
  • Cross-Site Scripting (XSS) in My Link Order 4.3 [original report - Thu, 13 Aug 2015] NOT PATCHED
  • Persistent Cross-Site Scripting (XSS) in WP Database Backup 3.3 [original report - Thu, 20 Aug 2015]
  • Arbitrary file upload and Reflected Cross-Site Scripting (XSS) in Theme Test Drive 2.9 [original report - Thu, 20 Aug 2015]
  • Cross-Site Scripting (XSS) in Subscribe to Comments Reloaded 150611 [original report - Thu, 20 Aug 2015]
  • Cross-Site Scripting (XSS) in SEO Redirection 2.8 [original report - Fri, 21 Aug 2015]
  • Cross-Site Scripting (XSS) in qTranslate-X 3.4.3 [original report - Fri, 21 Aug 2015]
  • Blind SQL injection in Gallery Bank Lite Edition Version 3.0.229 [original report - Fri, 21 Aug 2015]
  • Persistent Cross-Site Scripting (XSS) in Crazy Bone 0.5.5 [original report - Fri, 21 Aug 2015]
  • Persistent Cross-Site Scripting (XSS) in Social Media Widget by Acurax 2.2 [original report - Fri, 21 Aug 2015]
  • Reflected Cross-Site Scripting (XSS) in Anti-spam by CleanTalk 5.21 [original report - Tue, 25 Aug 2015]
  • Blind SQL injection in Smooth Slider 2.6.5 [original report - Wed, 15 Jul 2015]
  • Cross-Site Scripting (XSS) in YITH Maintenance Mode 1.1.4 [original report - Fri, 21 Aug 2015]
  • Multiple SQL injections and XSS in Email Subscribers 2.9 [original report - Mon, 10 Aug 2015]
  • Cross Site Scripting (XSS) in Email newsletter 20.13.6 [original report - Mon, 10 Aug 2015] NOT PATCHED
  • Cross-Site Scripting (XSS) in Easy Pie Coming Soon 1.0.0 [original report - Mon, 10 Aug 2015]
  • Cross-Site Scripting (XSS) in Easy Pie Coming Soon 1.0.0 [original report - Mon, 10 Aug 2015]
  • Cross-Site Scripting (XSS) in Contact Bank Lite Edition 2.0.225 [original report - Thu, 13 Aug 2015]
  • Cross-Site Scripting (XSS) in Kiwi Logo Carousel 1.7.1 [original report - Thu, 13 Aug 2015]
  • Persistent Cross-Site Scripting (XSS) in WP Legal Pages 1.0.1 [original report - Fri, 21 Aug 2015]
  • Cross-Site Scripting (XSS) in WP Crontrol 1.2.3 [original report - Fri, 21 Aug 2015]
  • Cross-Site Scripting (XSS) in Websimon Tables 1.3.4 [original report - Fri, 21 Aug 2015] NOT PATCHED
  • Cross-Site Scripting (XSS) in SEO Rank Reporter 2.2.2 [original report - Mon, 24 Aug 2015] NOT PATCHED
  • Persistent Cross-Site Scripting (XSS) in WP Keyword Link 1.7 [original report - Mon, 24 Aug 2015] NOT PATCHED
  • Cross-Site Scripting (XSS) in Huge IT Portfolio Gallery 1.5.7 [original report - Mon, 24 Aug 2015]
  • Cross-Site Scripting (XSS) in Manual Image Crop 1.10 [original report - Mon, 24 Aug 2015]
  • Cross-Site Scripting (XSS) in iQ Block Country in 1.1.19 [original report - Mon, 24 Aug 2015]
  • SQL injection and Cross-Site Scripting (XSS) in GigPress 2.3.10 [original report - Mon, 24 Aug 2015]
  • Arbitrary file read in Multi Plugin Installer 1.1.0 [original report - Mon, 24 Aug 2015]
  • Persistent Cross-Site Scripting (XSS) in FV Wordpress Flowplayer 6.0.3.3 [original report - Mon, 24 Aug 2015]
  • Persistent Cross-Site Scripting (XSS) in Easy Coming Soon 1.8.1 [original report - Mon, 24 Aug 2015]
  • Cross-Site Scripting (XSS) in Contact Form Manager 1.4.1 [original report - Mon, 24 Aug 2015] NOT PATCHED
  • Blind SQL injection in WordPress Meta Robots 2.1 [original report - Tue, 25 Aug 2015] NOT PATCHED
  • Cross-Site Scripting (XSS) in Smart Slider 2 2.3.11 [original report - Wed, 26 Aug 2015]
  • Cross-Site Scripting (XSS) in Soundcloud is Gold 2.3.1 [original report - Wed, 26 Aug 2015]
  • Blind SQL injection in Contact Form Maker 1.7.30 [original report - Wed, 8 Jul 2015]
  • Cross-Site Scripting (XSS) in Plugin Central 2.5 [original report - Tue, 25 Aug 2015]
  • Blind SQL injection in yet another stars rating 0.9.0 [original report - Mon, 6 Jul 2015]
  • Blind SQL injection in smart manager for wp e commerce 3.9.6 [original report - Wed, 8 Jul 2015]
  • Blind SQL injection and CSRF for logged administrators in awesome filterable portfolio 1.8.6 [original report - Tue, 7 Jul 2015]
  • Blind SQL injections in WP Shop 3.4.3.15 [original report - Wed, 8 Jul 2015]
  • Cross-Site Scripting (XSS) in Job Manager 0.7.24 [original report - Tue, 25 Aug 2015]
  • Persistent Cross-Site Scripting (XSS) in "Post video players, slideshow albums, photo galleries and music / podcast playlist" 1.136 [original report - Tue, 25 Aug 2015]
  • Arbitrary file modification in Child Theme Creator by Orbisius 1.2.6 [original report - Wed, 8 Jul 2015]
  • SQL injection in WP-Stats-Dashboard 2.9.4 [original report - Tue, 25 Aug 2015] NOT PATCHED
  • Cross-Site Scripting (XSS) in WP Page Widget 2.7 [original report - Tue, 25 Aug 2015]
  • Blind SQL injection in wti like post 1.4.2 [original report - Sun, 5 Jul 2015]
  • SQL injection in Huge IT Google Map 2.2.5 [original report - Wed, 8 Jul 2015] NOT PATCHED
  • Blind SQL injection and Reflected XSS vulnerabilities in broken link manager plugin 0.4.5 [original report - Sat, 4 Jul 2015]
  • Blind SQL injection in Microblog Poster 1.6.0 [original report - Wed, 22 Jul 2015]
  • SQL injection and Cross-Site Scripting (XSS) in GoCodes 1.3.5 [original report - Tue, 25 Aug 2015] NOT PATCHED
  • Blind SQL injections in Auto Affiliate Links 4.9.9.4 [original report - Wed, 15 Jul 2015]
  • Cross-Site Scripting (XSS) in WP Widget Cache 0.26 [original report - Tue, 25 Aug 2015] NOT PATCHED
  • SQL injections and XSS in SendPress Newsletters 1.1.7.21 [original report - Thu, 23 Jul 2015]
  • Cross-Site Scripting (XSS) in Email Users 4.7.5 [original report - Mon, 10 Aug 2015]
  • Persistent Cross-Site Scripting (XSS) in Social Share Button 2.1 [original report - Tue, 25 Aug 2015] NOT PATCHED
  • Cross-Site Scripting (XSS) in Social Locker | BizPanda 4.2.0 [original report - Tue, 25 Aug 2015]
  • Cross Site Scripting (XSS) in Email Encoder Bundle - Protect Email Address 1.4.1 [original report - Mon, 10 Aug 2015]
  • Persistent Cross-Site Scripting (XSS) in Page Restrict 2.2.1 [original report - Tue, 25 Aug 2015]
  • Persistent Cross-Site Scripting (XSS) in Multicons 2.1 [original report - Tue, 25 Aug 2015]
  • Persistent Cross-Site Scripting (XSS) in PlugNedit Adaptive Editor 5.2.0 [original report - Tue, 25 Aug 2015]
  • Blind SQL injection in WooCommerce Abandon Cart Lite Plugin 1.7 [original report - Wed, 15 Jul 2015]
  • Persistent XSS in Broken Link manager Ver 0.5.5 [original report - Thu, 16 Jul 2015]
  • Blind SQL injections in Quiz Master Next 4.4.2 [original report - Thu, 16 Jul 2015]
  • Cross-Site Scripting (XSS) in Role Scoper 1.3.64 [original report - Wed, 26 Aug 2015]
  • Blind SQL injections in NEX-Forms 4.0 [original report - Thu, 16 Jul 2015]
  • Cross-Site Scripting (XSS) in Ad Inserter 1.5.5 [original report - Thu, 13 Aug 2015]
  • Cross-Site Scripting (XSS) in Olevmedia Shortcodes 1.1.8 [original report - Tue, 25 Aug 2015]
  • Blind SQL injection in Booking System (+WooCommerce) 2.0 [original report - Tue, 7 Jul 2015]
  • Blind SQL injections in Plgumatter Optin Feature Box 2.0.13 [original report - Thu, 16 Jul 2015]
  • Blind SQL injection in WR ContactForm 1.1.9 [original report - Thu, 9 Jul 2015]
  • Cross-Site Scripting (XSS) in Simple Fields 1.4.10 [original report - Tue, 25 Aug 2015]
  • Blind SQL injections, XSS and more in wp live chat support 4.3.5 [original report - Mon, 6 Jul 2015]

Some notes:

Without magic_quotes_gpc (wordpress "emulate" it anyway when it's off) it will be some more exploitable SQLi vulnerabilities.

I'm also pretty sure that there are some vulnerabilities in reports that I missed, or just thought that they were unexploitable.

Because of low resources limits (like memory limit ~3GB) many plugins from top 1000 list didn't get 100% code coverage.

░▒▓█ What about CVE identifiers?


I've sent bulk request to cve-assign@mitre.org recently. No response so far.

░▒▓█ Static code analysis tool and some hilights


I used own PhpSourcerer static code analysis tool, which is still in development. You can try PhpSourcerer yourself on php-grinder.com (current limits: 2GB Ram, 15 minutes execution time). It has nice web-ui instead of raw text file reports:





I show you some details about reported vulnerabilities to demonstrate what this tool is capable of.


◼ Reflected Cross-Site Scripting (XSS) in Simple Fields 1.4.10


Description: Authenticated users (like subscribers) can inject html/js code (there is no CSRF protection!)

Some urls: Sent report | Original project sources | PhpSourcerer output

This is a simple example, so I guess it doesn't need explanation:
...

function field_type_post_dialog_load() {

    global $sf;

    $arr_enabled_post_types = isset($_POST["arr_enabled_post_types"]) ? $_POST["arr_enabled_post_types"] : array();
    $str_enabled_post_types = isset($_POST["str_enabled_post_types"]) ? $_POST["str_enabled_post_types"] : "";
    $additional_arguments = isset($_POST["additional_arguments"]) ? $_POST["additional_arguments"] : "";
    $existing_post_types = get_post_types(NULL, "objects");
    $selected_post_type = isset($_POST["selected_post_type"]) ? (string) $_POST["selected_post_type"] : "";

    if (empty($arr_enabled_post_types)) {
      $arr_enabled_post_types = explode(",", $str_enabled_post_types);
    }

    /*echo "<br>selected_post_type: $selected_post_type";
    echo "<br>str_enabled_post_types: $str_enabled_post_types";
    echo "<br>enabled post types:"; print_r($arr_enabled_post_types);*/

    // If no post type is selected then don't show any posts
    if (empty($arr_enabled_post_types)) {
      _e("<p>No post type is selected. Please at at least one post type in Simple Fields.</p>", "simple-fields");
      exit;
    }
    ?>

    <?php if (count($arr_enabled_post_types) > 1) { ?>
      <p>Show posts of type:</p>
      <ul class="simple-fields-meta-box-field-group-field-type-post-dialog-post-types">
        <?php
        $loopnum = 0;
        foreach ($existing_post_types as $key => $val) {
          if (!in_array($key, $arr_enabled_post_types)) {
            continue;
          }
          if (empty($selected_post_type) && $loopnum == 0) {
            $selected_post_type = $key;
          }
          $class = "";
          if ($selected_post_type == $key) {
            $class = "selected";
          }
          printf("\n<li class='%s'><a href='%s'>%s</a></li>", $class, "$key", $val->labels->name);
          $loopnum++;
        }
      ?>
      </ul>
      <?php
    } else {
      $selected_post_type = $arr_enabled_post_types[0];
      ?>
      <p>Showing posts of type: <a href="<?php echo $selected_post_type; ?>"><?php echo $existing_post_types[$selected_post_type]->labels->name; ?></a></p>
      <?php
    } ?>

...



◼ Blind SQL injection in smart manager for wp e commerce 3.9.6


Description: Unauthenticated remote attackers can execute arbitrary SQL commands.

Some urls: Sent report | Original project sources | PhpSourcerer output

/smart-manager-for-wp-e-commerce/sm/woo-json.php:
<?php 

ob_start();

...

// For insert updating product in woo.
if (isset ( $_POST ['cmd'] ) && $_POST ['cmd'] == 'saveData') {

...

  if ( $_POST['active_module'] == "Coupons" ) {

...

  } else {
    $result = woo_insert_update_data ( $_POST );
  }
...
woo_insert_update_data() :
...

function woo_insert_update_data($post) {
  global $wpdb,$woocommerce;
    $_POST = $post;  

      // Fix: PHP 5.4
  $editable_fields = array(
    '_billing_first_name' , '_billing_last_name' , '_billing_email', '_billing_address_1', '_billing_address_2', '_billing_city', '_billing_state',
    '_billing_country','_billing_postcode', '_billing_phone',
    '_shipping_first_name', '_shipping_last_name', '_shipping_address_1', '_shipping_address_2',
    '_shipping_city', '_shipping_state', '_shipping_country','_shipping_postcode', 'order_status'
  );
    $new_product = json_decode($_POST['edited']);

  $edited_prod_ids = array();
  $edited_prod_slug = array();

  if (!empty($new_product)) {
    foreach($new_product as $product) {
      $edited_prod_ids[] = $product->id;
    }
  }

  //Code for getting the product slugs
  if ( !empty($edited_prod_ids) ) {
    $query_prod_slug = "SELECT id, post_name
              FROM {$wpdb->prefix}posts
              WHERE id IN (".implode(",",$edited_prod_ids).")";
    $results_prod_slug = $wpdb->get_results($query_prod_slug, 'ARRAY_A');
    $prod_slug_rows = $wpdb->num_rows;

...
So we can inject our payload in valid json variable, like in verification example in sent report:
curl --request POST  --data "cmd=saveData&edited=[{\"id\":\" 1) union select sleep(10),2; -- -\"}]" http://localhost/wp-content/plugins/smart-manager-for-wp-e-commerce/sm/woo-json.php



◼ Reflected Cross-Site Scripting (XSS) in SEO Redirection 2.8


Description: Authenticated administrators can inject html/js code (there is no CSRF protection).

Some urls: Sent report | Original project sources | PhpSourcerer output

/options/option_page_post_redirection_list.php:
<?php 

global $wpdb,$table_prefix,$util;
$table_name = $table_prefix . 'WP_SEO_Redirection';

  if($util->get('del')!='')
  {
    $delid=intval($util->get('del'));
    $wpdb->query(" delete from $table_name where ID='$delid' ");  
    
    if($util->there_is_cache()!='') 
    $util->info_option_msg("You have a cache plugin installed <b>'" . $util->there_is_cache() . "'</b>, you have to clear cache after any changes to get the changes reflected immediately! ");

    $SR_redirect_cache = new clogica_SR_redirect_cache();
    $SR_redirect_cache->free_cache();
  }
  
  $rlink=$util->get_current_parameters(array('del','search','page_num','add','edit'));
?>
<br/>

<script type="text/javascript">

...

</script>

<div class="link_buttons">
<table border="0" width="100%">
  <tr>
    <td width="110"><a href="<?php echo $rlink?>&add=1"><div class="add_link">Add New</div></a></div></td>
    <td align="right">
    <input onkeyup="if (event.keyCode == 13) go_search();" style="height: 30px;" id="search" type="text" name="search" value="<?php echo $util->get('search')?>" size="40">
    <a onclick="go_search()" href="#"><div class="search_link">Search</div></a> 
$util is object of "clogica_util" class (seo-redirection.php):
<?php
/*
...
*/

require_once ('common/controls.php');
require_once ('custom/controls.php');
require_once ('custom/controls/cf.SR_redirect_cache.class.php');

if(!defined('WP_SEO_REDIRECTION_OPTIONS'))
{
  define( 'WP_SEO_REDIRECTION_OPTIONS', 'wp-seo-redirection-group' );
}

if(!defined('WP_SEO_REDIRECTION_VERSION'))
{
  define( 'WP_SEO_REDIRECTION_VERSION', '2.8');
}

$util= new clogica_util();
$util->set_option_gruop(WP_SEO_REDIRECTION_OPTIONS);
...
Finally lets look how clogica_util::get() looks like:
...

public function get($key,$type='text')
{
  if(array_key_exists($key,$_GET))
  {
      $unsafe_val=$_GET[$key];
        return $this->sanitize_req($unsafe_val,$type);    
  }
  else
  {
      return '';
  }
}

...

public function sanitize_req($unsafe_val,$type='text')
{
   switch ($type) {
     case 'text': return sanitize_text_field($unsafe_val);
     break;
     
     case 'int': return intval($unsafe_val);
     break;
     
     case 'email': return sanitize_email($unsafe_val);
     break;
     
     case 'filename': return sanitize_file_name($unsafe_val);
     break;
     
     case 'title': return sanitize_title($unsafe_val);
     break;
        
     default:
        return sanitize_text_field($unsafe_val);
     
     }
}

...
Yep. Wordpress sanitize_text_field() doesn't prevent from succesful XSS exploitation in html tag attribute.


◼ Reflected Cross-Site Scripting (XSS) in Display Widgets 2.03


Description: Authenticated users (like subscribers) can inject html/js code (there is no CSRF protection!).

Some urls: Sent report | Original project sources | PhpSourcerer output

Lets look at the DWPlugin::show_widget_options() method (invokes by http://localhost/wp-admin/admin-ajax.php?action=dw_show_widget)
...
    function show_widget_options() {
        $instance = htmlspecialchars_decode(nl2br(stripslashes($_POST['opts'])));
        $instance = json_decode($instance, true);
        $this->id_base = $_POST['id_base'];
        $this->number = $_POST['widget_number'];
        
        $new_instance = array();
        $prefix = 'widget-'. $this->id_base .'['. $this->number .'][';
        foreach ( $instance as $k => $v ) {
            $n = str_replace( array( $prefix, ']'), '', $v['name']);
            $new_instance[$n] = $v['value'];
        }

        self::show_hide_widget_options($this, '', $new_instance);
        die();
    }
...
As we can see we have two variables assigned to object properties, and then another method (show_hide_widget_options) is called with $this argument. DWPlugin::show_hide_widget_options():
...
    function show_hide_widget_options($widget, $return, $instance) {
        self::register_globals();
    
        $wp_page_types = self::page_types();
            
        $instance['dw_include'] = isset($instance['dw_include']) ? $instance['dw_include'] : 0;
        $instance['dw_logged'] = self::show_logged($instance);
        $instance['other_ids'] = isset($instance['other_ids']) ? $instance['other_ids'] : '';
?>   
    <p>
        <label for="<?php echo $widget->get_field_id('dw_include'); ?>"><?php _e('Show Widget for:', 'display-widgets') ?></label>
        <select name="<?php echo $widget->get_field_name('dw_logged'); ?>" id="<?php echo $widget->get_field_id('dw_logged'); ?>" class="widefat">
            <option value=""><?php _e('Everyone', 'display-widgets') ?></option>
            <option value="out" <?php echo selected( $instance['dw_logged'], 'out' ) ?>><?php _e('Logged-out users', 'display-widgets') ?></option>
            <option value="in" <?php echo selected( $instance['dw_logged'], 'in' ) ?>><?php _e('Logged-in users', 'display-widgets') ?></option>
        </select>
    </p>

     <p>
     <select name="<?php echo $widget->get_field_name('dw_include'); ?>" id="<?php echo $widget->get_field_id('dw_include'); ?>" class="widefat">
...
And finally get_field_id method and get_field_name:
...
    function get_field_name($field_name) {
  return 'widget-' . $this->id_base . '[' . $this->number . '][' . $field_name . ']';
 }
    
 function get_field_id($field_name) {
  return 'widget-' . $this->id_base . '-' . $this->number . '-' . $field_name;
 }
...
$this->id_base and $this->number were assigned at the beggining in DWPlugin::show_widget_options method.


◼ Persistent Cross-Site Scripting (XSS) in FV Wordpress Flowplayer 6.0.3.3


Description: Authenticated administrators can store html/js code in plugin configuration values (there is no CSRF protection!)

Some urls: Sent report | Original project sources | PhpSourcerer output

First lets look at fv_wp_flowplayer_admin_init() function:
...
function fv_wp_flowplayer_admin_init() {
 if( isset($_GET['type']) ) {
  if( $_GET['type'] == 'fvplayer_video' || $_GET['type'] == 'fvplayer_video_1' || $_GET['type'] == 'fvplayer_video_2' || $_GET['type'] == 'fvplayer_mobile' ) {
   $_GET['post_mime_type'] = 'video';
  }
  else if( $_GET['type'] == 'fvplayer_splash' || $_GET['type'] == 'fvplayer_logo' ) {
   $_GET['post_mime_type'] = 'image';
  }
  }
  
  if( isset($_POST['fv-wp-flowplayer-submit']) ) {
   global $fv_fp;
   if( method_exists($fv_fp,'_set_conf') ) {
   $fv_fp->_set_conf();    
  } else {
   echo 'Error saving FV Flowplayer options.';
  }
 }

  global $fv_fp;
 ...
As we can see we execute _set_conf() method on $fv_wp object when we send fv-wp-flowplayer-submit in POST. $fv_fp is global object of flowplayer_frontend class defined in flowplayer.php main file.
...
$fv_wp_flowplayer_ver = '2.3.17';
$fv_wp_flowplayer_core_ver = '5.5.2';

include( dirname( __FILE__ ) . '/includes/extra-functions.php' );
if( file_exists( dirname( __FILE__ ) . '/includes/module.php' ) ) {
  include( dirname( __FILE__ ) . '/includes/module.php' );
}

include( dirname( __FILE__ ) . '/models/checker.php' );
$FV_Player_Checker = new FV_Player_Checker();

include_once(dirname( __FILE__ ) . '/models/flowplayer.php');
include_once(dirname( __FILE__ ) . '/models/flowplayer-frontend.php');
$fv_fp = new flowplayer_frontend();

if( is_admin() ) {
 include( dirname( __FILE__ ) . '/controller/backend.php' );
  
  register_deactivation_hook( __FILE__, 'flowplayer_deactivate' );

} 
 
include( dirname( __FILE__ ) . '/controller/frontend.php' );
require_once( dirname( __FILE__ ) . '/controller/shortcodes.php');
$fv_fp->_set_conf() method is not really belongs to "flowplayer_frontend" class but to "flowplayer" that "flowplayer_frontend" is extending:
...
class flowplayer_frontend extends flowplayer {
...
flowplayer::_set_conf():
...
 public function _set_conf() {
    $aNewOptions = $_POST;
    $sKey = $aNewOptions['key'];

    foreach( $aNewOptions AS $key => $value ) {
      if( is_array($value) ) {
        $aNewOptions[$key] = $value;
      } else if( !in_array( $key, array('amazon_region', 'amazon_bucket', 'amazon_key', 'amazon_secret', 'font-face', 'ad', 'ad_css') ) ) {
        $aNewOptions[$key] = trim( preg_replace('/[^A-Za-z0-9.:\-_\/]/', '', $value) );
      } else {
        $aNewOptions[$key] = stripslashes($value);
      }
      if( (strpos( $key, 'Color' ) !== FALSE )||(strpos( $key, 'canvas' ) !== FALSE)) {
        $aNewOptions[$key] = '#'.strtolower($aNewOptions[$key]);
      }
    }
    $aNewOptions['key'] = trim($sKey);
    $aOldOptions = is_array(get_option('fvwpflowplayer')) ? get_option('fvwpflowplayer') : array();
    
    if( isset($aNewOptions['db_duration']) && $aNewOptions['db_duration'] == "true" && ( !isset($aOldOptions['db_duration']) || $aOldOptions['db_duration'] == "false" ) ) {
      global $FV_Player_Checker;
      $FV_Player_Checker->queue_add_all();
    }
    
    if( !isset($aNewOptions['pro']) || !is_array($aNewOptions['pro']) ) {
      $aNewOptions['pro'] = array();
    }
    
    if( !isset($aOldOptions['pro']) || !is_array($aOldOptions['pro']) ) {
      $aOldOptions['pro'] = array();
    }    
 
    
    $aNewOptions['pro'] = array_merge($aOldOptions['pro'],$aNewOptions['pro']);
    $aNewOptions = array_merge($aOldOptions,$aNewOptions);
    $aNewOptions = apply_filters( 'fv_flowplayer_settings_save', $aNewOptions, $aOldOptions );
    update_option( 'fvwpflowplayer', $aNewOptions );
    $this->conf = $aNewOptions;    
    
    $this->css_writeout();
           
    return true;  
  }
...
As we can see we override plugin options, that is why we can inject XSS payload in fv_flowplayer_admin_default_options():
...
function fv_flowplayer_admin_default_options() {
 global $fv_fp;
?>
 <table class="form-table2">

...      

  <tr>
   <td><label for="width">Default video size [px]:</label></td>
   <td colspan="2">      
    <label for="width">W:</label> <input type="text" class="small" name="width" id="width" value="<?php echo trim($fv_fp->conf['width']); ?>" />  
    <label for="height">H:</label> <input type="text" class="small" name="height" id="height" value="<?php echo trim($fv_fp->conf['height']); ?>" />       
   </td>
  </tr>      
  <tr>
   <td><label for="googleanalytics">Google Analytics ID:</label></td>
   <td colspan="3"><input type="text" name="googleanalytics" id="googleanalytics" value="<?php echo trim($fv_fp->conf['googleanalytics']); ?>" /></td>
  </tr>
  <tr>
   <td><label for="key">Commercial License Key:</label></td>
   <td colspan="3"><input type="text" name="key" id="key" value="<?php echo trim($fv_fp->conf['key']); ?>" /></td>
  </tr>
...



◼ Blind SQL injection in Master Slider 2.5.1


Description: Authenticated users (like editors) can execute arbitrary sql commands (there is no CSRF protection)

Some urls: Sent report | Original project sources | PhpSourcerer output

I'll just show you just the flow without commenting (pay attention to orderby parameter/variables). We start by looking at /master-slider/admin/views/slider-dashboard/list-sliders.php file:
<?php
    msp_get_panel_header();

    // Display sliders list
 $slider_table_list = new MSP_List_Table();
 $slider_table_list->prepare_items(); 
 $slider_table_list->display(); 
MSP_List_Table::prepare_items() :
...
 function prepare_items() {

  $columns  = $this->get_columns();
  $hidden  = array();
  $sortable  = $this->get_sortable_columns();
   
  $this->_column_headers = array( $columns, $hidden, $sortable );
  
  $this->process_bulk_action();

  $perpage   = (int) apply_filters( 'masterslider_admin_sliders_per_page', 10 );
  $current_page  = $this->get_pagenum();
  $orderby   = 'ID';
  $order    = 'DESC';
  $total_items  =  $this->get_total_count();


  $this->items  = $this->get_records( $perpage, $current_page, $orderby, $order );
  // echo '
'; print_r( $this->items ); echo '
'; // tell the class the total number of items and how many items to show on a page $this->set_pagination_args( array( 'total_items' => $total_items, 'per_page' => $perpage )); } ...
MSP_List_Table::get_total_count() :
...
 function get_total_count(){
  global $mspdb;
  
  $all_items = $this->get_records( 0 );
  return count( $all_items );
 }
...
MSP_List_Table::get_records() :
...
 function get_records( $perpage = 20, $paged  = 1, $orderby = 'ID', $order = 'DESC', $where = "status='published'" ){
  global $mspdb;
  
  $offset  = ( (int)$paged - 1 ) * $perpage;
  $orderby = isset( $_REQUEST['orderby'] ) ? $_REQUEST['orderby'] : 'ID';
  $order   = isset( $_REQUEST['order'] ) ? $_REQUEST['order'] : 'ASC';
  
  $search  = isset( $_REQUEST['s'] ) ? " AND title LIKE '%%" . $_REQUEST['s'] . "%%'" : '';

  return $mspdb->get_sliders( $perpage, $offset, $orderby, $order, $where.$search );
 }
...
MSP_DB::get_sliders() :
...
 public function get_sliders( $perpage = 0, $offset  = 0, $orderby = 'ID', $sort = 'DESC', $where = "status='published'" ) {
  
  // pull mulitple row results from sliders table
  if( ! $results = $this->get_sliders_list( $perpage, $offset, $orderby, $sort, $where ) ){
   return;
  }

  // map through some fields and unserialize values if some data fields are serialized
  foreach ($results as $row_index => $row) {
   $results[$row_index] = $this->maybe_unserialize_fields($row);
  }

  return $results;
 }
...
MSP_DB::get_sliders_list() :
...
 public function get_sliders_list( $perpage = 0, $offset  = 0, $orderby = 'ID', $order = 'DESC', $where = "status='published'" ) {
  global $wpdb;

  $args = array(
   'perpage' => $perpage,
   'offset'  => $offset,
   'orderby' => $orderby,
   'order'   => $order,
   'where'   => $where
  );
  
  return $this->ms_query( $args );
 }
...
And finally MSP_DB::ms_query() where actual SQL injection takes place:
...
 public function ms_query( $args = array() ) {
  global $wpdb;

  $default_args = array(
   'perpage' => 0,
   'offset'  => 0,
   'orderby' => 'ID',
   'order'   => 'DESC',
   'where'   => "status='published'",
   'like'    => ''
  );

  $args = wp_parse_args( $args, $default_args );


  // convert perpage type to number
  $limit_num = (int) $args['perpage'];

  // convert offset type to number
  $offset_num = (int) $args['offset'];

  // remove limit if limit number is set to 0
  $limit  = ( 1 > $limit_num ) ? '' : 'LIMIT '. $limit_num; 

  // remove offect if offset number is set to 0
  $offset = ( 0 == $offset_num )? '' : 'OFFSET '. $offset_num; 

  // add LIKE if defined
  $like  = empty( $args['like'] ) ? '' : 'LIKE '. $args['like']; 

  $where = empty( $args['where'] ) ? '' : 'WHERE '. $args['where']; 

  // sanitize sort type
  $order   = strtolower( $args['order'] ) === 'desc' ? 'DESC' : 'ASC';
  $orderby = $args['orderby'];

  $sql = "
   SELECT *
   FROM {$this->sliders}
   $where
   ORDER BY $orderby $order
   $limit 
   $offset
   ";
  
  return $wpdb->get_results( $sql, ARRAY_A );
 }
...


░▒▓█ Bonus: funny mistake in validation


Plugin: Visitor Maps and Who's Online 1.5.8.6

Idea was good, but something went wrong :-)
function view_whos_been_online() {

...

  $show = (isset($wo_prefs_arr['show'])) ? $wo_prefs_arr['show'] : 'none';
  if ( isset($_GET['show']) && in_array($_GET['show'], array('none','all','bots','guests')) ) {
    $wo_prefs_arr['show'] = $_GET['show'];
    $show = $_GET['show'];
  }

...

  $sort_by = (isset($wo_prefs_arr['sort_by'])) ? $wo_prefs_arr['sort_by'] : 'time';
  if ( isset($_GET['sort_by']) && array('who','visits','time','ip','location','url') ) {
    $wo_prefs_arr['sort_by'] = $_GET['sort_by'];
    $sort_by = $_GET['sort_by'];
  }

...

  $order = (isset($wo_prefs_arr['order'])) ? $wo_prefs_arr['order'] : 'desc';
  if ( isset($_GET['order']) && array('desc','asc') ) {
   // bots
    $wo_prefs_arr['order'] = $_GET['order'];
    $order = $_GET['order'];
  }
...

19 comments:

  1. Just a suggestion, but this list would be much more useful to most people if you listed the plugin name first and then the type of vulnerability, and then alphabetized the list.

    ReplyDelete
  2. Very nice. The static code analysis tools for PHP are pretty old and useless at this point, and it nice to see someone working to revitalize the area. Have you considered open sourcing your tool? (Possibly through OWASP?)

    ReplyDelete
    Replies
    1. Source code should be published soon or later, because the main purpose of this tool was to inject it in "Continuous Integration" chain. However, codebase is still messy with many experimental ideas and I just don't want to publish it now as it is.

      Delete
    2. On the other hand, publishing it even before your code has "production-grade" quality would still be benefical to a lot ot people, and it would also be the opportunity to have the community help you tidy it up. "Publish early, release often" ;)

      Delete
  3. Looks like Plugin Central has been patched

    ReplyDelete
    Replies
    1. P.S. We've added *all* of these to https://wpvulndb.com with links back to this post :)

      (patched ones won't show on front page but are in the DB if you look at individual plugin pages)

      Delete
    2. Great! But there are some wrong dates for "Publicly Published" fields (like in https://wpvulndb.com/vulnerabilities/8325). Public disclosure was 21.11.2015 for all plugins affected.

      Delete
  4. This comment has been removed by a blog administrator.

    ReplyDelete
  5. This comment has been removed by a blog administrator.

    ReplyDelete

  6. Thank you so much... your blog is giving very useful knowledge for all.i didn’t have the knowledge in this now i get an idea about this..
    thks a lot:-)To know more seo training in chennai

    ReplyDelete
  7. This is a topic that’s near to my heart… Many thanks!
    Exactly where are your contact details though?



    Here My web
    - Grosir Jaket Parka
    - Grosir Jaket Parka
    - Grosir Jaket Parka
    - Grosir Jaket Parka
    - Grosir Jaket Parka
    - Grosir Jaket Parka

    ReplyDelete
  8. below youll discover the link to some websites that we believe you ought to visit
    neat huge : Souvenir Pernikahan Unik - Souvenir Pernikahan Unik

    ReplyDelete
  9. we like to honor lots of other internet web sites around the internet, even when they arent linked to us, by linking to them. Under are some webpages worth checking out

    Here My Website :

    - Grosir Jaket Parka
    - Harga Grosir Jaket Parka
    - Grosir Jaket Parka Wanita
    - Grosir Jaket Parka Murah
    - Grosir Jaket Parka Wanita

    ReplyDelete
  10. we like to honor lots of other internet web sites around the internet, even when they arent linked to us, by linking to them. Under are some webpages worth checking out


    below youll discover the link to some websites that we believe you ought to visit

    ReplyDelete
  11. This blog is so nice to me. I will continue to come here again and again. Visit my link as well. Good luck
    obat aborsi

    ReplyDelete