目录
本文通过《upload-labs通关笔记-第17关二次渲染之gif格式图片》系列,制作gif图片马来绕过二次渲染进行渗透实战。在文件上传安全中,当攻击者上传一个看似正常的图片,但其中隐藏了恶意代码。当服务器对图片进行重新二次渲染(如调整大小、压缩等操作)并重新生成新的图片文件,可以消除文件中可能隐藏的恶意代码,可以防范文件上传风险。
一、二次渲染
文件上传的二次渲染是预防利用图片马攻击的文件上传风险方法,其原理是通过Web服务器端的相关函数对用户上传的图片进行重新二次渲染生成新的突破,破坏掉原始图片文件中可能隐藏的恶意代码,从而有效防范了图片马等常见文件上传攻击手段。二次渲染的处理逻辑分为3个步骤,具体如下所示。
-
接收用户上传的图片
-
使用Web服务器端图像处理库对原始图片二次渲染重新生成文件
-
只保存新生成的文件,丢弃原始上传的图片文件
二次渲染处理图片具有如下优点。
- 清除恶意代码:二次渲染利用图像处理库对上传的图片进行重新生成,能够有效识别并丢弃图片中隐藏的非图像数据,如恶意脚本、病毒代码等。这使得攻击者难以通过在图片中嵌入恶意代码来执行攻击,大大降低了服务器遭受攻击的风险。
- 规范图像格式:在二次渲染过程中,图片会被强制转换为标准的图像格式,纠正可能存在的格式错误或异常。这有助于防止攻击者利用畸形图像文件来绕过文件类型检查或触发服务器端的渗透攻击。
二、代码审计
进入upload-labs靶场的第17关二次渲染关卡,查看源码分析其功能。先校验上传文件的扩展名及 MIME 类型,仅开放 JPEG、PNG、GIF 三种图片格式上传权限。随后通过imagecreatefromjpeg /png /gif函数启动二次渲染流程,在此过程中会破坏隐藏在文件里的 PHP 代码、恶意脚本等使之被修改,重新生成纯净的图片文件。系统仅保留经渲染后的 “干净” 图片,同时删除用户原始上传文件。本关卡只针对gif类型的图片进行二次渲染绕过,故而对gif格式的源码进行详细注释,具体如下所示。
详细的注释如下所示。
<?php
// 检查上传文件的扩展名是否为 "gif" 并且文件的 MIME 类型是否为 "image/gif"
// $fileext 代表上传文件的扩展名,$filetype 代表文件的 MIME 类型
if(($fileext == "gif") && ($filetype=="image/gif")){
// 尝试将上传的临时文件从临时存储路径移动到目标存储路径
// $tmpname 是上传文件在服务器临时存储的路径,$target_path 是目标存储路径
if(move_uploaded_file($tmpname,$target_path)){
// 使用上传的图片生成新的图片
// imagecreatefromgif 是 PHP 的 GD 库函数,用于从 GIF 格式的图片文件创建一个新的图像资源
// $target_path 为存储图片的目标路径
$im = imagecreatefromgif($target_path);
// 检查是否成功创建图像资源
if($im == false){
// 如果创建失败,说明该文件可能不是合法的 GIF 格式图片
// 将提示信息 $msg 设置为相应内容
$msg = "该文件不是gif格式的图片!";
// 尝试删除之前移动到目标路径的文件
// @ 符号用于抑制可能出现的警告信息
@unlink($target_path);
}else{
// 如果成功创建图像资源,为新生成的图片指定一个文件名
// srand(time()) 用于初始化随机数生成器,以当前时间作为种子,确保每次生成的随机数不同
srand(time());
// 生成一个随机整数,并将其转换为字符串,作为新图片的文件名
$newfilename = strval(rand()).".gif";
// 拼接新图片在目标目录中的完整路径
// UPLOAD_PATH 是预定义的上传文件保存目录
$img_path = UPLOAD_PATH.'/'.$newfilename;
// 将之前创建的图像资源以 GIF 格式保存到指定的新路径
// imagegif 是 PHP 的 GD 库函数,用于将图像资源保存为 GIF 格式的文件
imagegif($im,$img_path);
// 尝试删除之前移动到目标路径的原始上传文件
@unlink($target_path);
// 将 $is_upload 标记为 true,表示文件上传并处理成功
$is_upload = true;
}
} else {
// 如果文件移动失败,将提示信息 $msg 设置为上传出错
$msg = "上传出错!";
}
}else{
// 如果上传文件的扩展名不是 "gif" 或者 MIME 类型不是 "image/gif"
// 将提示信息 $msg 设置为只允许上传特定后缀的图片文件
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
?>
通过代码审计可知本关卡采用文件后缀、MIME法且通过imagecreatefromgif函数进行二次渲染处理,具体分析如下。
(1)双重验证
- 检查文件扩展名(匹配.gif)
- 验证MIME类型(匹配image/gif)
(2)二次渲染
- 使用imagecreatefromgif()和imagegif()对图像进行二次处理
- 这种方法能有效消除gif文件中可能隐藏的恶意代码
- 生成全新的图像文件,不保留原始文件的元数据
(3)随机文件名
- 使用time()和rand()生成不易被猜测的随机文件名
- 防止文件名冲突和预测攻击
三、渗透思路
打开靶场的第17关卡,将16关生成的3个图片马传入后,发现图片马中的恶意代码均被破坏,同样的渗透方法无法成功。如何保证将恶意代码插入到图片中后,这个图片传到服务器后在二次渲染后仍然能保留呢?也就是说如何保证既能保证图片被处理,同时恶意代码也不被破坏呢。文件上传中的二次渲染的过程可能会移除或更改文件的某些内容部分。为了成功绕过二次渲染,需要理解图片文件的结构以及服务器处理图片的方式。那么我们根据3种图片格式(gif,png和jpg)分别使用不同的方法渗透,具体如下所示。
- GIF绕过:原始图片0,使用copy命令生成包含PHP代码的GIF图片马1,上传后GIF图片马1后,网站对图片马1进行二次渲染生成图片2,使用十六进制编辑器比较上传前后的文件差异(图片马1和图片2),找到未改变的部分将PHP代码插入到图片马1的该位置上,再次上传修改后的图片马1即可成功。
- PNG绕过:PNG图片由多个数据块组成,涉及到校验和的修改,容易破坏图片结构,可以通过特定脚本生成绕过渲染的图片马
- JPG绕过:由于JPG图片容易损坏,需要选择合适的图片进行操作。可以使用专门的脚本来处理JPG图片,将PHP代码注入到图片中,然后上传。如果处理成功,上传的图片将包含未被二次渲染移除的PHP代码
本小节针对gif格式进行处理,也就是通过十六进制编辑器处理即可。
四、实战渗透
1、制作gif图片马
(1)构建脚本命名info.php
<?php
phpinfo();
?>
(2)准备好GIF类型的图片test.gif,本关卡复用16关的图片
进入到图片文件test.gif,test.jpg,test.png和脚本文件info.php所在的目录
(3)制作图片马
然后执行以下命令即可生成test16.jpg,test16.gif,test16.png三种类型的图片马。
copy /b test.gif + info.php test17.gif
如下所示,test17.gif的尾部包含脚本内容。
2、上传图片马并下载
上传图片马test17.gif并获取图片的地址,如下所示。
右键图像区域,选择“将图像另存为”,将图片保存,使用Editor打开刚保存好的gif文件(30962.gif),用16进制编辑器打开30962.gif,如下所示,图片马已经消失。
3、对比渲染前后图片
点击“工具”—>“比较文件”,把刚保存好的30962.gif和合成的原始图片马test17.gif进比较
比较后,点击“匹配”,蓝色部分就是没有被渲染的地方
在原始图片马数据test17.gif没有变化的地方插入如下code并保存。
<?php
phpinfo();
?>
切记需要在原始图片马test17.gif上修改,不是从靶场上下载下来的图片上修改。
4、重新上传修改后的图片马
如下所示上传test17.gif成功。
5、获取上传图片马的URL
http://127.0.0.1/upload-labs/upload/31726.gif
6、利用文件包含访问图片马
文件包含渗透主页如下所示。
http://127.0.0.1/upload-labs/include.php
构造文件包含访问图片马的URL,具体如下所示。
http://127.0.0.1/upload-labs/include.php?file=upload/31726.gif
访问脚本,鼠标滚轮向下滑获取到php版本信息,具体如下所示。