# 代码审计之逻辑上传漏洞挖掘

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2014-05-14T03:23:00Z
Canonical: https://wepostx.com/topics/464

> 乌云历史资料归档
>
> **原始作者：** felixk3y
> **原始编号：** superkieran-wooyundrops:248
> **原始发布时间：** 2014-05-14 11:23
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

## 0x00 前言

话说一个人的快乐,两个人分享就成为两份快乐,这个我看未必吧,倘若分享与被分享的两者之间是情敌关系,而分享者快乐的原因恰好是… 哈哈,不说了,都懂的;
BUT, 倘若一个技巧被分享出来,那么受益的人我坚信肯定远远不只两个,所以我们更应该学会的是--分享!
Today,简单说说漏洞挖掘中由逻辑缺陷造成的文件上传漏洞。
Tips:传统的MIME验证、客户端js验证、黑名单验证、解析漏洞等这些都比较简单,不在我们讨论的范围内。

## 0x01 程序员对某些常用函数的错误认识

这些函数有:empty()、isset()、strpos()、rename()等，如下面的代码(摘自用友ICC软件):

```text
#!php if($operateId == 1){ $date = date("Ymd"); $dest = $CONFIG->basePath."data/files/".$date."/"; $COMMON->createDir($dest); //if (!is_dir($dest)) mkdir($dest, 0777); $nameExt = strtolower($COMMON->getFileExtName($_FILES['Filedata']['name'])); $allowedType = array('jpg', 'gif', 'bmp', 'png', 'jpeg'); if(!in_array($nameExt, $allowedType)){ $msg = 0; } if(empty($msg)){ $filename = getmicrotime().'.'.$nameExt; $file_url = urlencode($CONFIG->baseUrl.'data/files/'.$date."/".$filename); $filename = $dest.$filename; if(empty($_FILES['Filedata']['error'])){ move_uploaded_file($_FILES['Filedata']['tmp_name'],$filename); } if (file_exists($filename)){ //$msg = 1; $msg = $file_url; @chmod($filename, 0444); }else{ $msg = 0; } } $outMsg = "fileUrl=".$msg; $_SESSION["eoutmsg"] = $outMsg; exit; }
```
我们来看上面的这段代码,要想文件成功的上传, if(empty($msg)) 必须为True才能进入if的分支,接下来我们来看empty函数何时返回True,看看PHP Manual怎么说,如图

![原文图片](/media/2016/06/6bde18a197e5890b1f115d06918c2369)

很明显，""、0、"0"、NULL、FALSE、array()、var $var; 以及没有任何属性的对象都将被认为是空的，如果var为空，则返回True。 非常好，接下来我们往回看，有这样的几行代码

```text
#!php $allowedType = array('jpg', 'gif', 'bmp', 'png', 'jpeg'); if(!in_array($nameExt, $allowedType)){ $msg = 0; }
```
看见没有,即使我们上传类似shell.php的文件，虽然程序的安全检查把$msg赋值为0,经empty($msg)后，仍然返回True，于是我们利用这个逻辑缺陷即可成功的上传shell.php。
具体详见漏洞案例：
[WooYun: 用友ICC网站客服系统远程代码执行漏洞](http://www.wooyun.org/bugs/wooyun-2012-04685)

## 0x02 程序员对某些常用函数的错误使用

这些函数有iconv()、copy()等,如下面的这段代码(摘自SiteStar)

```text
#!php public function img_create(){ $file_info =& ParamHolder::get('img_name', array(), PS_FILES); if($file_info['error'] > 0){ Notice::set('mod_marquee/msg', __('Invalid post file data!')); Content::redirect(Html::uriquery('mod_tool', 'upload_img')); } if(!preg_match('/\.('.PIC_ALLOW_EXT.')$/i', $file_info["name"])){ Notice::set('mod_marquee/msg', __('File type error!')); Content::redirect(Html::uriquery('mod_marquee', 'upload_img')); } if(file_exists(ROOT.'/upload/image/'.$file_info["name"])){ $file_info["name"] = Toolkit::randomStr(8).strrchr($file_info["name"],"."); } if(!$this->_savelinkimg($file_info)){ Notice::set('mod_marquee/msg', __('Link image upload failed!')); Content::redirect(Html::uriquery('mod_marquee', 'upload_img')); } //... } private function _savelinkimg($struct_file){ $struct_file['name'] = iconv("UTF-8", "gb2312", $struct_file['name']); move_uploaded_file($struct_file['tmp_name'], ROOT.'/upload/image/'.$struct_file['name']); return ParamParser::fire_virus(ROOT.'/upload/image/'.$struct_file['name']); }
```
我们再来看看这段代码, img_create()函数的逻辑非常严密，安全检查做的很到位。然而问题出在了_savelinkimg()函数，即在保存文件的前面程序员错误的使用了iconv()函数,并且文件名经过了此函数,为什么是错用了呢?因为啊 iconv函数在转码过程中，可能存在字符串截断的问题：
在iconv转码的过程中,utf->gb2312(其他部分编码之间转换同样存在这个问题)会导致字符串被截断，如：$filename="shell.php(hex).jpg";(hex为0x80-0x99)，经过iconv转码后会变成$filename="shell.php ";
所以，经过iconv 后$struct_file['name'])为shell.php，于是我们利用这个逻辑缺陷可以成功的上传shell.php(前提是上传的文件名为shell.php{%80-%99}.jpg)。
具体详见漏洞案例：
[WooYun: 建站之星模糊测试实战之任意文件上传漏洞](http://www.wooyun.org/bugs/wooyun-2014-048293)

## 0x03 历史经典漏洞再次爆发

条件竞争漏洞,这类历史经典漏洞在逐渐淡出人们视线的时候,再次爆发..
接着看下面这段代码(摘自某VPN系统)

```text
#!php <? if($_POST['realfile']){ copy($_POST['realfile'],$_POST['path']); } $file = mb_convert_encoding($_POST[file],"GBK","UTF-8"); header("Pragma:"); header("Cache-Control:"); header("Content-type:application/octet-stream"); header("Content-Length:".filesize($_POST[path])); header("Content-Disposition:attachment;filename=\"$file\""); readfile($_POST[path]); if($_POST['realfile']){ unlink($_POST["path"]); } ?>
```
上述代码的逻辑表面上看起来是这样的(对于攻击者来说)：
利用copy函数，将realfile生成shell.php-→删除掉shell.php
这样初看起来没办法利用，但是仔细一想, 这段代码其实是存在逻辑问题的，所以我们可以利用这个逻辑缺陷达到GetShell的目的。
具体利用方法：
copy成temp.php-->不断访问temp.php->temp.php生成shell.php->删除temp.php
具体详见漏洞案例：
[WooYun: 国内外多家vpn设备厂商批量漏洞(续集一)](http://www.wooyun.org/bugs/wooyun-2014-048202)
[WooYun: PHPCMS前台设计缺陷导致任意代码执行](http://www.wooyun.org/bugs/wooyun-2014-049794)

## Replies
