| DetailsAffected Software:Sermon Browser WordPress Plugin Fixed in Version:.44 Issue Type:Cross Site Scripting Original Code:Found Here DetailsThere are a couple of different issues here,but let’s focus on what the developers patched. On line 27,the developer uses the $_GET[‘getid3’] value to build a dynamic SQL statement. This is classic SQL injection. The patch seems straight forward,escape the $_GET[‘getid3’] value before using it in the SQL statement. Normally,SQL injection involves breaking out of a predefined SQL statement by closing off a quoted string and injecting your own SQL statement. Most escaping functions escape quotes and other special characters so that an attacker cannot escape out of a quoted string. There is a problem in this patch though. The tainted value is NOT enclosed within quotes,so the attacker does not need to escape out of a quoted string. The attacker is free to build a SQL injection payload as long as the payload doesn’t contain any special characters. So,despite escaping the $_GET[‘getid3’] value,SQL injection is still possible. Anyone spot the unpatched XSS? Developers Solution<?php...snip...// tags$tags = explode(',',$_POST['tags']);$wpdb->query("DELETE FROM{$wpdb->prefix}sb_sermons_tags WHERE sermon_id = $id;");foreach ($tags as $tag){$clean_tag = trim(mysql_real_escape_string($tag));$existing_id = $wpdb->get_var("SELECT id FROM{$wpdb->prefix}sb_tags WHERE name='$clean_tag'");if (is_null($existing_id)){$wpdb->query("INSERT INTO{$wpdb->prefix}sb_tags VALUES (null,'$clean_tag')");$existing_id = $wpdb->insert_id}$wpdb->query("INSERT INTO{$wpdb->prefix}sb_sermons_tags VALUES (null,$id,$existing_id)")}sb_delete_unused_tags();// everything is fine,get out of here!if(!isset($error)){sb_ping_gallery();echo "<script>document.location = '".$_SERVER['PHP_SELF']."?page=sermon-browser/sermon.php&saved=true';</script>";die()}}$id3_tags = array();if (isset($_GET['getid3'])){require_once('getid3/getid3.php');-$file_data = $wpdb->get_row("SELECT name,type FROM{$wpdb->prefix}sb_stuff WHERE id = ".$_GET['getid3']);+$file_data = $wpdb->get_row("SELECT name,type FROM{$wpdb->prefix}sb_stuff WHERE id = ".$wpdb->escape($_GET['getid3']));if ($file_data !== NULL){$getID3 = new getID3;if ($file_data->type == 'url'){$filename = substr($file_data->name,strrpos ($file_data->name,'/')+1);$sermonUploadDir = SB_ABSPATH.sb_get_option('upload_dir');$tempfilename = $sermonUploadDir.preg_replace('/([ ])/e','chr(rand(97,122))','').'.mp3';if ($tempfile = @fopen($tempfilename,'wb'))if ($remote_file = @fopen($file_data->name,'r')){$remote_contents = '';while (!feof($remote_file)){$remote_contents .= fread($remote_file,8192);if (strlen($remote_contents) >65536) break}fwrite($tempfile,$remote_contents);fclose($remote_file);fclose($tempfile);$id3_raw_tags = $getID3->analyze(realpath($tempfilename));unlink ($tempfilename)}} else{$filename = $file_data->name;$id3_raw_tags = $getID3->analyze(realpath(SB_ABSPATH.sb_get_option('upload_dir').$filename))}if (!isset($id3_raw_tags['tags'])){echo '<div id="message"class="updated fade"><p><b>'.__('No ID3 tags found.',$sermon_domain);if ($file_data->type == 'url') echo ' Remote files must have id3v2 tags.';echo '</b></div>'}...snip...?>DetailsAffected Software:StatPressCN Fixed in Version:1.9.1 Issue Type:SQL Injection Original Code:Found Here DetailsThis patch was full of interesting tidbits. First,the change log for this patch is as follows: **1.9.1** + 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> </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>DetailsAffected Software:Comment-Rating Plugin Fixed in Version:2.9.24 Issue Type:SQL Injection (SQLi) Original Code:Found Here DetailsThis week’s vulnerability was a tricky one. The bug patched in this change list affected the Comment-Rating plugin for WordPress (fixed in 2.9.24). Let’s take the bug step by step. First,the application takes a user/attacker supplied value and runs it through an escaping function here (line 9): $k_id = strip_tags($wpdb->escape($_GET['id'])); So,$k_id is now tainted and contains an escaped value provided by the attacker. A few lines later,we see the following code: if($k_id &&$k_action &&$k_path){ //Check to see if the comment id exists and grab the rating $query = “SELECT * FROM `$table_name` WHERE ck_comment_id = $k_id”; $result = mysql_query($query); The code above checks for a specific condition (which is a condition controllable by the attacker) then proceeds to build and execute a SQL query. On line 22 we see $k_id is used to build a dynamic SQL statement. Variables usage within stings are valid in PHP (http://php.net/manual/en/language.types.string.php –see Variable parsing). $k_id is escaped so we should be ok here…right? Actually,in this case escaping isn’t sufficient to prevent SQL injection. Escaping functions typically work by preventing a variable value from breaking out of quotes,unfortunately in this case there are no quotes to break out of. $k_id is designed to be a numeric value not a string,so there is no need to encapsulate the $k_id value in quotes. Although $k_id is designed to be numeric,there was nothing that would prevent an attacker from providing an arbitrary value for $k_id. For example,an attacker could provide a value like this for $k_id: 99999 union select uname,passwd from users As you can see,there are no special characters (double quotes,single quotes,or database escape characters) in the string above that would have been escaped by a database escaping function. When used to build the $query variable,we end up with: $query = “SELECT * FROM `$table_name` WHERE ck_comment_id = 99999 union select uname,passwd from users“; The developers addressed this vulnerability by validating that $k_id is indeed numeric before using the value to build a dynamic SQL statement. Developers Solution<?phprequire_once('../../../wp-config.php');require_once('../../../wp-includes/functions.php');// CSRF attack protection. Check the Referal field to be the same// domain of the script$k_id = strip_tags($wpdb->escape($_GET['id']));$k_action = strip_tags($wpdb->escape($_GET['action']));$k_path = strip_tags($wpdb->escape($_GET['path']));$k_imgIndex = strip_tags($wpdb->escape($_GET['imgIndex']));+// prevent SQL injection+if (!is_numeric($k_id)) die('error|Query error');$table_name = $wpdb->prefix . 'comment_rating';$comment_table_name = $wpdb->prefix . 'comments';if($k_id &&$k_action &&$k_path){ //Check to see if the comment id exists and grab the rating $query = "SELECT * FROM `$table_name` WHERE ck_comment_id = $k_id"; $result = mysql_query($query);if(!$result){die('error|mysql:'.mysql_error());} if(mysql_num_rows($result)){$duplicated = 0;// used as a counter to off set duplicated votes if($row = @mysql_fetch_assoc($result)){if(strstr($row['ck_ips'],getenv("REMOTE_ADDR"))){ // die('error|You have already voted on this item!'); // Just don't count duplicated votes $duplicated = 1; $ck_ips = $row['ck_ips']; } else{ $ck_ips = $row['ck_ips'] . ',' . getenv("REMOTE_ADDR");// IPs are separated by ',' } } $total = $row['ck_rating_up'] - $row['ck_rating_down'];if($k_action == 'add'){ $rating = $row['ck_rating_up'] + 1 - $duplicated; $direction = 'up'; $total = $total + 1 - $duplicated;} elseif($k_action == 'subtract'){ $rating = $row['ck_rating_down'] + 1 - $duplicated; $direction = 'down'; $total = $total - 1 + $duplicated;} else{ die('error|Try again later');//No action given. } if (!$duplicated){ $query = "UPDATE `$table_name` SET ck_rating_$direction = '$rating',ck_ips = '". $ck_ips . "' WHERE ck_comment_id = $k_id"; $result = mysql_query($query); if(!$result) { // die('error|query '.$query); die('error|Query error'); } // Now duplicated votes will not if(!mysql_affected_rows()) { die('error|affected '. $rating); } $karma_modified = 0; if (get_option('ckrating_karma_type') == 'likes' &&$k_action == 'add'){ $karma_modified = 1;$karma = $rating; } if (get_option('ckrating_karma_type') == 'dislikes' &&$k_action == 'subtract'){ $karma_modified = 1;$karma = $rating; } if (get_option('ckrating_karma_type') == 'both'){ $karma_modified = 1;$karma = $total; } if ($karma_modified){ $query = "UPDATE `$comment_table_name` SET comment_karma = '$karma' WHERE comment_ID = $k_id"; $result = mysql_query($query); if(!$result) die('error|Comment Query error'); } } } else{ die('error|Comment doesnt exist');//Comment id not found in db,something wrong ? }} else{ die('error|Fatal:html format error')}// Add the + sign,if ($total >0){$total = "+$total";}//This sends the data back to the js to process and show on the page// The dummy field will separate out any potential garbage that// WP-superCache may attached to the end of the return.echo("done|$k_id|$rating|$k_path|$direction|$total|$k_imgIndex|dummy");?>DetailsAffected Software:Surfnet IDS Fixed in Version:1.03.07 Issue Type:SQL Injection Original Code:Found Here DescriptionThere were a couple of SQL injection bugs here. Beginning at line 35,we see that the Surfnet IDS developers have accepted three POST parameters and have assigned tainted values to three different variables:$keyname,$vlanid,$action. $keyname is eventually passed to three different dynamic SQL queries,all of which result in SQL injection. Those queries can be seen on lines 52,59,and 69. $vlanid is passed to a dynamic SQL query,resulting in SQL injection. This dynamic query can be seen on line 59. Finally,$action is passed to a dynamic SQL statement,resulting in yet another SQL injection bug. This dynamic query can be found on line 69. All of the bugs were straightforward SQL injection bugs and should have been caught early in the dev cycle. The developers addressed the issue by and/or validating all of the POST parameters before using those values in SQL statements. Developers Solution<?php...snip...include '../include/config.inc.php';include '../include/connect.inc.php';include '../include/functions.inc.php';session_start();header("Cache-control:private");if (!isset($_SESSION['s_admin'])){pg_close($pgconn);$address = getaddress($web_port);header("location:${address}login.php");exit}$s_org = intval($_SESSION['s_org']);$s_admin = intval($_SESSION['s_admin']);$s_access = $_SESSION['s_access'];$s_access_sensor = intval($s_access{0});if ($s_access_sensor == 0){$m = 90;pg_close($pgconn);header("location:sensorstatus.php?selview=$selview&m=$m");exit}if (isset($_GET['selview'])){$selview = intval($_GET['selview'])}$error = 0;-$keyname = $_POST['keyname'];-$vlanid = $_POST['vlanid'];-$action = $_POST['action'];-if (isset($_POST[tapip])){+$keyname = pg_escape_string($_POST['keyname']);+$vlanid = intval($_POST['vlanid']);+$action = pg_escape_string($_POST['action']);+$action_pattern = '/^(NONE|REBOOT|SSHOFF|SSHON|CLIENT|RESTART|BLOCK)$/';+if (preg_match($action_pattern,$action) != 1){+ $m = 44;+ $error = 1;+}++if (isset($_POST[tapip]) &&$error != 1){$tapip = pg_escape_string(stripinput($_POST[tapip]));if (preg_match($ipregexp,$tapip)){ $sql_checkip = "SELECT tapip FROM sensors WHERE tapip = '$tapip' AND NOT keyname = '$keyname'"; $result_checkip = pg_query($pgconn,$sql_checkip); $checkip = pg_num_rows($result_checkip); if ($checkip >0){$m = 101;$error = 1; } else{$sql_updatestatus = "UPDATE sensors SET tapip = '$tapip' WHERE keyname = '$keyname' AND vlanid ='$vlanid'";$result_updatestatus = pg_query($pgconn,$sql_updatestatus);$m = 7; } } else{ $m = 102; $error = 1;}}if ($error == 0){$sql_updatestatus = "UPDATE sensors SET action = '".$action. "' WHERE keyname = '$keyname'";$result_updatestatus = pg_query($pgconn,$sql_updatestatus);$m = 7}pg_close($pgconn);if ($m != 1){header("location:sensorstatus.php?selview=$selview&m=$m&key=$keyname")} else{header("location:sensorstatus.php?selview=$selview&m=$m")}?>DetailsAffected Software:WordPress Core Fixed in Version:2.2 Issue Type:SQL Injection Original Code:Found Here DescriptionThis is a fairly straight forward SQL Injection bug here. First,although we can’t see exactly where $args[] is set,we have some strong clues that it contains user/attacker controlled data. For example,the first function on the code snippet wp_newCategory() takes an $args parameter and the first thing it does is escape the values within the array. The names of variables holding various values in the array also provide clues that $args cannot be trusted. On line 66 we see that $max_results is assigned the value from $args[4]. $max_results is then used to build a portion of a SQL string which is assigned to the $limit variable. $limit is then passed to the end of a SQL statement on line 84,resulting in SQL injection. Some readers may point out that $args is escaped in on line 60,before it is used to build any SQL statement. Unfortunately,escaping values in this case doesn’t prevent SQL injection. The attacker controlled value is eventually used to build a LIMIT clause. The LIMIT clause doesn’t enclose the attacker supplied values within quotes,so there are no quotes to break out of. The developers addressed this issue by casting args[4] to int during assignment to $max_results. If args[4] contains any characters that do not qualify as an integer,the value will not be passed to the LIMIT statement. Developers Solution<?php...snip...function wp_newCategory($args){$this->escape($args);$blog_id= (int) $args[0];$username= $args[1];$password= $args[2];$category= $args[3];if(!$this->login_pass_ok($username,$password)){return($this->error)}// Set the user context and make sure they are// allowed to add a category.set_current_user(0,$username);if(!current_user_can("manage_categories",$page_id)){return(new IXR_Error(401,__("Sorry,you do not have the right to add a category.")))}// We need this to make use of the wp_insert_category()// funciton.require_once(ABSPATH . "wp-admin/admin-db.php");// If no slug was provided make it empty so that// WordPress will generate one.if(empty($category["slug"])){$category["slug"] = ""}// If no parent_id was provided make it empty// so that it will be a top level page (no parent).if ( !isset($category["parent_id"]) )$category["parent_id"] = "";// If no description was provided make it empty.if(empty($category["description"])){$category["description"] = ""}$new_category = array("cat_name"=>$category["name"],"category_nicename"=>$category["slug"],"category_parent"=>$category["parent_id"],"category_description"=>$category["description"]);$cat_id = wp_insert_category($new_category);if(!$cat_id){return(new IXR_Error(500,__("Sorry,the new category failed.")))}return($cat_id)}function wp_suggestCategories($args){global $wpdb;$this->escape($args);$blog_id= (int) $args[0];$username= $args[1];$password= $args[2];$category= $args[3];-$max_results= $args[4];+$max_results = (int) $args[4];if(!$this->login_pass_ok($username,$password)){return($this->error)}// Only set a limit if one was provided.$limit = "";if(!empty($max_results)){$limit = "LIMIT{$max_results}"}$category_suggestions = $wpdb->get_results("SELECT cat_ID category_id,cat_name category_nameFROM{$wpdb->categories}WHERE cat_name LIKE '{$category}%'{$limit}");return($category_suggestions)}function blogger_getUsersBlogs($args){$this->escape($args);$user_login = $args[1];$user_pass = $args[2];if (!$this->login_pass_ok($user_login,$user_pass)){return $this->error}set_current_user(0,$user_login);$is_admin = current_user_can('level_8');$struct = array('isAdmin' =>$is_admin,'url' =>get_option('home') . '/','blogid' =>'1','blogName' =>get_option('blogname'));return array($struct)}...snip...?>DetailsAffected Software:Short URL Plugin Fixed in Version:Changeset 55280 Issue Type:SQL Injection Original Code:Found Here DescriptionThis weeks’ vulnerabilities were a couple of SQL injection bugs in the Short URL Plugin for WordPress. The symptoms for the issues indicate classic SQL injection,let’s have a quick look at the code. First,looking over the code sample,we see a couple of dynamically built SQL statements. It would probably make sense to spend a bit of time and convert these dynamic SQL statements into prepared statements,that way you won’t have to worry about a code change inadvertently re-introducing a SQL injection flaw or an escaping filter bypass. With dynamically built SQL statements we’ll also have to trace each variable until we can determine whether the value can be controlled by an attacker. Lucky for us,the variable assignments are very close to the SQL statements. In the vulnerable sample,we see that the author is taking values directly from a POST request and using those tainted values to build SQL statements. Looking at the check-in,we see that the developer chose to use WordPress’ built-in escaping function for escaping user/attacker controlled data before passing it to a SQL statement. Although the checked-in fixes were straightforward,I was surprised to see that the developers missed an obvious SQL injection on line 56. Same classic SQL injection symptoms,the only difference is the dynamic SQL being built is a DELETE SQL statement as opposed to an INSERT or UPDATE. For those that are wondering… YES,this SQL injection is still present in the latest version of the plug-in! If you happen to be using this plug-in on your website,I would recommend you escape $delete_id before passing it to a SQL statement! I notified the plug-in author,hopefully they’ll be a patch soon. Is this the first Spot-The-Vuln.com 0day? Developers Solution<?php...snip...function kd_admin_options_su(){ global $table_prefix,$wpdb,$user_ID; $table_name = $table_prefix . "short_url"; if($wpdb->get_var("show tables like '$table_name'") != $table_name){ $sql = "CREATE TABLE ".$table_name."( link_id int(11) NOT NULL auto_increment, link_url text NOT NULL, link_desc text NOT NULL, link_count int(11) NOT NULL default '0', PRIMARY KEY (`link_id`) );"; require_once(ABSPATH . 'wp-admin/upgrade-functions.php'); dbDelta($sql); } if(isset($_POST['action'])){$action = $_POST['action'];if($action == "create"){- $add_url = $_POST['form_url'];- $add_desc = $_POST['form_desc'];+ $add_url = $wpdb->escape($_POST['form_url']);+ $add_desc = $wpdb->escape($_POST['form_desc']);if($add_url == "http://"|| (!$add_url)){$ERR = $ERR . "<br>You must enter a URL to redirect to!";} if(!$ERR){$wpdb->query("INSERT INTO $table_name (link_url,link_desc) VALUES ('$add_url','$add_desc')"); $new_url = get_option("siteurl") . "/u/". mysql_insert_id(); $MES = $MES . "<br>The redirect URL has been added. Your new Short URL is:". $new_url; } }if($action == "edit"){- $edit_id = $_POST['id'];- $edit_url = $_POST['form_url'];- $edit_desc = $_POST['form_desc'];+ $edit_id = $wpdb->escape($_POST['id']);+ $edit_url = $wpdb->escape($_POST['form_url']);+ $edit_desc = $wpdb->escape($_POST['form_desc']); if($edit_url == "http://"|| (!$edit_url)){$ERR = $ERR . "<br>You must enter a URL to redirect to!";} if(!$ERR){$wpdb->query("UPDATE $table_name SET link_url='$edit_url',link_desc='$edit_desc' WHERE link_id = $edit_id"); $MES = $MES . "<br>The redirect URL has been modified."; } }if($action == "delete"){ $delete_id = $_POST['id']; $wpdb->query("DELETE FROM $table_name WHERE link_id = '$delete_id'"); $MES = $MES . "<br>Redirect deleted!"; } if($action == "clearall"){ $wpdb->query("UPDATE $table_name SET link_count='0' WHERE link_count >0"); $MES = $MES . "<br>Counts have been reset!"; }} ?> <div class=wrap> <form method="post"><h2>Short URL Admin</h2><?php if($ERR){echo "<p>". $ERR . "</p>";}if($MES){echo "<p>". $MES . "</p>";} ?><p>Short URL allows you to create shorter URL's and keeps track of how manytimes a link has been clicked. It's useful for managing downloads,keeping trackof outbound links and for masking URL's. Clicking the Clear All Clicks buttonwill reset the count for each entry. Visit the <a href="http://www.harleyquine.com/php-scripts/short-url-plugin/">plugin page</a>for more information about this plugin.</p><h2>Current Redirects</h2><table class="widefat"> <thead> <tr> <th scope="col">Short URL (The URL to use)</th> <th scope="col">Real URL (Where it redirects to)</th> <th scope="col">Notes</th> <th scope="col">Amount of Clicks</th> <th scope="col">Manage</th> </tr></thead> <tbody id="the-list"><?php $rowdata = $wpdb->get_results("SELECT * FROM $table_name"); foreach ($rowdata as $row){ $is_editing = $_POST['edit_id']; if($is_editing){if($is_editing == $row->link_id){$EDIT = 1;$EDIT_ID = $row->link_id;$EDIT_URL = $row->link_url;$EDIT_DESC = $row->link_desc;} }?> <tr class='<?php echo $class;?>'> <th scope="row"><a href="<? echo get_option("siteurl") . "/u/". $row->link_id;?>"target="_blank"><? echo get_option("siteurl") . "/u/". $row->link_id;?></a></th> <td><? echo $row->link_url;?></td> <td><? echo $row->link_desc;?></td> <td><? echo $row->link_count;?></td> <td><form method="post"name="delete"><input type="hidden"name="action"value="delete"><input type="hidden"name="id"value="<? echo $row->link_id;?>"><input type="submit"value="Delete"></form><form method="post"name="edit"><input type="hidden"name="edit_id"value="<? echo $row->link_id;?>"><input type="submit"value="Edit"></form></td>...snip...DetailsAffected Software:PunBB Fixed in Version:1.3 Issue Type:SQL Injection (SQLi) Original Code:Found Here DescriptionThis week’s bug was an old SQL injection bug that affected PunBB versions <1.3. In short,a value is taken from an attacker/user controlled POST request and is used to build a SQL statement. This bug actually requires a small amount of tracing,so here we go! First,we see that PunBB takes the attacker/user supplied content here (line 11) $form = array_map('trim',$_POST['form']);The line above uses the value passed via $_POST[‘form’] to populate the $form variable. The value goes through a trim() function,but is (for the most part) un-sanitized. It’s interesting that PHP allows for the submission of arrays through POST parameters. This behavior is mentioned in the comments on this page Next,the $form variable (which contains our attacker supplied values from $_POST[‘form’]) is used in a foreach statement and each index of the $form variable value is used in some application logic. You can see this in the following line (line 19) foreach ($form as $key =>$input) The foreach extracts the various values from the $form variable,does a quick comparison to a configuration value of some sort. If the comparison returns the correct value,the application uses the tainted value to populate a $query array variable. The tainted value is used here (line 26) 'SET'=>'conf_value='.$input, The name of the variable ($query),along with the names of the indexes in the array (UPDATE,SET,WHERE),and finally the names of variables/functions close-by ($forum_db,query_build) are dead giveaways that the untainted value will eventually be used in a SQL query. Use of a tainted value in this manner leads to SQL injection. The developers addressed this issue by casting the tainted $input value to an int before using it to build a SQL statement. Developers Solution...<snip>...// Load the admin.php language filerequire FORUM_ROOT.'lang/'.$forum_user['language'].'/admin_common.php';require FORUM_ROOT.'lang/'.$forum_user['language'].'/admin_settings.php';$section = isset($_GET['section']) ? $_GET['section']:null;if (isset($_POST['form_sent'])){$form = array_map('trim',$_POST['form']);($hook = get_hook('aop_form_submitted')) ? eval($hook):null;...<snip>...($hook = get_hook('aop_pre_update_configuration')) ? eval($hook):null;foreach ($form as $key =>$input){// Only update permission values that have changedif (array_key_exists('p_'.$key,$forum_config) &&$forum_config['p_'.$key] != $input){$query = array('UPDATE'=>'config',-'SET'=>'conf_value='.$input,+'SET' =>'conf_value='.intval($input),'WHERE'=>'conf_name=\'p_'.$forum_db->escape($key).'\'');($hook = get_hook('aop_qr_update_permission_conf')) ? eval($hook):null;$forum_db->query_build($query) or error(__FILE__,__LINE__)}// Only update option values that have changedif (array_key_exists('o_'.$key,$forum_config) &&$forum_config['o_'.$key] != $input){if ($input != '' || is_int($input))$value = '\''.$forum_db->escape($input).'\'';else$value = 'NULL';$query = array('UPDATE'=>'config','SET'=>'conf_value='.$value,'WHERE'=>'conf_name=\'o_'.$forum_db->escape($key).'\'');($hook = get_hook('aop_qr_update_permission_option')) ? eval($hook):null;$forum_db->query_build($query) or error(__FILE__,__LINE__)}}// Regenerate the config cacheif (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))require FORUM_ROOT.'include/cache.php';generate_config_cache();($hook = get_hook('aop_pre_redirect')) ? eval($hook):null;redirect(forum_link($forum_url['admin_settings_'.$section]),$lang_admin_settings['Settings updated'].' '.$lang_admin_common['Redirect'])}if (!$section || $section == 'setup'){// Setup the form$forum_page['group_count'] = $forum_page['item_count'] = $forum_page['fld_count'] = 0;// Setup breadcrumbs$forum_page['crumbs'] = array(array($forum_config['o_board_title'],forum_link($forum_url['index'])),array($lang_admin_common['Forum administration'],forum_link($forum_url['admin_index'])),array($lang_admin_common['Settings'],forum_link($forum_url['admin_settings_setup'])),array($lang_admin_common['Setup'],forum_link($forum_url['admin_settings_setup'])));($hook = get_hook('aop_setup_pre_header_load')) ? eval($hook):null;define('FORUM_PAGE_SECTION','settings');define('FORUM_PAGE','admin-settings-setup');require FORUM_ROOT.'header.php';// START SUBST - <!-- forum_main -->ob_start();($hook = get_hook('aop_setup_output_start')) ? eval($hook):null;?><div class="main-content main-frm"><form class="frm-form"method="post"accept-charset="utf-8"action="<?php echo forum_link($forum_url['admin_settings_setup']) ?>"><div class="hidden"><input type="hidden"name="csrf_token"value="<?php echo generate_form_token(forum_link($forum_url['admin_settings_setup'])) ?>"/><input type="hidden"name="form_sent"value="1"/></div><div class="content-head"><h2 class="hn"><span><?php echo $lang_admin_settings['Setup personal'] ?></span></h2></div>...<snip>...DetailsAffected Software:Members-only WordPress plugin Fixed in Version:0.6.7 Issue Type:SQL Injection Original Code: Found Here DescriptionThis week’s vuln was an easy one The vulnerability affected the “members-only” plug-in for WordPress and was patched in version 0.6.7. There are a couple changes in the diff,but the most important change (from a security standpoint) is the transition from a string-built,dynamic SQL statement to a prepared statement. The following lines show the SQL injection bug: $feedkey = $_GET['feedkey']; … <snip>… $find_feedkey = $wpdb->get_results(“SELECT umeta_id FROM $wpdb->usermeta WHERE meta_value = ‘$feedkey’”);
The members-only developers assigned a value for the $feedkey variable directly from the querystring. They then used the attacker controlled value to build a SQL statement,using the $feedkey value to complete the SQL WHERE clause. The symptoms presented above are classic SQL injection. I’m happy to see that the members-only developers chose to use prepared statements to fix the SQL injection. There are a few other fixes here,but the SQL injection was the most important security fix. Developers Solution1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| -if (empty($userdata->ID) &&$members_only_opt['feed_access'] != 'feednone') //Check if user is logged in or Feed Keys is required +if (empty($userdata->ID) || $members_only_opt['feed_access'] != 'feednone') //Check if user is logged in or Feed Keys is required { $feedkey = $_GET['feedkey']; if (!empty($feedkey)) { // Check if Feed Key is in the Database -$find_feedkey = $wpdb->get_results("SELECT umeta_id FROM $wpdb->usermeta WHERE meta_value = '$feedkey'"); +$find_feedkey = $wpdb->get_results( $wpdb->prepare( + "SELECT umeta_id FROM $wpdb->usermeta WHERE meta_value = %s", + $feedkey +) );
if (!empty($find_feedkey) &&$members_only_opt['feed_access'] == 'feedkeys') //If Feed Key is found and using Feed Keys { $feedkey_valid = TRUE; if (empty($feedkey) &&$members_only_opt['feed_access'] == 'feedkeys') { $feed = members_only_create_feed('No Feed Key Found',$errormsg['feedkey_missing']); -header("Content-Type:application/xml;charset=ISO-8859-1"); +header("Content-Type:application/xml;charset=". get_bloginfo( 'charset' ),true); echo $feed; exit; } elseif ($feedkey_valid == FALSE &&$members_only_opt['feed_access'] == 'feedkeys') { $feed = members_only_create_feed('Feed Key is Invalid',$errormsg['feedkey_invalid']); -header("Content-Type:application/xml;charset=ISO-8859-1"); +header("Content-Type:application/xml;charset=". get_bloginfo( 'charset' ),true); echo $feed; exit; }
if (empty($feedkey) &&$members_only_opt['feed_access'] == 'feedkeys') { $feed = members_only_create_feed('No Feed Key Found',$errormsg['feedkey_missing']); -header("Content-Type:application/xml;charset=ISO-8859-1"); +header("Content-Type:application/xml;charset=". get_bloginfo( 'charset' ),true); echo $feed; exit; } |
DetailsAffected Software:The Hacker’s Diet (WordPress Plugin) Fixed in Version:0.9.7b Issue Type:SQL Injection Original Code: Found Here DescriptionThis week’s vulnerability was a SQL injection vulnerability affecting the Hacker’s Diet WordPress plugin. In the vulnerable version,the plugin assigns several variables using values obtained directly from the querystring. The variable assignments are shown below: $weeks = $_GET["weeks"]; $start_date = $_GET["start_date"]; $end_date = $_GET["end_date"]; $goal = $_GET["goal"]; $user_id = $_GET["user"]; $maint_mode = $_GET["maint_mode"];
No sanitization or validation is done before assigning the values. Once the assignments are made,the attacker controlled values are then passed to a dynamic SQL string here resulting in SQL Injection: $query = “select date,weight,trend from “.$table_prefix.”hackdiet_weightlog where wp_id = $user_id and date >\”".date(“Y-m-d”,strtotime(“$weeks weeks ago”)).”\”order by date asc”; $query = “select date,weight,trend from “.$table_prefix.”hackdiet_weightlog where wp_id = $user_id and date >= \”$start_date\”and date <= \”$end_date\”order by date asc”;
The plugin authors patched this vulnerability by validating that the $_GET[“user”] and $_GET[“weeks”] parameters contains only numeric characters. An interesting exercise would be to trace through the code and find where the following variables are being used: $start_date = $_GET["start_date"]; $end_date = $_GET["end_date"]; $goal = $_GET["goal"]; $maint_mode = $_GET["maint_mode"];
Developers Solution1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| <?php include (dirname(__FILE__)."/jpgraph/jpgraph.php"); include (dirname(__FILE__)."/jpgraph/jpgraph_line.php"); include (dirname(__FILE__)."/jpgraph/jpgraph_scatter.php");
// get our db settings without loading all of wordpress every save $html = implode('',file("../../../wp-config.php")); $html = str_replace ("require_once","// ",$html); $html = str_replace ("<?php","",$html); $html = str_replace ("?>","",$html); eval($html);
mysql_connect(DB_HOST,DB_USER,DB_PASSWORD); mysql_select_db(DB_NAME);
+if (!is_numeric($_GET["user"]) || !is_numeric($_GET["weeks"])) { + exit; +}
$weeks = $_GET["weeks"]; $start_date = $_GET["start_date"]; $end_date = $_GET["end_date"]; $goal = $_GET["goal"]; $user_id = $_GET["user"]; $maint_mode = $_GET["maint_mode"];
if ($weeks) { - $query = "select date,weight,trend from ".$table_prefix."hackdiet_weightlog where wp_id = $user_id and date >\"".date("Y-m-d",strtotime("$weeks weeks ago"))."\"order by date asc"; + $query = "select date,weight,trend from ".$table_prefix."hackdiet_weightlog where wp_id = \"". $user_id . "\"and date >\"".date("Y-m-d",strtotime("$weeks weeks ago"))."\"order by date asc"; } else if ($start_date and $end_date) { - $query = "select date,weight,trend from ".$table_prefix."hackdiet_weightlog where wp_id = $user_id and date >= \"$start_date\"and date <= \"$end_date\"order by date asc"; + $query = "select date,weight,trend from ".$table_prefix."hackdiet_weightlog where wp_id = \"". $user_id . "\"and date >= \"$start_date\"and date <= \"$end_date\"order by date asc"; }
result = mysql_query($query); if (mysql_num_rows($result)) { if (mysql_num_rows($result) == 1) { // only one day,gotta finagle the display
$row = mysql_fetch_assoc($result);
// fake day before $weight_data[] = 0; if ($goal >0) { $goal_data[] = $goal; } $x_data[] = date("n/j",strtotime("yesterday",strtotime($row["date"])));
// data $weight_data[] = $row["weight"]; if ($goal >0) { $goal_data[] = $goal; } $x_data[] = date("n/j",strtotime($row["date"]));
// fake day after $weight_data[] = 0; if ($goal >0) { $goal_data[] = $goal; } $x_data[] = date("n/j",strtotime("tomorrow",strtotime($row["date"]))); } else { $num_rows = mysql_num_rows($result); if ($num_rows <= 7 * 2) { // 0-2 weeks $ticks = "daily"; } else if ($num_rows <= 31 * 4) { // 2 weeks - 4 months $ticks = "weekly"; } else { // 4 months + $ticks = "monthly"; }
$count = 1; while ($row = mysql_fetch_assoc($result)) { $weight_data[] = $row["weight"]; $trend_data[] = $row["trend"]; if ($goal >0) { $goal_data[] = $goal; } switch ($ticks) { case "weekly": if ($count == 1) { $x_data[] = date("n/j",strtotime($row["date"])); } else { $x_data[] = ""; if ($count == 7) { $count = 0; } } break; case "monthly": if (date("j",strtotime($row["date"])) == "1") { $x_data[] = date("n/j",strtotime($row["date"])); } else { $x_data[] = ""; } break; case "daily": default: $x_data[] = date("n/j",strtotime($row["date"])); break; }
$count++; } } |
DetailsAffected Software:WP Category Manager plugin Fixed in Version:1.3.1.0 Issue Type:Sql Injection Original Code: Found Here DescriptionThis week’s vulnerability affected the WP Category Manager plugin. There were two interesting characteristics I noticed with this code fix. First,while there were a number of SQL injection vulnerabilities fixed with this change list,there were also a large number of non security fixes included in this change list as well. It’s generally a good idea to keep security change lists separate from other change lists. The number of non security fixes included in this particular list was so distracting,I removed them from the post. The SQL injection fixes are pretty straight forward,changing dynamically built SQL statements into WordPress’ built-in $wpdb->prepare() function. The second characteristic that caught my attention was usage of numeric IDs at the end of SQL statements. For example: where object_id = $postId and term_taxonomy_id= $categoryId”;
This syntax creates a condition in which the typical addslashes() used to protect against SQL injection can be bypassed. For example,an attacker could craft a SQL injection string like: Sqli.php?categoryId=-1 union select 1,2,3,4,5–
As you can see,the injection string above contains no special characters that would be escaped by addslashes(). Fortunately,the Category Manager plugin developers chose to utilze $wpdb->prepare() instead of addslashes(). Developers Solution1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
| <?php
include_once('wpcm-options.php');
if( ! class_exists('wpcm_functions')):
class wpcm_functions { public static function remove_category($postId,$categoryId) { - global $wpdb; - $wpdb->show_errors(); - $queryStr = "DELETE FROM $wpdb->term_relationships - where object_id = $postId and term_taxonomy_id= $categoryId"; + echo $postId;
- $wpdb->query($queryStr); + if(is_int(intval($postId))) + { + global $wpdb; + + $wpdb->show_errors(); + + $queryStr = $wpdb->prepare("DELETE FROM $wpdb->term_relationships where object_id = %d and term_taxonomy_id= %s",$postId,$categoryId); + $wpdb->query($queryStr); + } }
public static function get_posts($category,$page) { global $wpdb; $wpdb->show_errors();
// First figure out how many posts to show per page. This will be your limit $pageSize = wpcm_options::get_option('postsperpage');
$finalQueryLine = '';
if($pageSize != -1) { // Next figure out how many posts to skip. This will be your offset $offset = $pageSize * $page;
$finalQueryLine = "limit ". $pageSize . "offset ". $offset;
+ } + + $querystr = $wpdb->prepare("select wposts.*,wp_term_taxonomy.term_taxonomy_id + from $wpdb->posts wposts + LEFT JOIN $wpdb->term_relationships wp_term_relationships ON wposts.ID = wp_term_relationships.object_id + LEFT JOIN $wpdb->term_taxonomy wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id + LEFT JOIN $wpdb->terms wp_terms ON wp_terms.term_id = wp_term_taxonomy.term_id + WHERE wp_term_taxonomy.taxonomy = 'category' + and wp_terms.name = '%s' + and wposts.post_status='publish' + ORDER BY wposts.ID ". $finalQueryLine ,$category); + + $postlist = $wpdb->get_results($querystr); + return $postlist; } - $querystr = "select wposts.*,wp_term_taxonomy.term_taxonomy_id - from $wpdb->posts wposts - LEFT JOIN $wpdb->term_relationships wp_term_relationships ON wposts.ID = wp_term_relationships.object_id - LEFT JOIN $wpdb->term_taxonomy wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id - LEFT JOIN $wpdb->terms wp_terms ON wp_terms.term_id = wp_term_taxonomy.term_id - WHERE wp_term_taxonomy.taxonomy = 'category' - and wp_terms.name = '". $category . "' - and wposts.post_status='publish' - ORDER BY wposts.ID ". $finalQueryLine; - - $postlist = $wpdb->get_results($querystr); - return $postlist; }
public static function get_postCount($category) { global $wpdb; $wpdb->show_errors();
- $querystr = "select count(*) - from $wpdb->posts wposts - LEFT JOIN $wpdb->term_relationships wp_term_relationships ON wposts.ID = wp_term_relationships.object_id - LEFT JOIN $wpdb->term_taxonomy wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id - LEFT JOIN $wpdb->terms wp_terms ON wp_terms.term_id = wp_term_taxonomy.term_id - WHERE wp_term_taxonomy.taxonomy = 'category' - and wp_terms.name = '". $category . "' - and wposts.post_status='publish'"; + $querystr = $wpdb->prepare("select count(*) + from $wpdb->posts wposts + LEFT JOIN $wpdb->term_relationships wp_term_relationships ON wposts.ID = wp_term_relationships.object_id + LEFT JOIN $wpdb->term_taxonomy wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id = wp_term_taxonomy.term_taxonomy_id + LEFT JOIN $wpdb->terms wp_terms ON wp_terms.term_id = wp_term_taxonomy.term_id + WHERE wp_term_taxonomy.taxonomy = 'category' + and wp_terms.name = '%s' + and wposts.post_status='publish'",$category);
$result = $wpdb->get_var($querystr,0,0); return $result;
}
public static function render_posts($postlist) { if($postlist) { foreach($postlist as $post) { echo '<div id="catmanagerpost'. $post->ID .'">'; echo '<span ><a href="'. get_permalink($post->ID) .'"title="'.$post->post_title . '">' . $post->post_title . '</a></span><span >' . date_format(date_create($post->post_date),"F j,Y") . '</span>'; echo '<p ><a href="javascript:void(0);"postID="'.$post->ID.'"catID="'. $post->term_taxonomy_id .'"id="catmanremovepost'. $post->ID .'"title="Remove post from this category">Remove</a>| '; echo edit_post_link('Edit Post','','',$post->ID); echo '</p></div>'; } } else { echo '<strong>No posts found</strong>'; } }
public static function render_postcount($category) { $count = wpcm_functions::get_postCount($category);
echo '<span id="wpcmpostcount">'.$count.'</span>'; }
public static function get_categories() { global $wpdb;
$wpdb->show_errors();
$querystr = "select wt.name,wt.term_id from $wpdb->terms wt join $wpdb->term_taxonomy wtt on wtt.term_id = wt.term_id where wtt.taxonomy = 'category' order by wt.name";
$catlist = $wpdb->get_results($querystr); return $catlist; } } endif;
?> |
|