| DetailsAffected Software:Lazyest-Gallery Fixed in Version:0.9 Issue Type:Cross Site Scripting (XSS) Original Code:Found Here DetailsFor most security issues,I give the developer the benefit of the doubt. It’s tough to keep track of all the corner cases and security nuances. For this diff however,there is no excuse. First,let’s cover what the patch fixes. On line 18,the developer was taking a tainted value passed via query string parameter and using that value to build HTML markup. This is XSS in its most classic form. Also,on line 58 the same tainted input is used to build the SRC attribute for an image tag,also resulting in XSS. The developer chose to encode both of these tainted values before using them in the HTML output. Now,let’s talk about the problems with this patch. First,the tainted value used to build the SRC attribute for an image tag needs additional validation. SRC attributes are tricky as they usually cause the browser to issue a request. Escaping the tainted SRC value only prevents the attacker from breaking out of the attribute and injecting their own HTML. Escaping doesn’t prevent the attacker from passing a well formed URI like javascript:javascript-payload-here. I can let the developer slide on this one… chalk it up as a lesson on corner cases. Now,if you look at the patched line,you’ll see that the ALT attribute for the same image tag also contains a XSS vulnerability. Yes,the developer missed a XSS vulnerability that is less than 5 characters away from a fixed XSS vulnerability. This also shows that the developer never tested the patch. The tainted query string parameter is the same for all the vulnerable sections. If the developer tried to test this patch,they would have discovered they were still exposed… Developers Solution<?php// Don't remove this lines:require_once('../../../wp-blog-header.php');global $lg_gallery;?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN""http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type"content="<?php bloginfo('html_type');?>;charset=<?php bloginfo('charset');?>"/><meta name="generator"content="WordPress <?php bloginfo('version');?>"/>-<title><?php echo $_GET['image'] ?></title>+<title><?php echo esc_html($_GET['image']) ?></title><style type="text/css">body{text-align:center;margin:0;padding:0}img{border:none}</style><script type="text/javascript">function WinWidth(){if (window.innerWidth!=window.undefined) return window.innerWidth;if (document.compatMode=='CSS1Compat') return document.documentElement.clientWidth;if (document.body) return document.body.clientWidth;return window.undefined}function WinHeight(){if (window.innerHeight!=window.undefined) return window.innerHeight;if (document.compatMode=='CSS1Compat') return document.documentElement.clientHeight;if (document.body) return document.body.clientHeight;return window.undefined}function FitPic(){iWidth=WinWidth();iHeight=WinHeight();iWidth = document.images[0].width - iWidth;iHeight = document.images[0].height - iHeight;window.resizeBy((iWidth),(iHeight))self.focus()} </script></head><body onload="FitPic()"><a href="javascript:self.close()"title="<?php _e('Click to close',$lg_text_domain);?>">-<img src="<?php echo str_replace("","%20",$lg_gallery->address.$_GET['folder'].$_GET['image']);?>"alt="<?php echo $_GET['image'];?>"/>+<img src="<?php echo str_replace("","%20",$lg_gallery->address.esc_attr($_GET['folder']).esc_attr($_GET['image']));?>"alt="<?php echo $_GET['image'];?>"/></a></body></html><?php?>DetailsAffected Software:The Hackers Diet Fixed in Version:9.7b Issue Type:Cross Site Scripting (XSS) Original Code:Found Here DescriptionFirst,some logistics… the code we’re looking at belongs to the “Weight_save.php” file which is part of the “Hackers Diet” WordPress plugin. This plugin was created to “Help you track and predict weight loss using your WordPress blog”. A single change was made to the Weight_save.php for this changelist. The single change simply removes an obvious XSS bug in which POST parameters are printed to HTML markup. This line was likely being used for debugging purposes and was forgotten during release to production. A more robust testing and release process would have caught this. Although the developers prevented an XSS vulnerability,they completely overlooked several other issues. IndigoMann (via blog comments) noticed XSS and SQL Injection in this code… The one issue that jumps out at me is this line: $user_id = $_POST["user"];
I always become very suspicious when an application passes user_id’s back and forth. Ideally,this data should be stored via session state,otherwise an attacker could pass an arbitrary value and access (and possibly update) another user’s data. Looking at the code,it appears this may be the case with the Hacker Diet plugin. Developers Solution<?// 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);if (isset($_POST["id"]) &&isset($_POST["user"]) &&is_numeric($_POST["user"]) &&isset($_POST["content"]) &&is_numeric($_POST["content"])){$date = substr($_POST["id"],7);$user_id = $_POST["user"];$weight = round($_POST["content"],1)} else{-print_r($_POST);+//print_r($_POST);echo "Please enter a valid number for your weight.";exit}mysql_connect(DB_HOST,DB_USER,DB_PASSWORD);mysql_select_db(DB_NAME);$query = "update ".$table_prefix."hackdiet_weightlog set weight = $weight where wp_id = $user_id and date = \"".date("Y-m-d",$date)."\"";mysql_query($query);if (mysql_affected_rows() != 1){// record doesn't exist yet,lets create it$query = "insert into ".$table_prefix."hackdiet_weightlog set date = \"".date("Y-m-d",$date)."\",weight = $weight,wp_id = $user_id";mysql_query($query);if (mysql_affected_rows() != 1){echo "Save failed. - ". mysql_error();exit()} else{echo htmlspecialchars($weight)}} else{echo htmlspecialchars($weight)}$query = "select trend from ".$table_prefix."hackdiet_weightlog where wp_id = $user_id and date <\"".date("Y-m-d",$date)."\"order by date desc limit 1";$result = mysql_query($query);if (mysql_num_rows($result) == 1){$trend = mysql_result($result,0);$use_first_weight_as_trend = false} else{// no trends exist below this entry,we must be first. so in next query,we need to grab today's weight to be trend 1$use_first_weight_as_trend = true}$query = "select date,weight,trend from ".$table_prefix."hackdiet_weightlog where wp_id = $user_id and date >= \"".date("Y-m-d",$date)."\"order by date asc";$result = mysql_query($query);while ($entry = mysql_fetch_assoc($result)){if ($use_first_weight_as_trend){$trend = $entry["weight"];$use_first_weight_as_trend = false} else{// exponentially smoothed moving average with 10% smoothing$trend = $trend + 0.1 * ($entry["weight"] - $trend)}$entry["trend"] = $trend;$weights[] = $entry}foreach ($weights as $entry){$query = "update ".$table_prefix."hackdiet_weightlog set trend = ".round($entry["trend"],1)."where wp_id = $user_id and date = \"".$entry["date"]."\"";mysql_query($query)}// 0 will always be the edited date,since the list contains the edited entry + all the ones after it,sorted asc.$dif = round($weights[0]["weight"] - $weights[0]["trend"],1);echo "<span class=\"trend_dif ".(($dif <0)?"good_trend":"bad_trend")."\">$dif</span>";?>DetailsAffected Software:WP-Slimbox 2 Fixed in Version:1.0.1 Issue Type:Cross Site Scripting (XSS) Original Code:Found Here DescriptionA bit of a head fake here. There are a lot of variable assignments in this code. Lots of variable assignments results in a lot of tracing during security code audits. As a variable is set with an untrusted value,it becomes tainted. Following that variable until you find exactly where its being used is crucial in understanding whether a security bug exists or not. Any one of those variable assignments could easily result in a major security vulnerability. In this week’s example,the vulnerable line came before the massive set of variable assignments. Once again,we see PHP_SELF being used to create a URL. Instead of trying to encode the value before using it in markup,the developer chose to remove the reference to PHP_SELF. Developers Solution<?php$easingArray = array(swing,easeInQuad,easeOutQuad,easeInOutQuad,easeInCubic,easeOutCubic,easeInOutCubic,easeInQuart,easeOutQuart,easeInOutQuart,easeInQuint,easeOutQuint,easeInOutQuint,easeInSine,easeOutSine,easeInOutSine,easeInExpo,easeOutExpo,easeInOutExpo,easeInCirc,easeOutCirc,easeInOutCirc,easeInElastic,easeOutElastic,easeInOutElastic,easeInBack,easeOutBack,easeInOutBack,easeInBounce,easeOutBounce,easeInOutBounce);$overlayOpacity = array(0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1);$msArray = array(1,100,200,300,400,500,600,700,800,900,1000);$captions = array('a-title','img-alt','img-title','href','None');?><div class="wrap">-<form method="post"action="<?php echo $_SERVER['PHP_SELF']?>?page=slimbox2options"id="options"><?phpecho wp_nonce_field('update-options','wp_slimbox_wpnonce');?><h2><?php _e('WP Slimbox2 Plugin','wp-slimbox2');?></h2>+ <form method="post"action=""id="options"><?php echo wp_nonce_field('update-options','wp_slimbox_wpnonce');?><h2><?php _e('WP Slimbox2 Plugin','wp-slimbox2');?></h2><?phpif(isset($_POST['action']) &&wp_verify_nonce($_POST['wp_slimbox_wpnonce'],'update-options')){$options->update_option(array('autoload' =>$_POST['wp_slimbox_autoload'],'loop' =>$_POST['wp_slimbox_loop'],'overlayOpacity' =>$_POST['wp_slimbox_overlayOpacity'],'overlayColor' =>$_POST['wp_slimbox_overlayColor'],'overlayFadeDuration' =>$_POST['wp_slimbox_overlayFadeDuration'],'resizeDuration' =>$_POST['wp_slimbox_resizeDuration'],'resizeEasing' =>$_POST['wp_slimbox_resizeEasing'],'initialWidth' =>$_POST['wp_slimbox_initialWidth'],'initialHeight' =>$_POST['wp_slimbox_initialHeight'],'imageFadeDuration' =>$_POST['wp_slimbox_imageFadeDuration'],'captionAnimationDuration' =>$_POST['wp_slimbox_captionAnimationDuration'],'caption' =>array($_POST['wp_slimbox_caption1'],$_POST['wp_slimbox_caption2'],$_POST['wp_slimbox_caption3'],$_POST['wp_slimbox_caption4']),'url' =>$_POST['wp_slimbox_url'],'selector' =>$_POST['wp_slimbox_selector'],'counterText' =>$_POST['wp_slimbox_counterText'],'closeKeys' =>$_POST['wp_slimbox_closeKeys'],'previousKeys' =>$_POST['wp_slimbox_previousKeys'],'nextKeys' =>$_POST['wp_slimbox_nextKeys'],'picasaweb' =>$_POST['wp_slimbox_picasaweb'],'flickr' =>$_POST['wp_slimbox_flickr'],'mobile' =>$_POST['wp_slimbox_mobile'],'maintenance' =>$_POST['wp_slimbox_maintenance'],'cache' =>$_POST['wp_slimbox_cache']));echo '<div id="message"class="updated fade"><p><strong>'.__('Settings Saved','wp-slimbox2').'.</strong></p></div>'}$caption = $options->get_option('caption');function selectionGen(&$option,&$array){foreach($array as $key=>$ms){$selected = ($option != $ms)? '':' selected';echo "<option value='$ms'$selected>".(($ms=='1'&&$array[0]!='0')?__('Disabled','wp-slimbox2'):$ms)."</option>\n"}}?><div style="clear:both;padding-top:5px;"></div><h2><?php _e('Settings','wp-slimbox2');?></h2><table class="widefat"cellspacing="0"id="inactive-plugins-table"><thead><tr><th scope="col"colspan="2"><?php _e('Setting','wp-slimbox2');?></th><th scope="col"><?php _e('Description','wp-slimbox2');?></th></tr></thead><tfoot><tr><th scope="col"colspan="3"><?php _e('Use the various options above to control some of the advanced settings of the plugin','wp-slimbox2');?></th></tr></tfoot><tbody class="plugins"><tr class='inactive'><td class='name'><?php _e('Autoload?','wp-slimbox2');?></td><th scope='row' class='check-column'><input type="checkbox"name="wp_slimbox_autoload"<?php if ($options->get_option('autoload') == 'on') echo ' checked="yes"';?>/></th><td class='desc'><p><?php _e('This option allows the user to automatically activate Slimbox on all links pointing to ".jpg",".jpeg",".png",".bmp"or ".gif". All image links will automatically be grouped together in a gallery according to the selector chosen below. If this isn\'t activated you will need to manually add <b><code>rel="lightbox"</code></b>for individual images or <b><code>rel="lightbox-imagesetname"</code></b>for groups on all links you wish to use the Slimbox effect. <b>Default is Disabled.</b>','wp-slimbox2');?></p></td></tr><tr class='inactive'><td class='name'><?php _e('Enable Picasaweb Integration?','wp-slimbox2');?></td><th scope='row' class='check-column'><input type="checkbox"name="wp_slimbox_picasaweb"<?php if ($options->get_option('picasaweb') == 'on') echo ' checked="yes"';?>/></th><td class='desc'><p><?php _e('This option allows the user to automatically add the Slimbox effect to Picasaweb links when provided an appropriate url (this is separate from the autoload script which only functions on direct image links). <b>Default is Disabled.</b>','wp-slimbox2');?></p></td></tr><tr class='inactive'><td class='name'><?php _e('Enable Flickr Integration?','wp-slimbox2');?></td><th scope='row' class='check-column'><input type="checkbox"name="wp_slimbox_flickr"<?php if ($options->get_option('flickr') == 'on') echo ' checked="yes"';?>/></th><td class='desc'><p><?php _e('This option allows the user to automatically add the Slimbox effect to Flickr links when provided an appropriate url (this is separate from the autoload script which only functions on direct image links). <b>Default is Disabled.</b>','wp-slimbox2');?></p></td></tr><tr class='inactive'><td class='name'><?php _e('Loop?','wp-slimbox2');?></td><th scope='row' class='check-column'><input type="checkbox"name="wp_slimbox_loop"<?php if ($options->get_option('loop') == 'on') echo ' checked="yes"';?>/></th><td class='desc'><p><?php _e('This option allows the user to navigate between the first and last images of a Slimbox gallery group when there is more than one image to display. <b>Default is Disabled.</b>','wp-slimbox2');?></p></td></tr><tr class='inactive'><td class='name'><?php _e('Overlay Opacity','wp-slimbox2');?></td><th scope='row' class='check-column'><select name="wp_slimbox_overlayOpacity"><?php selectionGen($options->get_option('overlayOpacity'),$overlayOpacity);?></select></th><td class='desc'><p><?php _e('This option allows the user to adjust the opacity of the background overlay. 1 is completely opaque,0 is completely transparent. <b>Default is 0.8.</b>','wp-slimbox2');?></p></td></tr><tr class='inactive'><td class='name'><?php _e('Overlay Color','wp-slimbox2');?></td><th scope='row' class='check-column'><input type="text"id="wp_slimbox_overlayColor"name="wp_slimbox_overlayColor"value="<?php echo $options->get_option('overlayColor');?>"size="7"maxlength="7"/><div id="picker"></div></th><td class='desc'><p><?php _e('This option allows the user to set the color of the overlay by selecting your hue from the circle and color gradient from the square. Alternatively you may manually enter a valid HTML color code. The color of the entry field will change to reflect your selected color. <b>Default is #000000.</b>','wp-slimbox2');?></p></td></tr><tr class='inactive'><td class='name'><?php _e('Overlay Fade Duration','wp-slimbox2');?></td><th scope='row' class='check-column'><select name="wp_slimbox_overlayFadeDuration"><?php selectionGen($options->get_option('overlayFadeDuration'),$msArray);?></select></th><td class='desc'><p><?php _e('This option allows the user to adjust the duration of the overlay fade-in and fade-out animations,in milliseconds. <b>Default is 400.</b>','wp-slimbox2');?></p></td></tr><tr class='inactive'><td class='name'><?php _e('Resize Duration','wp-slimbox2');?></td><th scope='row' class='check-column'><select name="wp_slimbox_resizeDuration"><?php selectionGen($options->get_option('resizeDuration'),$msArray);?></select></th><td class='desc'><p><?php _e('This option allows the user to adjust the duration of the resize animation for width and height,in milliseconds. <b>Default is 400.</b>','wp-slimbox2');?></p></td></tr>DetailsAffected Software:TimesToCome Stop Bot Registration (WordPress Plugin) Fixed in Version:1.9 Issue Type:SQL Injection Original Code: Found Here DescriptionStraight up SQL Injection vulnerability. The following attacker controlled values are taken via the following lines: $request_time = $_SERVER['REQUEST_TIME']; $http_accept = $_SERVER['HTTP_ACCEPT']; $http_user_agent = $_SERVER['HTTP_USER_AGENT']; $http_remote_addr = $_SERVER['REMOTE_ADDR'];
Then,a few lines down the attacker controlled values are used as part of a dynamic INSERT SQL statement. $sql = “INSERT INTO ”. $registration_log_table_name . ”( ip,email,problem,accept,agent,day ) VALUES ( ‘$http_remote_addr’,‘$user’,‘$error’,‘$http_accept’,‘$http_user_agent’,NOW() )”; $result = $wpdb->query( $sql );
Understanding that taking attacker controlled data and using it as part of a dynamic SQL statement is a bad idea,the plug-in developers checked in a patch to sanitize the data before using in the SQL statement. The path is shown below: $http_accept = htmlentities($http_accept); $http_user_agent = htmlentities($http_user_agent); $http_remote_addr = htmlentities($http_remote_addr); $http_request_uri = htmlentities($html_request_uri);
There are a couple curious things about this patch. First,instead of transitioning away from dynamic SQL building (which can be difficult to get just right),the authors decided to sanitize the input before passing it to the dynamic SQL. Second (and more interesting),is the authors used htmlentities() to escape the data. Htmlentities() is typically used to escape data to prevent XSS attacks,not SQL Injection. Htmlentities() takes a few parameters,one of which is the optional $quote_style parameter. The $quote_style parameter defines how strings with double and single quotes will be escaped. According to the PHP documentation the three options are: ENT_COMPAT - Will convert double-quotes and leave single-quotes alone. ENT_QUOTES - Will convert both double and single quotes. ENT_NOQUOTES –Will leave both double and single quotes unconverted
If the $quote_style is not specified,PHP will default to ENT_COMPAT. Do you think this patch will hold up to the test of time? 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
| // log all requests to register on our blog function ttc_add_to_log( $user,$error) {
global $wpdb; $registration_log_table_name = $wpdb->prefix . "ttc_user_registration_log"; $request_time = $_SERVER['REQUEST_TIME']; $http_accept = $_SERVER['HTTP_ACCEPT']; $http_user_agent = $_SERVER['HTTP_USER_AGENT']; $http_remote_addr = $_SERVER['REMOTE_ADDR'];
if($wpdb->get_var("show tables like '$registration_log_table_name'") != $registration_log_table_name) {ttc_wp_user_registration_install(); }
// wtf? accept statements coming in at over 255 chars? Prevent sql errors and any funny business // by shortening anything from user to 200 chars if over 255 if ( strlen($email) >200 ){ $email = substr ($email,0,200 );} if ( strlen($http_accept ) >200 ) { $http_accept = substr ( $http_accept,0,200 );} if ( strlen($http_user_agent ) >200 ) { $http_user_agent = substr ( $http_user_agent,0,200 );}
+// clean input for database +$http_accept = htmlentities($http_accept); +$http_user_agent = htmlentities($http_user_agent); +$http_remote_addr = htmlentities($http_remote_addr); +$http_request_uri = htmlentities($html_request_uri);
$sql = "INSERT INTO ". $registration_log_table_name . "( ip,email,problem,accept,agent,day ) VALUES ( '$http_remote_addr','$user','$error','$http_accept','$http_user_agent',NOW() )"; $result = $wpdb->query( $sql ); }
// add an email to our email blacklist if we decide it is an bot function ttc_add_to_blacklist( $email ) { global $wpdb; $blacklist_table_name = $wpdb->prefix . "ttc_user_registration_blacklist";
if($wpdb->get_var("show tables like '$blacklist_table_name'") != $blacklist_table_name) { ttc_wp_user_registration_install(); }
if ( strlen($email) >200 ){ $email = substr ($email,0,200 );}
$sql = "INSERT INTO ". $blacklist_table_name . "( blacklisted ) VALUES ( '$email' )"; $result = $wpdb->query( $sql );
}
// add an ip to our ip blacklist if we decide it is a bot function ttc_add_to_ip_blacklist( $ip ) { global $wpdb; $ip_table_name = $wpdb->prefix . "ttc_ip_blacklist";
if($wpdb->get_var("show tables like '$ip_table_name'") != $ip_table_name) { ttc_wp_user_registration_install(); }
$sql = "INSERT INTO ". $ip_table_name . "( ip ) VALUES ( '$ip' )"; $result = $wpdb->query( $sql ); } |
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;
?> |
DetailsAffected Software:AskApache Password Protect Fixed in Version: 4.3.2 Issue Type:Insecure Logging (Defense in Depth) Original Code: Found Here DescriptionThis week’s bug was discovered in the AskApache Password Protect plugin for WordPress. Once again,we are examining “security software” that is designed to provide various security protection mechanisms for a deployed WordPress blog. The description for the AskApache security plug-in is as follows: Advanced Security:Password Protection,Anti-Spam,Anti-Exploits,more to come
A very noble effort indeed This vulnerability was in the aa_pp_hashit() function. The aa_pp_hashit() function takes three arguments:$format,$user,and $pass. The aa_pp_hashit() function then attempts to create a hash containing the creds. Whenever I see functions utilizing crypto,I’m always reminded of this scene in Office Space . In this particular patch,vulnerability was in this line: aa_pp_mess(‘Created ‘.$format.’Hash for ‘.$user.’with Password ‘.$pass);
The aa_pp_mess() function actually logged the clear text username and password before putting it through a hashing function. There is rarely a need to log a clear text password… in fact,I’m going to go out on a limb here and say there is NEVER a good time when you should log a clear text password. Even password hashes or other weird representations of passwords shouldn’t be logged. Logging sensitive data is always tricky. If you’re logging sensitive data please consider the permissions required to access that sensitive data,ensure the file is properly ACL’d and conduct regular audits of log file access. Most importantly,ask yourself: Why do I need to log this data? The vulnerability was fixed by removing references to user password (and even references to the user that called the function). Now I just have to figure out why the AskApache devs are passing a default value for $pass 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
| // aa_pp_hashit //------------------------------------------------------------------------------------------- function aa_pp_hashit($format,$user='',$pass=''){ global $aa_PP; - aa_pp_mess('Created '.$format.' Hash for '.$user.' with Password '.$pass); + aa_pp_mess('Created '.$format.' Hash'); $hash=''; switch ($format){ case 'TEST': $hash=array(); foreach($aa_PP['algorithms'] as $key=>$value)$hash[]=aa_pp_hashit($key,"test{$key}","test{$key}"); return $hash; break; case 'PLAIN': $hash=$user.':'.$pass; break; case 'CRYPT': $seed = NULL; for ($i = 0;$i <8;$i++) {$seed .= substr('0123456789abcdef',rand(0,15),1);} $hash=$user.':'.crypt($pass,"$1$".$seed); break; case 'SHA1': $hash=$user.':{SHA}'.base64_encode(pack("H*",sha1($pass))); break; case 'MD5':// php.net/crypt.php#73619 $saltt = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789"),0,8); $len = strlen($pass);$text = $pass.'$apr1$'.$saltt;$bin = pack("H32",md5($pass.$saltt.$pass)); for($i = $len;$i >0;$i -= 16) { $text .= substr($bin,0,min(16,$i));} for($i = $len;$i >0;$i >>= 1) { $text .= ($i &1) ? chr(0):$pass{0};} $bin = pack("H32",md5($text)); for($i=0;$i<1000;$i++) { $new = ($i &1) ? $pass:$bin;if ($i % 3) $new .= $saltt;if ($i % 7) $new .= $pass;$new .= ($i &1) ? $bin:$pass;$bin = pack("H32",md5($new));} for($i=0;$i<5;$i++) { $k = $i + 6;$j=$i + 12;if($j==16){ $j = 5;} $TRp = $bin[$i].$bin[$k].$bin[$j].$TRp;} $TRp = chr(0).chr(0).$bin[11].$TRp; $TRp = strtr(strrev(substr(base64_encode($TRp),2)),"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); $hash="$user:$"."apr1"."$".$saltt."$".$TRp; break; }
return $hash; }//========================================================================================================================= // aa_pp_show_encryptions //------------------------------------------------------------------------------------------- function aa_pp_show_encryptions($label,$type=0){ global $aa_PP; if($type==0) { ?> <p><label><?php _e($label);?><br /> <select name="aapassformat"id="aapassformat"> <?php foreach($aa_PP['algorithms'] as $key=>$value){?> <option value="<?php echo $key;?>"<?php if($aa_PP['format']==$key)echo ' selected="selected"';elseif($aa_PP['algorithms'][$key]['enabled']!='1')echo ' disabled="disabled"';?>><?php echo $key;?> </option> <?php }?> </select> </label></p> <?php } elseif($type==3) { ?> <p><label><?php _e($label);?><br /> <input id="aapassformat"name="aapassformat"type="hidden"value="<?php echo $aa_PP['format'];?>"/></label></p> <ul> <?php foreach($aa_PP['algorithms'] as $key=>$value){?> <li><label><input name="aapassformat"id="aapassformat<?php echo strtolower($key);?>"type="radio"value="<?php echo $key;?>"<?php if($aa_PP['format']==$key)echo 'checked="checked"'; elseif($aa_PP['algorithms'][$key]['enabled']!='1')echo 'disabled="disabled"';?>/><strong><?php echo $key;?></strong>- <?php echo $aa_PP['algorithms'][$key]['desc'];?></label></li> <?php }?> </ul> <?php } else if($type==4) { ?> <h4><?php _e($label);?></h4> <?php foreach($aa_PP['algorithms'] as $key=>$value){?> <p><strong><?php echo $key;?></strong>- <?php echo $aa_PP['algorithms'][$key]['desc'];?></p> <?php }?> <hr style="visibility:hidden;padding-top:.25em;clear:both;"/> <?php } }//=========================================================================================================================
// aa_pp_mess //------------------------------------------------------------------------------------------- function aa_pp_mess($message=''){ if(@defined('AA_PP_DEBUG_LOGFILE'))error_log($message,3,AA_PP_DEBUG_LOGFILE); - else error_log($message); + else if(AA_PP_DEBUG)error_log($message) if(AA_PP_DEBUG){ ?><div id="message"style="margin:1em auto;"><p><?php echo $message;?></p></div><?php } }//========================================================================================================================= |
DetailsAffected Software: Short URL Plugin Fixed in Version: 2.0 Issue Type:SQL Injection Original Code: Found Here DescriptionThis was a vulnerability that affected the Short URL WordPress plugin. The vulnerability is very straightforward and should have been easily detected by a security code reviewer. The vulnerable code section takes attacker controlled data directly from $_POST[‘form_url’],$_POST[‘form_desc’],and $_POST[‘id’] and uses the tainted value immediately in dynamically built SQL statements. One interesting piece of this particular code fix is that the developers chose to implement the code fixes near the assignment of the variable (as opposed to near consumption,in the SQL statement). Another interesting piece of the code fix is the logic for the following conditional: if($action == “delete”){
looks like the devs may have forgotten something 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
| <?php 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> <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 many times a link has been clicked. It's useful for managing downloads,keeping track of outbound links and for masking URL's. Clicking the Clear All Clicks button will reset the count for each entry. Visit the <a href="<a href="http://www.harleyquine.com/php-scripts/short-url-plugin/%22%3Eplugin">http://www.harleyquine.com/php-scripts/short-url-plugin/">plugin</a>page</a>for more information about this plugin.</p>
<h2>Current Redirects</h2> <table> <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"> ?> |
|