在CTF与渗透实战中总是会出现文件上传的问题,而文件上传有两大决定性的因素
文件上传不一定是为了getshell,而上传的文件种类也是千奇百怪,目前来说,文件上传比较多的是pdf、压缩包。
这个示例题目是2023年的CISCN的web题里的一道,原理就是服务器错误的解析了压缩包导致的
题目地址
https://github.com/NUDTTAN91/ciscn_2023_unzip
题目代码为
error_reporting(0);
highlight_file(__FILE__);
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if (finfo_file($finfo, $_FILES["file"]["tmp_name"]) === 'application/zip'){
exec('cd /tmp && unzip -o ' . $_FILES["file"]["tmp_name"]);
};
可以看到上传文件的判断非常的奇怪,接下来我会对每一句代码进行解析(我打算写一个代码审计的系列,从现在开始)
总体分析:这个PHP脚本允许用户上传一个ZIP文件,并在验证其MIME类型为’application/zip’后将其解压缩到服务器的/tmp目录。同时,脚本还会显示其自身的源代码。
当解压操作可以覆盖上一次解压文件就可以造成任意文件上传漏洞。此题上传的文件被保存到了/tmp目录下,而且没有require和include这种文件包含的点,因此无法直接执行上传文件里面的代码,因此这里考虑利用软连接,进行目录穿越,将文件上传到任意目录
软连接是linux中一个常用命令, 它的功能是为某一个
文件在另外一个位置建立一个同步的链接。软连接类似
与c语言中的指针,传递的是文件的地址; 更形象一
些,软连接类似于WINDOWS系统中的快捷方式。 例
如,在a文件夹下存在一个文件hello,如果在b文件夹
下也需要访问hello文件,那么一个做法就是把hello复
制到b文件夹下,另一个做法就是在b文件夹下建立
hello的软连接。通过软连接,就不需要复制文件了,相
当于文件只有一份,但在两个文件夹下都可以访问。
有文件上传接口,但是上传文件的目录不能确定
可以上传zip文件并且会将文件解压到上传目录下
可以getshell的文件可以绕过waf成功上传
创建一个指向/var/www/html目录的软链接,因为html目录下是web环境(大部分情况是),为了后续可以getshell
使用ll查看文件夹的真实指向
可以看到这是一个链接文件,并且指向/var/www/html
zip --symlinks,是在zip压缩文件中,包含符号链接
本身,而不是它们指向的实际文件或目录,也就是说当
解压缩这个zip文件,将得到符号链接,而不是它们指向
的实际文件
打包到到1.zip,对link文件进行压缩
接下来创建的这个目录要上面创建的文件夹名字相同,所以我们这里先将之前创建的文件夹删除再创建
打包成2.zip
现在我们将1.zip 和2.zip 依次 上传,压缩包会被解压,当我们上传第二个压缩包时会覆盖上一个link目录,但是
link目录软链接指向/var/www/html解压的时候会把shell.php放在/var/www/html下,此时我们就达到了getsehll的目的。
会出现源码页面,不用管,接着传2.zip进行getshell
在现实中或awd、dawd中大家都需要进行patch(修复)操作,所以面试基本上都会把重点放在应急响应这里,这里我们也尝试修复这个文件上传漏洞
仅允许上传ZIP文件,使用ZipArchive类解压缩文件,为解压缩的文件添加随机前缀,并限制解压缩的目录
error_reporting(0);
// 限制允许上传的文件类型
$allowed_extensions = array('zip');
// 获取上传文件的扩展名
$uploaded_file_extension = pathinfo($_FILES["file"]["name"], PATHINFO_EXTENSION);
// 检查文件扩展名是否在允许的列表中
if (in_array(strtolower($uploaded_file_extension), $allowed_extensions)) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if (finfo_file($finfo, $_FILES["file"]["tmp_name"]) === 'application/zip') {
// 使用PHP的ZipArchive类来解压缩文件
$zip = new ZipArchive;
if ($zip->open($_FILES["file"]["tmp_name"]) === TRUE) {
// 为解压缩的文件添加随机前缀
$random_prefix = uniqid();
$extract_to_directory = "/tmp/" . $random_prefix;
// 创建解压缩目录
if (!is_dir($extract_to_directory)) {
mkdir($extract_to_directory, 0755, true);
}
// 解压缩文件到指定目录
$zip->extractTo($extract_to_directory);
$zip->close();
echo "Unzip successful!";
} else {
echo "Failed to open the zip file.";
}
} else {
echo "Invalid file type. Please upload a zip file.";
}
} else {
echo "File extension not allowed. Please upload a zip file.";
}
?>