WooYun-2014-68853:Phpyun注入漏洞二

漏洞作者: ′雨。认证白帽子

来源:http://www.wooyun.org/bugs/wooyun-2014-068853

简要描述

刚在官网下的。

前台注入。 可以直接出管理员的帐号和密码。 无视360webscan。

详细说明

本来以为挖不到了 无聊翻翻文件看看。 翻到了上次那个注入的文件。

model/register.class.php

function regsave_action(){

        $_POST=$this->post_trim($_POST);

        $_POST['username']=iconv("utf-8","gbk",$_POST['username']);

        $_POST['unit_name']=iconv("utf-8","gbk",$_POST['unit_name']);

省略点

ip = $this->obj->fun_ip_get();

            $data['username']=$_POST['username'];

            $data['password']=$pass;

            $data['moblie']=$_POST['moblie'];

            $data['email']=$_POST['email'];

            $data['usertype']=$_POST['usertype'];

            $data['status']=$satus;

            $data['salt']=$salt;

            $data['reg_date']=time();

            $data['reg_ip']=$ip;

            $data['qqid']=$_SESSION['qq']['openid'];

            $data['sinaid']=$_SESSION['sinaid'];

            $userid=$this->obj->insert_into("member",$data);

主要看到这里ip = $this->obj->fun_ip_get();

一开始就是想的会不会有很古老的xff洞? 后面想了想不太可能把 phpyun这程序目测还算不错的。

可是还是看了看这函数。

然后看到两个文件里面都声明了这函数

1 在 /include/public.function.php 中

function fun_ip_get() {

    if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown")) {

        $ip = getenv("HTTP_CLIENT_IP");

    } else

        if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown")) {

            $ip = getenv("HTTP_X_FORWARDED_FOR");

        } else

            if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown")) {

                $ip = getenv("REMOTE_ADDR");

            } else

                if (isset ($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown")) {

                    $ip = $_SERVER['REMOTE_ADDR'];

                } else {

                    $ip = "unknown";

                }

     $preg="/\A((([0-9]?[0-9])|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\.){3}(([0-9]?[0-9])|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))\Z/";

     if(preg_match($preg,$ip)){

        return ($ip);

     }

这个文件里面的这函数验证了。

2 /model/class/action.class.php

function fun_ip_get() {

        if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown")) {

            $ip = getenv("HTTP_CLIENT_IP");

        } else

            if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown")) {

                $ip = getenv("HTTP_X_FORWARDED_FOR");

            } else

                if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown")) {

                    $ip = getenv("REMOTE_ADDR");

                } else

                    if (isset ($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown")) {

                        $ip = $_SERVER['REMOTE_ADDR'];

                    } else {

                        $ip = "unknown";

                    }

        return ($ip);

而这/model/class/action.class.php文件里面的却没验证ip是否合法。。

而刚才调用的函数 就是调用的这文件里面的。。。。。

碉堡了。。

继续在model/register.class.php里面看。

$ip = $this->obj->fun_ip_get();

            $data['username']=$_POST['username'];

            $data['password']=$pass;

            $data['moblie']=$_POST['moblie'];

            $data['email']=$_POST['email'];

            $data['usertype']=$_POST['usertype'];

            $data['status']=$satus;

            $data['salt']=$salt;

            $data['reg_date']=time();

            $data['reg_ip']=$ip;

            $data['qqid']=$_SESSION['qq']['openid'];

            $data['sinaid']=$_SESSION['sinaid'];

            $userid=$this->obj->insert_into("member",$data);

然后就带入到了insert当中

function insert_into($table,$data=array()){

        $value="";

        $FieldSQL = "SELECT `COLUMN_NAME` FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$this->def.$table."'";

        $Fquery = $this->db->query($FieldSQL);

        while($Frow=$this->db->fetch_array($Fquery)){

            $Freturn[]=$Frow;

        }

        if(is_array($Freturn))

        {

            foreach($Freturn as $Fkey=>$Fval)

            {

                $fields[] =  $Fval['COLUMN_NAME'];

            }

            if(is_array($data)){

                foreach($data as $key=>$v){

                    if(in_array($key,$fields))

                    {

                        $v = $this->FilterStr($v);

                        $value[]="`".$key."`='".mysql_real_escape_string($v)."'";

可是在这里 mysql_real_escape_string 转义了 没办法用。

在找找其他哪个文件调用了这函数。

在model/login.class.php中

function loginsave_action()

    { 

        $username=iconv("utf-8","gbk",$_POST['username']);

        if($_COOKIE['uid']!=""&&$_COOKIE['username']!="")

        {

            $this->ajaxlogin($_POST['comid'],"您已经登陆了,您不是个人用户!");

            echo "您已经登录了!";die;

        }

        if($_POST['path']!="index")

        {

            if(strstr($this->config["code_web"],'前台登陆'))

            {

                if(md5($_POST["authcode"])!=$_SESSION["authcode"])

                {

                    $this->ajaxlogin($_POST['comid'],"验证码错误!");

                    echo "验证码错误!";die;

                }

            }

        }

省略一点

$time = time();

                            $ip = $this->obj->fun_ip_get();

                            $this->obj->DB_update_all("member","`login_ip`='$ip',`login_date`='$time',`login_hits`=`login_hits`+1","`uid`='".$user['uid']."'");

                            $this->unset_cookie();

                            $this->add_cookie($user['uid'],$user['username'],$user['salt'],$user['email'],$user['password'],$_POST['usertype']);

然后在这里

$this->obj->fun_ip_get(); 再次调用了这函数,

进入DB_update_all

function DB_update_all($tablename, $value, $where = 1){

        $SQL = "UPDATE `" . $this->def . $tablename . "` SET $value WHERE $where";

         $this->db->query("set sql_mode=''");

        $return=$this->db->query($SQL);

        return $return;

    }

这函数里没有转义 所以可以直接来注入了。

而且由于可控的是在set位 所以我们可以想update 哪个column 就update哪个column。

这里我们update一下email.

再来看一下360webscan。

foreach($_GET as $key=>$value) {

      webscan_StopAttack($key,$value,$getfilter,"GET");

    }

  }

  if ($webscan_post) {

    foreach($_POST as $key=>$value) {

      webscan_StopAttack($key,$value,$postfilter,"POST");

    }

  }

  if ($webscan_cookie) {

    foreach($_COOKIE as $key=>$value) {

      webscan_StopAttack($key,$value,$cookiefilter,"COOKIE");

    }

  }

  if ($webscan_referre) {

    foreach($webscan_referer as $key=>$value) {

      webscan_StopAttack($key,$value,$postfilter,"REFERRER");

    }

  }

}

检测了get post cookie

server里面只检测了referer

所以 xff的话 不用管360。

漏洞证明

然后注销账户 重新登录一次。

修复方案

检测是否合法。