| DetailsAffected Software:BezahlCode-Generator Fixed in Version:1.1 Issue Type:Cross Site Scripting (XSS) Original Code:Found Here DescriptionA couple straightforward XSS bugs. $_REQUEST will create an associative array which contains the contents of $_GET,$_POST,and $_COOKIE which are all user/attacker controllable. These variables are then used to create HTML markup. Security bugs are caused by many different reasons. When auditing code for security issues,if you come across issues like the ones shown below its highly likely that the developer simply doesn’t understand the security risk they created. It might be a good idea to review other change lists associated with this developer as they will likely contain similar code symptoms. This type of issue is also indicative of lack of security awareness. The developer here could use some security education about various security issues along with some tips on preventing these types of security issues in the future. Developers Solution<?php if ($data!='') {?><img src="/generator/?generate=<?php echo urlencode($data)?>"/><?php }?></div><br/><form action="/generator/"name="wizard"method="post"class="BezahlCodeForm"><label for="singlepayment"><input type="radio"id="singlepayment"name="gen_type"value="singlepayment"<?php if($_REQUEST['gen_type']=="singlepayment"|| empty($_REQUEST['gen_type'])) echo 'checked="checked"'?>/>Überweisung</label><br /><label for="singlepaymentspende"><input type="radio"id="singlepaymentspende"name="gen_type"value="singlepaymentspende"<?php if($_REQUEST['gen_type']=="singlepaymentspende") echo 'checked="checked"'?>/>Spendenzahlung</label><br /><label for="singledirectdebit"><input type="radio"id="singledirectdebit"name="gen_type"value="singledirectdebit"<?php if($_REQUEST['gen_type']=="singledirectdebit") echo 'checked="checked"'?>/>Lastschrift</label><br />-Name:<br /><input type="text"tooltipText="Format:DTAUS Text"id="gen_name"onblur="checkInput(this,'dtaus')"name="gen_name"maxlength="27"value="<?= isset($_REQUEST['gen_name'])?$_REQUEST['gen_name']:""?>">+Name:<br /><input type="text"tooltipText="Format:DTAUS Text"id="gen_name"onblur="checkInput(this,'dtaus')"name="gen_name"maxlength="27"value="<?= isset($_REQUEST['gen_name'])?htmlspecialchars($_REQUEST['gen_name']):""?>"><br />-Kontonummer:<br /><input type="text"tooltipText="Format:Ganzzahl z.B. 1234"id="gen_account"onblur="checkInput(this,'ganzzahl')"name="gen_account"value="<?= isset($_REQUEST['gen_account'])?$_REQUEST['gen_account']:""?>">+Kontonummer:<br /><input type="text"tooltipText="Format:Ganzzahl z.B. 1234"id="gen_account"onblur="checkInput(this,'ganzzahl')"name="gen_account"value="<?= isset($_REQUEST['gen_account'])?htmlspecialchars($_REQUEST['gen_account']):""?>"><br />-BLZ:<br /><input type="text"tooltipText="Format:Ganzzahl z.B. 1234"id="gen_BNC"onblur="checkInput(this,'ganzzahl')"name="gen_BNC"value="<?= isset($_REQUEST['gen_BNC'])?$_REQUEST['gen_BNC']:""?>">+BLZ:<br /><input type="text"tooltipText="Format:Ganzzahl z.B. 1234"id="gen_BNC"onblur="checkInput(this,'ganzzahl')"name="gen_BNC"value="<?= isset($_REQUEST['gen_BNC'])?htmlspecialchars($_REQUEST['gen_BNC']):""?>"><br />-Betrag in Euro (z.B. 1234,50) <br /><input type="text"tooltipText="Format:Dezimalzahl z.B. 1234,50"onblur="checkInput(this,'dezimalzahl')"id="gen_amount"name="gen_amount"value="<?= isset($_REQUEST['gen_amount'])?$_REQUEST['gen_amount']:""?>">+Betrag in Euro (z.B. 1234,50) <br /><input type="text"tooltipText="Format:Dezimalzahl z.B. 1234,50"onblur="checkInput(this,'dezimalzahl')"id="gen_amount"name="gen_amount"value="<?= isset($_REQUEST['gen_amount'])?htmlspecialchars($_REQUEST['gen_amount']):""?>"><br />-Verwendungszweck:<br /><input type="text"id="gen_reason"tooltipText="Format:DTAUS Text"onblur="checkInput(this,'dtaus')"name="gen_reason"maxlength="54"value="<?= isset($_REQUEST['gen_reason'])?$_REQUEST['gen_reason']:""?>">+Verwendungszweck:<br /><input type="text"id="gen_reason"tooltipText="Format:DTAUS Text"onblur="checkInput(this,'dtaus')"name="gen_reason"maxlength="54"value="<?= isset($_REQUEST['gen_reason'])?htmlspecialchars($_REQUEST['gen_reason']):""?>"><br/><input type="button"value="Erstellen"onclick='javascript:generateImage();'></form><?php if(!(get_option("bezahlcode_showlink") == "hidden")){?><br /><span class="bezahlCodeLink">Weitere Informationen:<a href="http://www.bezahlcode.de"title="BezahlCode - Schnell,einfach und sicher bezahlen"target="_blank">www.bezahlcode.de</a></span><?php } ?></div><script type="text/javascript">var tooltipObj = new DHTMLgoodies_formTooltip();tooltipObj.initFormFieldTooltip();</script>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:AskApache Password Protector Fixed in Version:4.0.1 Issue Type:Cross Site Scripting (XSS) Original Code:Found Here DescriptionUpon first glance,we see that the vulnerable code sample comes from an error page of some sort. Error pages are often overlooked when it comes to security (or even general QA). Make sure you put your error pages through the same rigorous security process as you would any other page. The Same Origin Policy won’t distinguish between a forgotten error page and the highly trafficked portal page of your web application. A vulnerability on an error page can have the same devastating effect as a vulnerability on the main portal page. Looking at this bug,we see that the error page is a bit too helpful and echoes back all the information contained in the $_SERVER superglobal. Unfortunately,this superglobal contains all sorts of user/attacker controlled information,resulting in XSS. In this fix,the developers wisely removed the vulnerable line entirely. Developers Solution<?phpob_start();//http://www.askapache.com/htaccess/apache-status-code-headers-errordocument.html... <SNIP>...if (isset($_SERVER['REDIRECT_STATUS'])) $err_code = $_SERVER['REDIRECT_STATUS'];$err_req_meth = $_SERVER['REQUEST_METHOD'];$err_req = htmlentities(strip_tags($_SERVER['REQUEST_URI']));$err_phrase = $err_status_codes[$err_code][0];$err_body = str_replace( array('INTERROR','THEREQUESTURI','THEREQMETH'),array('The server encountered an internal error or misconfiguration and was unable to complete your request.',$err_req,$err_req_meth),$err_status_codes[$err_code][1]);@header("HTTP/1.1 $err_code $err_phrase",1);@header("Status:$err_code $err_phrase",1);//400 || 408 || 413 || 414 || 500 || 503 || 501//@header("Connection:close",1);if ( $err_code=='400'||$err_code=='403'||$err_code=='405'||$err_code[0]=='5'){@header("Connection:close",1);if ($err_code == '405') @header('Allow:GET,HEAD,POST,OPTIONS,TRACE');echo "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html>\n<head>\n<title>{$err_code}{$err_phrase}</title>\n<h1>{$err_phrase}</h1>\n<p>{$err_body}<br>\n</p>\n</body></html>"} else echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xml:lang="en"lang="en"><head><title>'.$err_code.' '.$err_phrase.'</title><meta http-equiv="content-type"content="text/html;charset=UTF-8"/></head><body><h1>'.$err_code.' '.$err_phrase.'</h1><hr /><p>'.$err_body.'<br /></p><pre>-'.print_r($_SERVER,1).'</pre></body></html>';?>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:Subscribe to Comments Plugin Fixed in Version:2.1 Issue Type:Cross Site Scripting (XSS) Original Code:Found Here DescriptionA familiar symptom here with the same ole result. In the vulnerable code we see a call to $_SERVER['REQUEST_URI']. For some reason,many developers assume REQUEST_URI cannot be tainted and used for XSS. REQUEST_URI will not only include the path to current php file,it will also include any querystring parameters in the URI as well. Here’s a few examples of what REQUEST_URI will return: http://spotthevuln.com/blah/ results in –>/ http://spotthevuln.com/blah//index.php results in –>/blah/index.php http://spotthevuln.com/blah/index.php?qs=value results in –>/blah/index.php?qs=value http://spotthevuln.com/blah/index.php/qs1/qs2 results in –>/blah/index.php/qs1/qs2
As you can see an attacker can easily taint the REQUEST_URI value,using it in XSS attacks. The developers addressed this vulnerability by encoding calls to REQUEST_URI. Developers Solution<?phpfunction show_subscription_checkbox ($id='0'){global $sg_subscribe;sg_subscribe_start();if ( $sg_subscribe->checkbox_shown ) return $id;if ( !$email = $sg_subscribe->current_viewer_subscription_status() ):?><?php ?><?php ?><?php ?><p <?php if ($sg_subscribe->clear_both) echo 'style="clear:both;"';?>class="subscribe-to-comments"> <input type="checkbox"name="subscribe"id="subscribe"value="subscribe"style="width:auto;"<?php if ($sg_subscribe->default_subscribed) echo 'checked="checked"';?>/> <label for="subscribe"><?php echo $sg_subscribe->not_subscribed_text;?></label></p><?php ?><?php elseif ( $email == 'admin' &¤t_user_can('manage_options') ):?><?php ?><?php ?><?php ?><p <?php if ($sg_subscribe->clear_both) echo 'style="clear:both;"';?>class="subscribe-to-comments"><?php echo str_replace('[manager_link]',$sg_subscribe->manage_link($email,true,false),$sg_subscribe->author_text);?></p><?php else:?><?php ?><?php ?><?php ?><p <?php if ($sg_subscribe->clear_both) echo 'style="clear:both;"';?>class="subscribe-to-comments"><?php echo str_replace('[manager_link]',$sg_subscribe->manage_link($email,true,false),$sg_subscribe->subscribed_text);?></p><?php ?><?php endif;$sg_subscribe->checkbox_shown = true;return $id}function show_manual_subscription_form (){global $id,$sg_subscribe,$user_email;sg_subscribe_start();$sg_subscribe->show_errors('solo_subscribe','<div class="solo-subscribe-errors">','</div>',__('<strong>Error:</strong>','subscribe-to-comments'),'<br />');if ( !$sg_subscribe->current_viewer_subscription_status() ):get_currentuserinfo();?><?php ?><?php ?><?php ?>-<form action="http://<?php echo $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ?>"method="post">+<form action="http://<?php echo $_SERVER['HTTP_HOST'] . wp_specialchars($_SERVER['REQUEST_URI']);?>"method="post"><input type="hidden"name="solo-comment-subscribe"value="solo-comment-subscribe"/><input type="hidden"name="postid"value="<?php echo $id;?>"/>-<input type="hidden"name="ref"value="<?php echo 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];?>"/>+<input type="hidden"name="ref"value="<?php echo urlencode('http://' . $_SERVER['HTTP_HOST'] . wp_specialchars($_SERVER['REQUEST_URI']));?>"/><p class="solo-subscribe-to-comments"><?php _e('Subscribe without commenting','subscribe-to-comments');?><br /><label for="solo-subscribe-email"><?php _e('E-Mail:','subscribe-to-comments');?><input type="text"name="email"id="solo-subscribe-email"size="22"value="<?php echo $user_email;?>"/></label><input type="submit"name="submit"value="<?php _e('Subscribe','subscribe-to-comments');?>"/></p></form><?php ?>DetailsAffected Software:WordPress (Core) Fixed in Version:2.3 Issue Type:Cross Site Scripting (XSS) Original Code:Found Here DescriptionInteresting bug here,not because of the technical details associated with the bug but more of the process associated with code check in process. First,the bug is simple,after looking through the code we see a single line that outputs POST and GET parameters into HTML markup. Although the user controlled values are enclosed within HTML comments,the comment tokens do little to prevent XSS as an attacker can simply break out of the HTML comments. What’s surprising here is the line of code that causes the XSS appears to be some kind of debugging statement. Debugging is a necessary function in any large/complex software project. Developers should have a standard way to tag debugging functionality so that it doesn’t accidently make its way into production builds. If the debugging functionality were tagged/marked,it would be easy to identify and could have been flagged by simple automation before/during check in time. This bug also shows that the organization likely doesn’t have robust testing program. A quick buddy test of this page would have likely identified this issue and it could have been removed. I’m also guessing that this debugging functionality doesn’t have any test cases associated with it,which would have raised more red flags. Developers Solution<?phpfunction convert_all_confirm(){print '<div class="narrow">';print '<h3>' . __('Confirm') . '</h3>';print '<p>' . __('You are about to convert all categories to tags. Are you sure you want to continue?') . '</p>';print '<form action="admin.php?import=wp-cat2tag"method="post">';print '<p style="text-align:center"class="submit"><input type="submit"value="' . __('Yes') . '"name="yes_convert_all_cats"/> <input type="submit"value="' . __('No') . '"name="no_dont_do_it"/></p>';print '</form>';print '</div>'}function convert_all(){global $wpdb;$wpdb->query("UPDATE $wpdb->term_taxonomy SET taxonomy = 'post_tag',parent = 0 WHERE taxonomy = 'category'");clean_category_cache($category->term_id)}function init(){-echo '<!--';print_r($_POST);print_r($_GET);echo '-->';if (isset($_POST['maybe_convert_all_cats'])){$step = 3} elseif (isset($_POST['yes_convert_all_cats'])){$step = 4} elseif (isset($_POST['no_dont_do_it'])){die('no_dont_do_it')} else{$step = (isset($_GET['step'])) ? (int) $_GET['step']:1}$this->header();if (!current_user_can('manage_categories')){print '<div class="narrow">';print '<p>' . __('Cheatin’uh?') . '</p>';print '</div>'} else{switch ($step){case 1:$this->welcome();break;case 2:$this->convert_them();break;case 3:$this->convert_all_confirm();break;case 4:$this->convert_all();break}}$this->footer()}function WP_Categories_to_Tags(){// Do nothing.}}$wp_cat2tag_importer = new WP_Categories_to_Tags();register_importer('wp-cat2tag',__('Categories to Tags Converter'),__('Convert existing categories to tags,selectively.'),array(&$wp_cat2tag_importer,'init'));?>DetailsAffected Software:WordPress Fixed in Version:2.1 Issue Type:HTTP Header Injection Original Code:Found Here DescriptionThis week’s bug was a Set-Cookie/HTTP Header injection bug in WordPress Core. Looking at the code,we see that the WordPress Developers assign several variables directly from the $_POST body. These assignments are listed below: $comment_author = trim($_POST['author']); $comment_author_email = trim($_POST['email']); $comment_author_url = trim($_POST['url']); $comment_content = trim($_POST['comment']);
Each of these variables should now be considered tainted and should be sanitized/encoded before using their values in any operations. It seems that the WordPress developers were aware that the values assigned to the variables listed above could not be trusted and proceeded to escape the values before using them in database related operations. Some examples of the sanitization can are provided below: $comment_author = $wpdb->escape($user_identity); $comment_author_email = $wpdb->escape($user_email); $comment_author_url = $wpdb->escape($user_url);
Unfortunately,the developers utilized the incorrect sanitizing routines for the tainted values in the setcookie API. The developers used stripslashes() to sanitize the user controlled value before passing it to the setcookie() API. Although stripslashes can help prevent other types of vulnerabilities,it was not intended to defend against HTTP HEADER injection or cookie poisoning. By injecting a series of CRLF (%0d%0a) characters,an attacker could use this bug to inject arbitrary headers into the HTTP response and in some cases use this header injection bug for XSS. The WordPress developers addressed this issue by passing the attacker controlled value to the clean_url() function before allowing it in the setcookie. The full source for clean_url() can be found here,however the more interesting sanitizing routines are provided below: $url = preg_replace(‘|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\\x80-\\xff]|i’,”,$url); $strip = array(‘%0d’,‘%0a’,‘%0D’,‘%0A’); $url = _deep_replace($strip,$url); $url = str_replace(‘;//’,‘://’,$url);
Developers Solution<?phprequire( dirname(__FILE__) . '/wp-config.php' );nocache_headers();$comment_post_ID = (int) $_POST['comment_post_ID'];$status = $wpdb->get_row("SELECT post_status,comment_status FROM $wpdb->posts WHERE ID = '$comment_post_ID'");if ( empty($status->comment_status) ){do_action('comment_id_not_found',$comment_post_ID);exit} elseif ( 'closed' == $status->comment_status ){do_action('comment_closed',$comment_post_ID);die( __('Sorry,comments are closed for this item.') )} elseif ( 'draft' == $status->post_status ){do_action('comment_on_draft',$comment_post_ID);exit}$comment_author = trim($_POST['author']);$comment_author_email = trim($_POST['email']);$comment_author_url = trim($_POST['url']);$comment_content = trim($_POST['comment']);// If the user is logged inget_currentuserinfo();if ( $user_ID ):$comment_author = $wpdb->escape($user_identity);$comment_author_email = $wpdb->escape($user_email);$comment_author_url = $wpdb->escape($user_url);else:if ( get_option('comment_registration') )die( __('Sorry,you must be logged in to post a comment.') );endif;$comment_type = '';if ( get_settings('require_name_email') &&!$user_ID ){if ( 6 >strlen($comment_author_email) || '' == $comment_author )die( __('Error:please fill the required fields (name,email).') );elseif ( !is_email($comment_author_email))die( __('Error:please enter a valid email address.') )}if ( '' == $comment_content )die( __('Error:please type a comment.') );$commentdata = compact('comment_post_ID','comment_author','comment_author_email','comment_author_url','comment_content','comment_type','user_ID');wp_new_comment( $commentdata );if ( !$user_ID ):setcookie('comment_author_' . COOKIEHASH,stripslashes($comment_author),time() + 30000000,COOKIEPATH,COOKIE_DOMAIN);setcookie('comment_author_email_' . COOKIEHASH,stripslashes($comment_author_email),time() + 30000000,COOKIEPATH,COOKIE_DOMAIN);-setcookie('comment_author_url_' . COOKIEHASH,stripslashes($comment_author_url),time() + 30000000,COOKIEPATH,COOKIE_DOMAIN);+setcookie('comment_author_url_' . COOKIEHASH,stripslashes(clean_url($comment_author_url)),time() + 30000000,COOKIEPATH,COOKIE_DOMAIN);endif;$location = ( empty( $_POST['redirect_to'] ) ) ? get_permalink( $comment_post_ID ):$_POST['redirect_to'];wp_redirect( $location );?>
DetailsAffected Software:WordPress (core) Fixed in Version:1.2 Issue Type:XSS Original Code: Found Here DescriptionLooking at this code made me smile,its about 6 years old. There’s a lot going on here and quite a few issues. The first thing that jumped out at me was the use of $HTTP_POST_FILES. $HTTP_POST_FILES means were working with user controlled files. There are tons of things that can go wrong when dealing with user/attacker controlled files (a list too long to go into here). Lets hope the WordPress devs are on their A-game here. Looking at the patch submitted by the WordPress developers,we see that they changed references to $HTTP_POST_FILES to the superglobal $_FILES. Within the $_FILES array there are a couple indexes that are commonly used. These indexes are: [name] [type] [tmp_name] [error] [size]
Name,type,and size are all controlled by the user/attacker,so the WordPress developers should be wary when dealing with these values. Surprisingly (or unsurprisingly,depending on your point of view),this patch doesn’t contain seem to contain any robust validation of data associated with the uploaded file data. Instead,the defenses put in place here seem to be centered around replacing a poor validation/sanitization routine with a more robust encoding routine which prevents a XSS vulnerability. The replaced sanitization routine and the XSS bug are presented in the following lines: -$imgdesc = str_replace(‘”‘,‘&quot;’,$_POST['imgdesc']); +$imgdesc = htmlentities2($imgdesc); class=”uploadform”value=”<?php echo $imgdesc;?>”/>
What’s surprising is although the WordPress developers prevented this single XSS vulnerability,there is a large number of XSS vulnerabilities in this file. The most obvious symptom is “<?php echo $_REQUEST[] ?>. Additionally,the loose validation of the user uploaded file is concerning,especially with the number of problems that can be encountered when dealing with user controlled files. Maybe the varsity team was on vacation when this patch went in. 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
| <?php //Makes sure they choose a file
//print_r($HTTP_POST_FILES); //die();
- $imgalt = (isset($_POST['imgalt'])) ? $_POST['imgalt']:$imgalt; - - $img1_name = (strlen($imgalt)) ? $_POST['imgalt']:$HTTP_POST_FILES['img1']['name']; - $img1_type = (strlen($imgalt)) ? $_POST['img1_type']:$HTTP_POST_FILES['img1']['type']; - $imgdesc = str_replace('"','&quot;',$_POST['imgdesc']); +$imgalt = basename( (isset($_POST['imgalt'])) ? $_POST['imgalt']:'' ); + +$img1_name = (strlen($imgalt)) ? $imgalt:basename( $_FILES['img1']['name'] ); +$img1_type = (strlen($imgalt)) ? $_POST['img1_type']:$_FILES['img1']['type']; +$imgdesc = htmlentities2($imgdesc);
$imgtype = explode(".",$img1_name); $imgtype = strtolower($imgtype[count($imgtype)-1]);
if (in_array($imgtype,$allowed_types) == false) { die(sprintf(__('File %1$s of type %2$s is not allowed.') ,$img1_name,$imgtype)); }
if (strlen($imgalt)) { $pathtofile = get_settings('fileupload_realpath')."/".$imgalt; -$img1 = $_POST['img1']; +$img1 = $_POST['img1']['tmp_name']; } else { $pathtofile = get_settings('fileupload_realpath')."/".$img1_name; -$img1 = $HTTP_POST_FILES['img1']['tmp_name']; +$img1 = $_FILES['img1']['tmp_name']; }
// makes sure not to upload duplicates,rename duplicates $i = 1; $pathtofile2 = $pathtofile; $tmppathtofile = $pathtofile2; $img2_name = $img1_name;
while (file_exists($pathtofile2)) { $pos = strpos($tmppathtofile,'.'.trim($imgtype)); $pathtofile_start = substr($tmppathtofile,0,$pos); $pathtofile2 = $pathtofile_start.'_'.zeroise($i++,2).'.'.trim($imgtype); $img2_name = explode('/',$pathtofile2); $img2_name = $img2_name[count($img2_name)-1]; }
if (file_exists($pathtofile) &&!strlen($imgalt)) { $i = explode(' ',get_settings('fileupload_allowedtypes')); $i = implode(',',array_slice($i,1,count($i)-2)); $moved = move_uploaded_file($img1,$pathtofile2); // if move_uploaded_file() fails,try copy() if (!$moved) { $moved = copy($img1,$pathtofile2); } if (!$moved) { die(sprintf(__("Couldn't upload your file to %s."),$pathtofile2)); } else { chmod($pathtofile2,0666); @unlink($img1); }
//
// duplicate-renaming function contributed by Gary Lawrence Murphy ?> <p><strong><?php __('Duplicate File?') ?></strong></p> <p><b><em><?php printf(__("The filename '%s' already exists!"),$img1_name);?></em></b></p> <p><?php printf(__("Filename '%1\$s' moved to '%2\$s'"),$img1,"$pathtofile2 - $img2_name") ?></p> <p><?php _e('Confirm or rename:') ?></p> <form action="upload.php"method="post"enctype="multipart/form-data"> <input type="hidden"name="MAX_FILE_SIZE"value="<?php echo get_settings('fileupload_maxk') *1024 ?>"/> <input type="hidden"name="img1_type"value="<?php echo $img1_type;?>"/> <input type="hidden"name="img1_name"value="<?php echo $img2_name;?>"/> <input type="hidden"name="img1_size"value="<?php echo $img1_size;?>"/> <input type="hidden"name="img1"value="<?php echo $pathtofile2;?>"/> <input type="hidden"name="thumbsize"value="<?php echo $_REQUEST['thumbsize'];?>"/> <input type="hidden"name="imgthumbsizecustom"value="<?php echo $_REQUEST['imgthumbsizecustom'];?>"/> <?php _e('Alternate name:') ?><br /><input type="text"name="imgalt"size="30"value="<?php echo $img2_name;?>"/><br /> <br /> <?php _e('Description:') ?><br /><input type="text"name="imgdesc"size="30"value="<?php echo $imgdesc;?>"/> <br /> <input type="submit"name="submit"value="<?php _e('Rename') ?>"/> </form> </div> |
| |