upload-labs通关笔记-第14关 文件上传之文件头绕过(图片马)
目录
一、图片特点
二、源码分析
1、代码审计
2、渗透思路
三、制作简易图片马
1、制作脚本test14.php
2、制作图片马
(1)制作jpg图片马
(2)制作gif图片马
(3)制作png图片马
四、文件包含与图片马
五、渗透实战
1、jpg渗透
(1)上传jpg图片马
(2)获取图片URL地址
(3)打开文件包含网站
(4)利用文件包含
2、png渗透
3、gif渗透
本文通过《upload-labs靶场通关笔记系列》来进行upload-labs靶场的渗透实战,本文讲解upload-labs靶场第14关图片马之文件头绕过渗透实战。
一、图片特点
在upload-labs靶场的1-13关卡,判断是否为图片或者脚本,基本都是通过文件的扩展名是否在黑名单或者白名单中。然而这种方法并不可靠,判断是否是真实的图片还可以根据图片的文件结构来进行验证,比如说jpg、png和gif图片的文件特征如下所示。
- JPEG:以 FF D8 开头,以 FF D9 结束
- PNG:以89 50 4E 47 0D 0A 1A 0A开头
- GIF:以 GIF89a 或 GIF87a 开头,十六进制以47 49 46 38开头
-
GIF89a(较新版本,支持动画和透明色)
-
十六进制:
47 49 46 38 39 61
-
ASCII:
GIF89a
-
-
GIF87a(旧版本,基础功能)
-
十六进制:
47 49 46 38 37 61
-
ASCII:
GIF87a
-
-
二、源码分析
打开靶场14关,查看源码进行源码分析,发现本小节依旧是白名单绕过,不过判断是否为图片的方法不是根据文件名的后缀,而是根据文件内容的前两个字节,具体源码如下图所示。
1、代码审计
这段代码实现了一个文件上传功能,并且通过读取文件的二进制头部信息来判断文件的真实类型,只允许已知类型(JPEG、PNG、GIF)的文件上传,同时对上传过程中的不同情况给出相应的提示信息。详细注释后的源码如下所示。
<?php
// 定义一个函数 getReailFileType,用于根据文件的二进制头部信息判断文件的真实类型
function getReailFileType($filename) {// 以二进制只读模式打开指定的文件// "rb" 表示以二进制只读模式打开文件,确保能正确读取文件的二进制数据$file = fopen($filename, "rb");// 从打开的文件中读取前 2 个字节的数据// 许多文件类型的特征信息存储在文件的开头部分,读取前 2 个字节有助于判断文件类型$bin = fread($file, 2);// 关闭已打开的文件,释放系统资源fclose($file);// 使用 unpack 函数将读取的二进制数据按指定格式解析为数组// "C2chars" 表示将二进制数据解析为 2 个无符号字符,并存储在名为 "chars" 的数组中// @ 符号用于抑制可能出现的警告信息$strInfo = @unpack("C2chars", $bin);// 将解析得到的 2 个无符号字符的值拼接成一个整数// 不同文件类型的二进制头部有特定的字节组合,通过这个整数可以判断文件类型$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);// 初始化文件类型变量,用于存储最终判断的文件类型$fileType = '';// 根据拼接得到的整数 $typeCode 进行判断,确定文件的具体类型switch ($typeCode) {// 如果 $typeCode 为 255216,说明文件是 JPEG 格式case 255216:$fileType = 'jpg';break;// 如果 $typeCode 为 13780,说明文件是 PNG 格式case 13780:$fileType = 'png';break;// 如果 $typeCode 为 7173,说明文件是 GIF 格式case 7173:$fileType = 'gif';break;// 如果 $typeCode 不匹配上述任何一种情况,将文件类型标记为未知default:$fileType = 'unknown';}// 返回最终判断的文件类型return $fileType;
}// 初始化变量 $is_upload,用于标记文件是否上传成功,初始值为 false 表示尚未成功上传
$is_upload = false;
// 初始化变量 $msg,用于存储上传过程中的提示信息,初始值为 null 表示暂无提示信息
$msg = null;// 检查是否通过 POST 方式提交了名为 'submit' 的表单元素
// 如果提交了,说明用户触发了文件上传操作,进入后续处理逻辑
if (isset($_POST['submit'])) {// 获取上传文件在服务器临时存储的路径// $_FILES['upload_file']['tmp_name'] 是 PHP 处理文件上传时自动生成的临时文件路径$temp_file = $_FILES['upload_file']['tmp_name'];// 调用 getReailFileType 函数,根据文件的二进制头部信息判断文件的真实类型$file_type = getReailFileType($temp_file);// 检查判断得到的文件类型是否为未知类型if ($file_type == 'unknown') {// 如果是未知类型,将提示信息 $msg 设置为文件未知,上传失败$msg = "文件未知,上传失败!";} else {// 如果文件类型已知,拼接上传文件在目标目录中的完整路径// UPLOAD_PATH 是预定义的上传文件保存目录// rand(10, 99) 生成一个 10 到 99 之间的随机整数// date("YmdHis") 获取当前的日期和时间,格式为年月日时分秒// 最后拼接上判断得到的文件扩展名$img_path = UPLOAD_PATH . "/" . rand(10, 99) . date("YmdHis") . "." . $file_type;// 尝试将临时文件从临时存储路径移动到目标路径// move_uploaded_file 是 PHP 内置函数,用于处理文件上传移动操作if (move_uploaded_file($temp_file, $img_path)) {// 如果文件移动成功,将 $is_upload 标记为 true,表示文件上传成功$is_upload = true;} else {// 如果文件移动失败,将提示信息 $msg 设置为上传出错$msg = "上传出错!";}}
}
?>
综上,本小节通过读取文件的前两个字符判断是否为图片,具体原理是因为图片的格式中,.png、.jpg、.gif的文件头格式中默认前几个字节如下所示:
1).Png图片文件前8字节:89 50 4E 47 0D 0A 1A 0A。即为 .PNG。
2).Jpg图片文件前2字节:FF D8。
3).Gif图片文件前6字节:47 49 46 38 39|37 61 。即为 GIF89(7)a。
整理为表格如下所示。
图片类型 | 十进制值 | 十六进制值 | 特征字符特点 |
---|---|---|---|
JPEG | 255, 216 | FF, D8 | 以FF D8 开头 |
PNG | 137, 80 | 89, 50 | 以89 50 4E 47 0D 0A 1A 0A 开头,这是 PNG 文件的签名。 |
GIF | 71, 73 | 47, 49 | 以47 49 46 38 开头,后面跟着版本号,如37 或39 ,表示 GIF87a 或 GIF89a 版本。 |
2、渗透思路
由于源码中根据文件的前2个字节的十进制值判断是否为图片文件,故而本实验中的绕过方法正式通过修改脚本内容,使得脚本得前两个字节与pass14源码中图片格式的前两个字节相同,通过这种方法来制作简易图片马欺骗服务器。
三、制作简易图片马
1、制作脚本test14.php
注意:<?php前面需要空出来两格,为了添加图片类型的头两个字节做准备),然后保存命名为test14.php,具体内容如下所示。
<?
phpphpinfo();
?>
2、制作图片马
使用UltraEdit软件编辑test14.jpg, 进入界面后,点击编辑十六进制编辑。
会出现以下画面。
(1)制作jpg图片马
这里我们以jpg的头部两个字节为例,jpg图片文件的对应头两个字节为FF D8,所以将文件开头的20 20 改为 FF D8 ,修改完成,另存为test14.jpg,这样我们就制作好了一个带有特定头部字节的文件。
(2)制作gif图片马
同样,.gif图片文件的对应头两个字节为47 49,所以将文件开头的20 20改为47 49另存为test14.gif。
(3)制作png图片马
.png图片文件的对应头两个字节为89 50,所以将文件开头的20 20改为89 50,另存为test14.png。
四、文件包含与图片马
文件包含漏_洞是因 Web 应用对文件包含的用户输入校验不足,使攻击者能控制包含文件路径,进而读取敏感文件或执行恶意代码。图片马则是将恶意脚本藏于图片中,利用文件包含实施攻击。攻击者先制作图片马,把恶意代码嵌入正常图片,再上传至有文件包含风险点的服务器,通过构造 URL 让服务器包含并执行图片马中的代码,以此获取服务器权限、窃取数据等,对系统安全危害极大。利用文件包含和图片马一般有以下步骤:
- 制作图片马:选择要上传的shell脚本,利用文本编辑器在脚本的首部添加图片特征的前两个字节并保存制作图片马。
- 上传图片马:寻找目标系统的文件上传接口,将图片马上传成功。
- 利用文件包含风险点执行图片马:攻击者通过构造特殊的 URL,利用文件包含风险点让服务器包含并执行图片马中的恶意代码。例如,假设存在文件包含的页面是include.php,通过访问include.php?file=upload/test14.jpg,其中upload是图片马上传的目录,其中test14.jpg是图片马文件名,服务器就会执行图片马中的恶意代码。
五、渗透实战
1、jpg渗透
(1)上传jpg图片马
如下图所示,上传成功。
(2)获取图片URL地址
在上传成功的图片显示区,鼠标右键点击回复框,点击复制图像链接,获取图片马URL地址,具体如下所示。
http://127.0.0.1/upload-labs/ upload/8520211119092303.jpg
(3)打开文件包含网站
打开文件包含渗透网址,进入文件包含主界面,url地址如下所示。
http://127.0.0.1/upload-labs/include.php
(4)利用文件包含
当前upload-labs的靶场文件包含的url地址如下所示。
http://127.0.0.1/upload-labs/include.php
上传的图片脚本url如下所示。
http://127.0.0.1/upload-labs/upload/8520211119092303.jpg
于是脚本相对于文件包含url的相对地址如下所示。
upload/8520211119092303.jpg
需要构造文件包含利用的语句如下所示。
include.php?file=upload/8520211119092303.jpg
完整的文件包含利用URL如下所示,访问可以获得php版本信息,渗透成功。
http://127.0.0.1/upload-labs/include.php?file=upload/8520211119092303.jpg
2、png渗透
将制作好的png格式的图片马test14.png进行上传,如下所示。
test14.png上传成功画面如下所示,绕过了服务器的过滤条件。
由于.png文件的绕过与以上.jpg文件绕过的方式相似,此处就不再重复列举,只提供最后的结果作为比较,文件包含利用完整URL地址如下所示。
http://127.0.0.1/upload-labs/include.php?file=upload/8220211119092704.png
如下所示成功访问获得显示php版本号画面,证明我们使用文件包含进行渗透成功。
3、gif渗透
将制作好的gif格式的图片马test14.gif进行上传,如下所示。
test14.gif上传成功画面如下所示,绕过了服务器的过滤条件。
由于.gif文件的绕过与以上.jpg和png文件绕过的方式相似,此处就不再重复列举,只提供最后的结果作为比较,文件包含利用完整URL地址如下所示。
http://127.0.0.1/upload-labs/include.php?file=upload/5020211119092530.gif
如下所示,利用文件包含访问图片马成功显示php版本号,证明使用文件包含进行渗透成功。