Popular Vulnerable Code

Percentage –Cross Site Scripting

Details

Affected Software:Sermon Browser WordPress Plugin

Fixed in Version:.44

Issue Type:Cross Site Scripting

Original Code:Found Here

Details

There is a lot going on here in this code snippet. First,let’s talk about the patch. The patch adds a check to ensure the user requesting has the rights to edit a post. The added functionality only displays a link (A HREF) if the user has the correct permissions. Let’s hope there are additional checks in place to prevent the execution of this functionality,as opposed to just trying to obscure the link.

Second,there are a few SQL queries. The SQL queries actually seem to be well handled;most values are cast to int,which should work. Of course,Neal Poole and Jacob astutely point out that casts to int cannot always be trusted (http://spotthevuln.com/2011/03/invincible-cross-site-scripting/ <–see comments section). Abraham Kang also points out that some escaping functions can be defeated by using alternate charsets. The better solution is to use parameterized queries.

What about XSS? There is XSS everywhere on this page. In fact,there is so much XSS I’m not even going to try to list all of the exposures. Instead,I’ll focus on two different patterns. Let’s start with a classic XSS bug seen in many different PHP products. On line XX,the developer echoes back $_SERVER[‘PHP_SELF’]. This results in XSS. Many developers think that PHP_SELF will only echo back the current URL path with no user controlled input,but sadly this is not the case. PHP_SELF can almost always be tainted by a user/attacker. This comment on the reserved variables page for PHP sums it up nicely:http://www.php.net/manual/en/reserved.variables.php#55068.

Now,looking at lines 30-35,we see the developer uses stripslashes before echoing back a user controlled value. I’m guessing this is because the developer was worried about echoing back data that was stored with a database escaping function. Unfortunately,stripslashes does not prevent XSS. Pretty much all of the echo statements on this page are vulnerable to XSS (unless the data is being stored in an HTML encoded state). The echo statements on line 68 and line 70 are 100% XSS.

From the looks of this code,it’s likely the developer doesn’t know what XSS is. Any security training should probably first focus on this deficiency. Other code written by this developer should also be audited for XSS.

Developers Solution

<?php...snip...} elseif (isset($_POST['fetch'])){// ajax paginationif (function_exists('wp_timezone_override_offset'))wp_timezone_override_offset();$st = (int) $_POST['fetch'] - 1;if (!empty($_POST['title'])){$cond = "and m.title LIKE '%". mysql_real_escape_string($_POST['title']) . "%' "} else$cond = '';if ($_POST['preacher'] != 0){$cond .= 'and m.preacher_id = ' . (int) $_POST['preacher'] . ' '}if ($_POST['series'] != 0){$cond .= 'and m.series_id = ' . (int) $_POST['series'] . ' '}$m = $wpdb->get_results("SELECT SQL_CALC_FOUND_ROWS m.id,m.title,m.datetime,p.name as pname,s.name as sname,ss.name as ssnameFROM{$wpdb->prefix}sb_sermons as mLEFT JOIN{$wpdb->prefix}sb_preachers as p ON m.preacher_id = p.idLEFT JOIN{$wpdb->prefix}sb_services as s ON m.service_id = s.idLEFT JOIN{$wpdb->prefix}sb_series as ss ON m.series_id = ss.idWHERE 1=1{$cond}ORDER BY m.datetime desc,s.time desc LIMIT{$st},".sb_get_option('sermons_per_page'));$cnt = $wpdb->get_var("SELECT FOUND_ROWS()");?><?php foreach ($m as $sermon):?><tr class="<?php echo ++$i % 2 == 0 ? 'alternate':'' ?>"><th style="text-align:center"scope="row"><?php echo $sermon->id ?></th><td><?php echo stripslashes($sermon->title) ?></td><td><?php echo stripslashes($sermon->pname) ?></td><td><?php echo ($sermon->datetime == '1970-01-01 00:00:00') ? __('Unknown',$sermon_domain):strftime('%d %b %y',strtotime($sermon->datetime));?></td><td><?php echo stripslashes($sermon->sname) ?></td><td><?php echo stripslashes($sermon->ssname) ?></td><td><?php echo sb_sermon_stats($sermon->id) ?></td><td style="text-align:center">-<a href="<?php echo $_SERVER['PHP_SELF']?>?page=sermon-browser/new_sermon.php&mid=<?php echo $sermon->id ?>"><?php _e('Edit',$sermon_domain) ?></a>| <a onclick="return confirm('Are you sure?')"href="<?php echo $_SERVER['PHP_SELF']?>?page=sermon-browser/sermon.php&mid=<?php echo $sermon->id ?>"><?php _e('Delete',$sermon_domain) ?></a>+<?php //Security check+if (current_user_can('edit_posts')){?>+<a href="<?php echo $_SERVER['PHP_SELF']?>?page=sermon-browser/new_sermon.php&mid=<?php echo $sermon->id ?>"><?php _e('Edit',$sermon_domain) ?></a>| <a onclick="return confirm('Are you sure?')"href="<?php echo $_SERVER['PHP_SELF']?>?page=sermon-browser/sermon.php&mid=<?php echo $sermon->id ?>"><?php _e('Delete',$sermon_domain);?></a>|+<?php } ?>+<a href="<?php echo sb_display_url().sb_query_char(true).'sermon_id='.$sermon->id;?>">View</a></td></tr><?php endforeach ?><script type="text/javascript"><?php if($cnt<sb_get_option('sermons_per_page') || $cnt <= $st+sb_get_option('sermons_per_page')):?>jQuery('#right').css('display','none');<?php elseif($cnt >$st+sb_get_option('sermons_per_page')):?>jQuery('#right').css('display','');<?php endif ?></script><?php} elseif (isset($_POST['fetchU']) || isset($_POST['fetchL']) || isset($_POST['search'])){// ajax pagination (uploads)if (isset($_POST['fetchU'])){$st = (int) $_POST['fetchU'] - 1;$abc = $wpdb->get_results("SELECT f.*,s.title FROM{$wpdb->prefix}sb_stuff AS f LEFT JOIN{$wpdb->prefix}sb_sermons AS s ON f.sermon_id = s.id WHERE f.sermon_id = 0 AND f.type = 'file' ORDER BY f.name LIMIT{$st},".sb_get_option('sermons_per_page'))} elseif (isset($_POST['fetchL'])){$st = (int) $_POST['fetchL'] - 1;$abc = $wpdb->get_results("SELECT f.*,s.title FROM{$wpdb->prefix}sb_stuff AS f LEFT JOIN{$wpdb->prefix}sb_sermons AS s ON f.sermon_id = s.id WHERE f.sermon_id <>0 AND f.type = 'file' ORDER BY f.name LIMIT{$st},".sb_get_option('sermons_per_page'))} else{$s = mysql_real_escape_string($_POST['search']);$abc = $wpdb->get_results("SELECT f.*,s.title FROM{$wpdb->prefix}sb_stuff AS f LEFT JOIN{$wpdb->prefix}sb_sermons AS s ON f.sermon_id = s.id WHERE f.name LIKE '%{$s}%' AND f.type = 'file' ORDER BY f.name;")}?><?php if (count($abc) >= 1):?><?php foreach ($abc as $file):?><tr class="file <?php echo (++$i % 2 == 0) ? 'alternate':'' ?>"id="<?php echo $_POST['fetchU'] ? '':'s' ?>file<?php echo $file->id ?>"><th style="text-align:center"scope="row"><?php echo $file->id ?></th><td id="<?php echo $_POST['fetchU'] ? '':'s' ?><?php echo $file->id ?>"><?php echo substr($file->name,0,strrpos($file->name,'.')) ?></td><td style="text-align:center"><?php echo isset($filetypes[substr($file->name,strrpos($file->name,'.') + 1)]['name']) ? $filetypes[substr($file->name,strrpos($file->name,'.') + 1)]['name']:strtoupper(substr($file->name,strrpos($file->name,'.') + 1)) ?></td><?php if (!isset($_POST['fetchU'])){?><td><?php echo stripslashes($file->title) ?></td><?php } ?><td style="text-align:center"><script type="text/javascript"language="javascript">function deletelinked_<?php echo $file->id;?>(filename,filesermon){if (confirm('Do you really want to delete '+filename+'?')){if (filesermon != ''){return confirm('This file is linked to the sermon called ['+filesermon+']. Are you sure you want to delete it?')}return true}return false}</script><?php if (isset($_POST['fetchU'])){?><a id=""href="<?php echo $_SERVER['PHP_SELF']."?page=sermon-browser/new_sermon.php&amp;getid3={$file->id}";?>"><?php _e('Create sermon',$sermon_domain) ?></a>| <?php } ?><a id="link<?php echo $file->id ?>"href="javascript:rename(<?php echo $file->id ?>,'<?php echo $file->name ?>')"><?php _e('Rename',$sermon_domain) ?></a>| <a onclick="return deletelinked_<?php echo $file->id;?>('<?php echo str_replace("'",'',$file->name) ?>','<?php echo str_replace("'",'',$file->title) ?>');"href="javascript:kill(<?php echo $file->id ?>,'<?php echo $file->name ?>');"><?php _e('Delete',$sermon_domain) ?></a></td></tr><?php endforeach ?><?php else:?><tr><td><?php _e('No results',$sermon_domain) ?></td></tr><?php endif ?><?php}die();?>

Charming –XSS (uhhh wait,actually –SQL Injection)

Details

Affected Software:StatPressCN

Fixed in Version:1.9.1

Issue Type:SQL Injection

Original Code:Found Here

Details

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

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

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

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

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

Developers Solution

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

Proportion –Cross Site Scripting

Details

Affected Software:Lazyest-Gallery

Fixed in Version:0.9

Issue Type:Cross Site Scripting (XSS)

Original Code:Found Here

Details

For 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?>

Character –Cross Site Scripting

Details

Affected Software:PhotoSmash

Fixed in Version:1.0.5

Issue Type:Cross Site Scripting (XSS)

Original Code:Found Here

Description

Once again,we see the familiar pattern of the developer taking user/attacker controlled values and using those values to build HTML markup. Line 76 is the start of a large echo statement which writes a couple input fields to markup. The developer uses the $_REQUEST[‘bwbps_galname’] variable to populate the value attribute for one of the input form fields. Although not completely clear from the code snippet,the developers addressed this issue by placing an encoded version of $_REQUEST[‘bwbps_galname’] into a variable named $gallery_name and using the newly encoded value to build the HTML markup.

Although not addressed by this patch,there are a couple of areas that deserve deeper inspection. For example,on line 113 the application is calling a javascript eval on an unknown function. If this function contains user/attacker supplied content,this could result in XSS. Additionally,on line 136 it seems the user/attacker has some influence on variables passed to a SWF object. If the SWF doesn’t have the appropriate logic to handle the tainted data,this could result in a security vulnerability.

Developers Solution

<?php...snip...//Get a link for the Start Slideshow for PicLensfunction getPicLensLink($g,$atts){if($atts['link_text']){$link_text = $atts['link_text']} else{$link_text = 'Start Slideshow <img src="http://lite.piclens.com/images/PicLensButton.png"alt="PicLens"width="16"height="12"border="0"align="absmiddle">'}$picatts['id'] = $g['gallery_id'];$picatts['thumb_width'] = $g['thumb_width'];$picatts['thumb_height'] = $g['thumb_height'];$picatts['gallery_type'] = $g['gallery_type'];$picatts['images'] = $g['images'];$picatts['page'] = $g['page'];if($g['tags'] == 'post_tags'){$picatts['tags'] = $this->getPostTags(0)} else{$picatts['tags'] = $g['tags']}$param_array = $this->filterMRSSAttsFromArray($picatts,"");if( is_array($param_array)){$params = implode("&",$param_array);//$params = urlencode($params)}$ret = '<a class="piclenselink"href="javascript:PicLensLite.start({feedUrl:\''. plugins_url() . '/photosmash-galleries/bwbps-media-rss.php?'. $params . '\'});">' . $link_text . ' </a>';return $ret}function getPostTags($post_id){if(!$post_id ){global $wp_query;$post_id = $wp_query->post->ID}$terms = wp_get_object_terms( $post_id,'post_tag',$args ) ;if(is_array($terms)){foreach( $terms as $term ){$_terms[] = $term->name}unset($terms);if( is_array($_terms)){$ret = implode(",",$_terms)} else{$ret = ""}}return $ret}function mediaUAddGalleryFieldToMediaUploader(){if(isset($_REQUEST['bwbps_galid']) &&(int)$_REQUEST['bwbps_galid']){echo "<input type='hidden' id='bwbps_mediau_galid' name='bwbps_mediau_galid' value='". (int)$_REQUEST['bwbps_galid'] . "' /><input type='hidden' id='bwbps_galid' name='bwbps_galid' value='". (int)$_REQUEST['bwbps_galid'] . "' />-<input type='hidden' name='bwbps_galname' value='". $_REQUEST['bwbps_galname'] . "' />-<div style='background-color:#eaffdf;padding:5px;border:1px solid #a0a0a0;margin:3px;font-size:14px;color:#333;'>Adding to PhotoSmash:". $_REQUEST['bwbps_galname'] . "</div>+<input type='hidden' name='bwbps_galname' value='". $gallery_name . "' />+<div style='background-color:#eaffdf;padding:5px;border:1px solid #a0a0a0;margin:3px;font-size:14px;color:#333;'>Adding to PhotoSmash:". $gallery_name . "</div>"} else{$gid = isset($_REQUEST['bwbps_mediau_galid']) ? (int)$_REQUEST['bwbps_mediau_galid']:0;$galleryDDL = $this->getGalleryDDL($gid,"select gallery","","bwbps_mediau_galid",30,true,true);echo "<div style='padding:5px;margin:3px;font-size:14px;color:#333;'>Add to PhotoSmash:$galleryDDL</div>"}}function mediaUAddGalleryFieldToFlashUploader(){?><script type="text/javascript">if (typeof flashStartUploadFunctions == 'undefined'){var flashStartUploadFunctions = [];function addFlashStartUploadFunction( funct_name ){flashStartUploadFunctions.push( funct_name )}function runFlashStartUploadFunctions(){if( flashStartUploadFunctions.length >0 ){var bwbfunc;for( bwbfunc in flashStartUploadFunctions){eval(flashStartUploadFunctions[ bwbfunc ])}}}}addFlashStartUploadFunction( 'bwbpsAddGalleryToFlashUploader();' );jQuery(window).load( function(){swfu.settings.upload_start_handler = function(){runFlashStartUploadFunctions()}});function bwbpsAddGalleryToFlashUploader(){jQuery('#bwbps_uploaded_images',top.document).show().append('<h4>Flash upload...preview not available.</h4>');var gid = jQuery("#bwbps_mediau_galid_flash").val() + "";if( gid ){swfu.addPostParam('bwbps_mediau_galid',gid);<?phpif(isset($_REQUEST['bwbps_galid']) ){?>swfu.addPostParam('bwbps_galid',gid);<?php}?>}}</script><?phpif(isset($_REQUEST['bwbps_galid']) &&(int)$_REQUEST['bwbps_galid']){$this->count++;echo "<script type='text/javascript'>jQuery(window).load( function(){//Hide the other Media TabsjQuery('#tab-type_url').hide();jQuery('#tab-library').hide();";...snip...?>

Price –Cross Site Scripting

Details

Affected Software:PunBB

Fixed in Version:2.1

Issue Type:Cross Site Scripting (XSS)

Original Code:Found Here

Description

This week’s vulnerability was a XSS bug in PunBB. PunBB was taking an un-trusted value directly from the POST parameter ($_POST[‘prune_sticky’]) and echoing the un-trusted value directly into a value attribute for a hidden form input field. You can see the XSS bug in line 98. This echoing of un-trusted input results in XSS.

The PunBB developers did something I really like here. Instead of fixing the single instance of XSS and moving on,the PunBB developers went a step further and hardened the use of $_POST[‘prune_sticky’]. Instead of allowing users/attacker to provide arbitrary values for $_POST[’prune_sticky’] they restricted the acceptable values to 1 or 0. You can see this fix in line 11. This is a perfect example of root cause analysis in action. The PunBB developers took a few minutes to understand how the application uses $_POST[‘ prune_sticky’] and adjusted the application behavior to protect against other attacks while being transparent to the user. This patch submitted by the PunBB developers goes a long way in protecting their customers and is a great example of being smart about security fixes.

Developers Solution

<?php... <snip>...if (isset($_GET['action']) || isset($_POST['prune']) || isset($_POST['prune_comply'])){if (isset($_POST['prune_comply'])){confirm_referrer('admin_prune.php');$prune_from = $_POST['prune_from'];+$prune_sticky = isset($_POST['prune_sticky']) ? '1':'0';$prune_days = intval($_POST['prune_days']);$prune_date = ($prune_days) ? time() - ($prune_days*86400):-1;@set_time_limit(0);if ($prune_from == 'all'){$result = $db->query('SELECT id FROM '.$db->prefix.'forums') or error('Unable to fetch forum list',__FILE__,__LINE__,$db->error());$num_forums = $db->num_rows($result);for ($i = 0;$i <$num_forums;++$i){$fid = $db->result($result,$i);-prune($fid,$_POST['prune_sticky'],$prune_date);+prune($fid,$prune_sticky,$prune_date);update_forum($fid)}}else{$prune_from = intval($prune_from);-prune($prune_from,$_POST['prune_sticky'],$prune_date);+prune($fid,$prune_sticky,$prune_date);update_forum($prune_from)}// Locate any "orphaned redirect topics"and delete them$result = $db->query('SELECT t1.id FROM '.$db->prefix.'topics AS t1 LEFT JOIN '.$db->prefix.'topics AS t2 ON t1.moved_to=t2.id WHERE t2.id IS NULL AND t1.moved_to IS NOT NULL') or error('Unable to fetch redirect topics',__FILE__,__LINE__,$db->error());$num_orphans = $db->num_rows($result);if ($num_orphans){for ($i = 0;$i <$num_orphans;++$i)$orphans[] = $db->result($result,$i);$db->query('DELETE FROM '.$db->prefix.'topics WHERE id IN('.implode(',',$orphans).')') or error('Unable to delete redirect topics',__FILE__,__LINE__,$db->error())}redirect('admin_prune.php','Posts pruned. Redirecting &hellip;')}$prune_days = $_POST['req_prune_days'];if (!@preg_match('#^\d+$#',$prune_days))message('Days to prune must be a positive integer.');$prune_date = time() - ($prune_days*86400);$prune_from = $_POST['prune_from'];// Concatenate together the query for counting number or topics to prune$sql = 'SELECT COUNT(id) FROM '.$db->prefix.'topics WHERE last_post<'.$prune_date.' AND moved_to IS NULL';-if ($_POST['prune_sticky'] == '0')+if (!$prune_sticky)$sql .= ' AND sticky=\'0\'';if ($prune_from != 'all'){$prune_from = intval($prune_from);$sql .= ' AND forum_id='.$prune_from;// Fetch the forum name (just for cosmetic reasons)$result = $db->query('SELECT forum_name FROM '.$db->prefix.'forums WHERE id='.$prune_from) or error('Unable to fetch forum name',__FILE__,__LINE__,$db->error());$forum = '"'.pun_htmlspecialchars($db->result($result)).'"'}else$forum = 'all forums';$result = $db->query($sql) or error('Unable to fetch topic prune count',__FILE__,__LINE__,$db->error());$num_topics = $db->result($result);if (!$num_topics)message('There are no topics that are '.$prune_days.' days old. Please decrease the value of "Days old"and try again.');$page_title = pun_htmlspecialchars($pun_config['o_board_title']).' / Admin / Prune';require PUN_ROOT.'header.php';generate_admin_menu('prune');?><div class="blockform"><h2><span>Prune</span></h2><div class="box"><form method="post"action="admin_prune.php?action=foo"><div class="inform"><input type="hidden"name="prune_days"value="<?php echo $prune_days ?>"/>-<input type="hidden"name="prune_sticky"value="<?php echo $_POST['prune_sticky'] ?>"/>+<input type="hidden"name="prune_sticky"value="<?php echo $prune_sticky ?>"/><input type="hidden"name="prune_from"value="<?php echo $prune_from ?>"/><fieldset><legend>Confirm prune posts</legend><div class="infldset"><p>Are you sure that you want to prune all topics older than <?php echo $prune_days ?>days from <?php echo $forum ?>? (<?php echo $num_topics ?>topics)</p><p>WARNING! Pruning posts deletes them permanently.</p></div></fieldset></div><p><input type="submit"name="prune_comply"value="Prune"/><a href="javascript:history.go(-1)">Go back</a></p></form></div></div><div class="clearer"></div></div>... <snip>...

Last –Cross Site Scripting

Details

Affected Software:AskApache Password Protector

Fixed in Version:4.0.1

Issue Type:Cross Site Scripting (XSS)

Original Code:Found Here

Description

Upon 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>';?>

Haircut –Cross Site Scripting

Details

Affected Software:WP-Slimbox 2

Fixed in Version:1.0.1

Issue Type:Cross Site Scripting (XSS)

Original Code:Found Here

Description

A 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>

Australia –Cross Site Scripting

Details

Affected Software:FreePBX

Fixed in Version:2.5

Issue Type:Cross Site Scripting (XSS)

Original Code:Found Here

Description

One of the more weird XSS vulnerabilities I’ve seen :)
Here we see FreePBX using a potion of a log file in their HTML markup. Specifically,the PHP code uses the system() API to execute a command on the PBX system. The results of the system() command are printed to the HTML markup. In this case,FreePBX runs a tail on a log file displaying some of the entries contained within that log file. Looking at the vulnerable code sample,it is impossible to understand exactly what is contained in these log files,however it appears that HTML can exist in the log entries due to the sed command being run on the log file output:

| sed -e “s/$/<br>/”

The developers realized that the log files could contain other dangerous HTML elements and modified their sed command to try and filter those elements out. Maybe a better approach would be to use a proper encoding API? Luckily,it doesn’t seem like the attacker can control anything passed to system(),otherwise this would have been a code execution bug as opposed to just an XSS!

Developers Solution

<?php$display = $_REQUEST['display'];$type = isset($_REQUEST['type']) ? $_REQUEST['type']:'tool';$action = isset($_REQUEST['action']) ? $_REQUEST['action']:'';?></div><div class="content"><?phpswitch($action){case 'showlog':?><h2><?php echo sprintf(_('%s - last 2000 lines'),$amp_conf['ASTLOGDIR']."/full") ?></h2><a href="config.php?<?php echo "display=$display&type=$type&action=showlog"?>"><?php echo _("Redisplay Asterisk Full debug log (last 2000 lines)") ?></a><br><hr><br><?php-echo system ('tail --line=2000 '.$amp_conf['ASTLOGDIR'].'/full | sed -e "s/$/<br>/"');+system ('tail --line=2000 '.$amp_conf['ASTLOGDIR'].'/full | sed -e "s,<,\&lt;,g;s,>,\&gt;,g;s/$/<br>/"');break;default:echo "<h2>"._("Asterisk Log Files")."</h2>";?><a href="config.php?<?php echo "display=$display&type=$type&action=showlog"?>"><?php echo _("Display Asterisk Full debug log (last 2000 lines)") ?></a><br><br><br><br><br><br><br><br><br><br><br><br><br><?php  break}?></div>

Covet –Cross Site Scripting

Details

Affected Software:Subscribe to Comments Plugin

Fixed in Version:2.1

Issue Type:Cross Site Scripting (XSS)

Original Code:Found Here

Description

A 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' &&current_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 ?>

Vulnerability –Cross Site Scripting

Details

Affected Software:WordPress (Core)

Fixed in Version:2.3

Issue Type:Cross Site Scripting (XSS)

Original Code:Found Here

Description

Interesting 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"/>&nbsp;&nbsp;&nbsp;&nbsp;<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&#8217;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'));?>