原始漏洞
漏洞原理:我们上传一个zip的压缩包,它会解压然后删除其中不是.jpg .gig .png的文件
function check_dir($dir):这是一个PHP函数的定义,它接受一个参数 $dir,代表要检查的目录路径。
dir);:使用 opendir 函数打开指定目录,并将返回的目录句柄赋值给变量 $handle。
while ((handle)) !== false):使用 readdir 函数循环读取目录中的文件,每次循环将文件名赋给变量 $f。
if (!in_array($f, array('.', '..'))) { ... }:在每次循环中,检查当前文件名是否为当前目录(.)或父目录(..),如果不是,则执行下面的操作。
f, '.'), 1));:使用 strrchr 函数找到文件名中的最后一个 .,然后使用 substr 截取文件扩展名,并用 strtolower 将其转换为小写。
if (!in_array(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。
在aaaaaaa中存在以上文件,生成aaaaaaa.zip压缩包,然后对压缩包进行制作
保证最后解压要报错
将制作好的zip上传,同时使用burp连续访问
手动多次上传zip保证刚解压出来的111.php被访问到
发现上传成功了,生成了随机名文件
查看一句话发现已经生成
连接蚁剑
造成以上漏洞的原因就是,解压完成的文件没有进行递归删除,导致包含其中的一句话绕过了。