wordpress主题dmeng存在XSS跨站

  • A+
所属分类:漏洞时代
摘要

由于某编辑辛苦弄了一套主题。然后被某墨盒测试出来一个XSS漏洞。然后就丢给我。让我看看这个漏洞能不能修复。
首先找到问题所在,是在发送私信的地方存在对参数每一过滤好造成的。读取的时候也是没有处理。直接给打印出来了。
首先找到问题所在文件
themes/dmeng2.0/author.php 86行左右

由于某编辑辛苦弄了一套主题。然后被某墨盒测试出来一个XSS漏洞。然后就丢给我。让我看看这个漏洞能不能修复。
首先找到问题所在,是在发送私信的地方存在对参数每一过滤好造成的。读取的时候也是没有处理。直接给打印出来了。
首先找到问题所在文件
themes/dmeng2.0/author.php 86行左右

 //~ 私信start  $get_pm = isset($_POST['pm']) ? trim($_POST['pm']) : '';           //获取传递来的pm赋值给$get_pm   if( isset($_POST['pmNonce']) && $get_pm && is_user_logged_in() ){  //检测验证码,是否登录,以及PM是否存在   if ( ! wp_verify_nonce( $_POST['pmNonce'], 'pm-nonce' ) ) {    $message = __('安全认证失败,请重试!','dmeng');   }else{    $pm_title = json_encode(array(     'pm' => $curauth->ID,     'from' => $current_user->ID    ));    if( add_dmeng_message( $curauth->ID, 'unrepm', '', $pm_title, $get_pm ) ){  //直接发送$get_pm     //~ 发邮件通知     if(is_email($curauth->dmeng_verify_email)){      $m_headline = sprintf(__('%1$s给你发来了私信','dmeng'), $current_user->display_name);      $m_content = '<h3>'.sprintf( __('%1$s,你好!','dmeng'), $curauth->display_name ).'</h3><p>'.sprintf( __('%1$s给你发来了<a href="%2$s" target="_blank">私信</a>,快去看看吧:<br> %3$s','dmeng'), $current_user->display_name, htmlspecialchars( add_query_arg('tab', 'message', get_author_posts_url( $curauth->ID )) ), $get_pm).'</p>'; //直接显示$get_pm      dmeng_send_email( $curauth->dmeng_verify_email, $m_headline, $m_content );     }     $message = __('发送成功!','dmeng');    }   }  }

可以看出来$get_pm在整个流程中并没有被转码或者其他的操作。直接带入数据库并显示出来了。所以,发信的内容为我们精心构造的XSS代码。就再你查看私信的时候就触发了。如果发信给管理员呢..
wordpress主题dmeng存在XSS跨站
我们继续看下add_dmeng_message函数
在文件dmeng2.0/inc/message.php

function add_dmeng_message( $uid=0, $type='', $date='', $title='', $content='' ){   $uid = intval($uid);  $title = sanitize_text_field($title);    if( !$uid || empty($title) ) return;   $type = $type ? sanitize_text_field($type) : 'unread';  $date = $date ? $date : current_time('mysql');  $content = htmlspecialchars($content); //对content进行了htmlspecialchars处理。不过后面却使用了htmlspecialchars进行了还原。    global $wpdb;  $table_name = $wpdb->prefix . 'dmeng_message';   if($wpdb->query( "INSERT INTO $table_name (user_id,msg_type,msg_date,msg_title,msg_content) VALUES ('$uid', '$type', '$date', '$title', '$content')" ))   return 1;    return 0;   }

在后台也有一个显示的

if($tab=='repm'){     $table_name = $wpdb->prefix . 'dmeng_message';    $sql_where = "FROM $table_name WHERE msg_type='repm' OR msg_type='unrepm'";    $count =  $wpdb->get_var( "SELECT count(msg_id) ".$sql_where );    $messages = $wpdb->get_results( "SELECT user_id,msg_date,msg_title,msg_content ".$sql_where." ORDER BY msg_id DESC LIMIT $offset,$limit" );  if( $messages ){   $output = '';   foreach( $messages as $message ){    $repm = json_decode($message->msg_title);    $output .= '<p>'.sprintf( __('%1$s %2$s 给 %3$s 发私信:%4$s', 'dmeng'),          $message->msg_date,          '<a href="'.dmeng_get_user_url('message', $repm->from).'" target="_blank">'.get_the_author_meta('display_name', $repm->from).'</a>',          '<a href="'.dmeng_get_user_url('message', $repm->pm).'" target="_blank">'.get_the_author_meta('display_name', $repm->pm).'</a>',          htmlspecialchars_decode($message->msg_content)). //这里的呢~恩。入库使用的是htmlspecialchars,这里还原了。不是啥都没有做嘛~          '</p>';   }  }   }

后来看到作者在群里面说修改的脚本
97行

   if( add_dmeng_message( $curauth->ID, 'unrepm', '', $pm_title, strip_tags($get_pm, '<a><br><p>') ) ){

自己本地测试了下

<?php $get_pm='<a href=ja&#118;asc&#114;ipt:ale&#114t(document.cookie)>aaa</a>';   echo strip_tags($get_pm, '<a><br><p>'); ?>

wordpress主题dmeng存在XSS跨站
针对作者自己写的过滤,小小的突破了下。本地测试效果还是不错的
wordpress主题dmeng存在XSS跨站
感觉这样的修复,实在是。简单的写了一个过滤$get_pm = htmlspecialchars(addslashes(stripSlashes($get_pm)));

 //~ 私信start  $get_pm = isset($_POST['pm']) ? trim($_POST['pm']) : '';           //获取传递来的pm赋值给$get_pm   $get_pm = htmlspecialchars(addslashes(stripSlashes($get_pm)));     //对$get_pm在进入数据之前进行处理  if( isset($_POST['pmNonce']) && $get_pm && is_user_logged_in() ){  //检测验证码,是否登录,以及PM是否存在   if ( ! wp_verify_nonce( $_POST['pmNonce'], 'pm-nonce' ) ) {    $message = __('安全认证失败,请重试!','dmeng');   }else{    $pm_title = json_encode(array(     'pm' => $curauth->ID,     'from' => $current_user->ID    ));    if( add_dmeng_message( $curauth->ID, 'unrepm', '', $pm_title, $get_pm ) ){  //直接发送$get_pm     //~ 发邮件通知     if(is_email($curauth->dmeng_verify_email)){      $m_headline = sprintf(__('%1$s给你发来了私信','dmeng'), $current_user->display_name);      $m_content = '<h3>'.sprintf( __('%1$s,你好!','dmeng'), $curauth->display_name ).'</h3><p>'.sprintf( __('%1$s给你发来了<a href="%2$s" target="_blank">私信</a>,快去看看吧:<br> %3$s','dmeng'), $current_user->display_name, htmlspecialchars( add_query_arg('tab', 'message', get_author_posts_url( $curauth->ID )) ), $get_pm).'</p>'; //直接显示$get_pm      dmeng_send_email( $curauth->dmeng_verify_email, $m_headline, $m_content );     }     $message = __('发送成功!','dmeng');    }   }  }

这里的过滤呐。还是不变化吧

if($tab=='repm'){     $table_name = $wpdb->prefix . 'dmeng_message';    $sql_where = "FROM $table_name WHERE msg_type='repm' OR msg_type='unrepm'";    $count =  $wpdb->get_var( "SELECT count(msg_id) ".$sql_where );    $messages = $wpdb->get_results( "SELECT user_id,msg_date,msg_title,msg_content ".$sql_where." ORDER BY msg_id DESC LIMIT $offset,$limit" );  if( $messages ){   $output = '';   foreach( $messages as $message ){    $repm = json_decode($message->msg_title);    $output .= '<p>'.sprintf( __('%1$s %2$s 给 %3$s 发私信:%4$s', 'dmeng'),          $message->msg_date,          '<a href="'.dmeng_get_user_url('message', $repm->from).'" target="_blank">'.get_the_author_meta('display_name', $repm->from).'</a>',          '<a href="'.dmeng_get_user_url('message', $repm->pm).'" target="_blank">'.get_the_author_meta('display_name', $repm->pm).'</a>',          htmlspecialchars(addslashes(stripSlashes(htmlspecialchars_decode($message->msg_content)))). //这里的呢~恩。入库使用的是htmlspecialchars,这里还原了。不是啥都没有做嘛~于是继续给他xxx          '</p>';   }  }   }

最新号外:后来作者使用了wp自带的过滤函数wp_strip_all_tags来过滤了。详情关注
wp-includes/formatting.php

function wp_strip_all_tags($string, $remove_breaks = false) {  $string = preg_replace( '@<(script|style)[^>]*?>.*?<///1>@si', '', $string );  $string = strip_tags($string);   if ( $remove_breaks )   $string = preg_replace('/[/r/n/t ]+/', ' ', $string);   return trim( $string ); }

因此,当然,我个人比较喜欢记录出来全部的东西。这里是采用了替换的方式。个人喜好不一样而已。这里告一个段落...

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: