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。
漏洞证明
然后注销账户 重新登录一次。
修复方案
检测是否合法。