AppSec Blog

Spot the Vuln - Charming - SQL Injection


Affected Software: StatPressCN

Fixed in Version: 1.9.1

Issue Type: SQL Injection

Original Code: Found Here


This patch was full of interesting tidbits. First, the change log for this patch is as follows:

+ fix a flaw allowing a remote cross-site scripting attack

Keep the change list description in mind as we go over the patch submitted by the developers. The submitted patch is pretty simple. There is an additional qualifier set for an if statement that checks to see if $_GET["where$i"] is contained within array $f. It's difficult to determine whether this is true? but it doesn't really matter. The second change is an addslashes to $_GET["what$i"] before using the tainted query string parameter to build a dynamic SQL statement. This is to prevent an obvious SQL injection bug in the LIKE operator of the SQL statement.

What's surprising is the developer missed the $_GET["where$i"] query string parameter used to build the SQL statement on the same line. This bug is equally devastating and results in SQL injection against the application. So despite the change log description, this patch is to address a SQL injection bug, NOT an XSS.

Looking through the rest of the code, we see XSS (lines 7-9 and 17) and SQL injection bugs (lines 57,65, 77) littered throughout the code base. These bugs still exist in the latest version, are not patched, and put users at risk. If you have this plug-in installed, your server and users are at significant risk!

Developers Solution

</table> <br> <table> <tr> <td> <table> <tr><td><input type=checkbox name=oderbycount value=checked <?php print $_GET['oderbycount'] ?>> <?php _e('sort by count if grouped','statpresscn'); ?></td></tr> <tr><td><input type=checkbox name=spider value=checked <?php print $_GET['spider'] ?>> <?php _e('include spiders/crawlers/bot','statpresscn'); ?></td></tr> <tr><td><input type=checkbox name=feed value=checked <?php print $_GET['feed'] ?>> <?php _e('include feed','statpresscn'); ?></td></tr> </table> </td> <td width=15> </td> <td> <table> <tr> <td><?php _e('Limit results to','statpresscn'); ?> <select name=limitquery><?php if($_GET['limitquery'] >0) { print "<option>".$_GET['limitquery']."</option>";} ?><option>200</option><option>150</option><option>50</option></select> </td> </tr> <tr><td>&nbsp;</td></tr> <tr> <td align=right><input type=submit value=<?php _e('Search','statpresscn'); ?> name=searchsubmit></td> </tr> </table> </td> </tr> </table><!- It's strange that the page value should be spc-search, and not others. -> <input type=hidden name=page value='spc-search'><input type=hidden name=statpress_action value=search> </form><br> <?php if(isset($_GET['searchsubmit'])) { # query builder $qry=""; # FIELDS $fields=""; for($i=1;$i<=5;$i++) { - if($_GET["where$i"] != ") { +if($_GET["where$i"] != " && array_key_exists($_GET["where$i"], $f)) {//??where?????? $fields.=$_GET["where$i"].","; } } $fields=rtrim($fields,","); # WHERE $where="WHERE 1=1"; if($_GET['spider'] != 'checked') { $where.=" AND spider=""; } if($_GET['feed'] != 'checked') { $where.=" AND feed=""; } for($i=1;$i<=5;$i++) { if(($_GET["what$i"] != ") && ($_GET["where$i"] != ")) { - $where.=" AND ".$_GET["where$i"]." LIKE '%".$_GET["what$i"]."%'"; +$where.=" AND ".$_GET["where$i"]." LIKE '%".addslashes($_GET["what$i"])."%'";//addslashes?????? } } # ORDER BY $orderby=""; for($i=1;$i<=5;$i++) { if(($_GET["sortby$i"] == 'checked') && ($_GET["where$i"] != ")) { $orderby.=$_GET["where$i"].','; } } # GROUP BY $groupby=""; for($i=1;$i<=5;$i++) { if(($_GET["groupby$i"] == 'checked') && ($_GET["where$i"] != ")) { $groupby.=$_GET["where$i"].','; } } if($groupby != ") { $grouparray = explode(",",rtrim($groupby,',')); $groupby="GROUP BY ".rtrim($groupby,','); $fields.=",count(*) as totale"; if($_GET['oderbycount'] == 'checked') { $orderby="totale DESC,".$orderby; } } if($orderby != ") { $orderby="ORDER BY ".rtrim($orderby,','); } $limit="LIMIT ".$_GET['limitquery']; # Results print "<h2>".__('Results','statpresscn')."</h2>"; $sql="SELECT $fields FROM $table_name $where $groupby $orderby $limit;"; //print "$sql<br>"; print "<table class='widefat'><thead><tr>"; for($i=1;$i<=5;$i++) { if($_GET["where$i"] != ") { print "<th scope='col'>"; if((count($grouparray)>0)&&in_array($_GET["where$i"],$grouparray)){ print "<font color=red>"; } print ucfirst($f[$_GET["where$i"]]); if((count($grouparray)>0)&&in_array($_GET["where$i"],$grouparray)){ print "</font>"; } print "</th>"; } } if($groupby != ") { print "<th scope='col'><font color=red>".__('Count','statpresscn')."</font></th>"; } print "</tr></thead><tbody id='the-list'>"; $qry=$wpdb->get_results($sql,ARRAY_N); $cloumnscount = count($wpdb->get_col_info("name")); foreach ($qry as $rk) { print "<tr>"; for($i=1;$i<=$cloumnscount;$i++) { print "<td>"; if($_GET["where$i"] == 'urlrequested') { print "<a href=".heart5_config_url($rk[$i-1])." target=_heart5>"; print iri_StatPress_Decode($rk[$i-1]); print "</a>"; } else { print $rk[$i-1]; } // print $rk[$i-1]; print "</td>"; } print "</tr>"; } print "</table>"; print "<br /><br /><font size=1 color=gray>sql: $sql</font>"; }?> </div>

Post a Comment


* Indicates a required field.