PHP文件上传流量层面WAF绕过
字数 1309 2025-08-27 12:33:43

PHP文件上传流量层面WAF绕过技术详解

一、简介

PHP文件上传实现规范为RFC1867,本文基于PHP 7.3.4 + nginx 1.20.1环境分析,相关源码可在GitHub获取。PHP解析multipart/form-data HTTP请求体的入口函数是SAPI_POST_HANDLER_FUNC

二、文件解析流程

PHP处理文件上传的基本流程如下:

  1. 接收HTTP请求
  2. 解析multipart/form-data格式
  3. 处理Content-Disposition头部
  4. 解析文件名和其他元数据
  5. 存储上传文件到临时目录
  6. 填充$_FILES超全局变量

三、关键绕过技术

1. 前向截断

技术原理
\/字符会对文件名进行前向截断。例如info.txt/info.php会被处理为info.php

关键函数
php_ap_basename()函数会寻找\/字符最后出现的位置并进行截断:

static char *php_ap_basename(const zend_encoding *encoding, char *path) {
    char *s = strrchr(path, '\\');
    char *s2 = strrchr(path, '/');
    if (s && s2) {
        if (s > s2) { ++s; } else { s = ++s2; }
        return s;
    } else if (s) { return ++s; }
    else if (s2) { return ++s2; }
    return path;
}

设计初衷
解决IE浏览器上传文件时传递全路径名的问题。

2. 后向截断

技术原理
\0(NULL字节)会对文件名进行后向截断。例如info.php\0xxx会被处理为info.php

实现细节

  • 解析header时仅对内存进行copy
  • 使用strlen获取filename长度,遇到\0即终止
  • 同样适用于$_POST变量名,但不适用于变量值

3. 文件名末尾反斜杠忽略

技术原理
当出现\后紧跟引号字符时,PHP会忽略\只取引号的值。

关键函数
php_ap_getword()中的处理逻辑:

if (*pos == '\\' && pos[1] && pos[1] == quote) {
    pos += 2;
}

4. 分号影响解析

技术原理
类似filename=info.php;.txt;的字符串会被解析为info.php

解析流程

  1. 先使用;进行分词,得到filename=info.php.txt
  2. 然后使用=进行分词,将filename解析为info.php

5. 双写filename

技术原理
PHP按照从前到后的顺序解析Content-Disposition,后面相同的变量名会覆盖前面的值。

四、上传失败场景分析

1. 文件名首字符为NULL

当filename首字符为\0时,上传会失败:

if (filename[0] == '\0') {
    #if DEBUG_FILE_UPLOAD
    sapi_module.sapi_error(E_NOTICE, "No file uploaded");
    #endif
    cancel_upload = UPLOAD_ERROR_D;
}

2. name首字符为右方括号

当name首字符为]时,会导致上传失败:

while (*tmp) {
    if (*tmp == '[') { c++; }
    else if (*tmp == ']') {
        c--;
        if (tmp[1] && tmp[1] != '[') {
            skip_upload = 1;
            break;
        }
    }
    if (c < 0) { skip_upload = 1; break; }
    tmp++;
}

五、实战应用技巧

  1. 组合使用技术:可以灵活组合前向截断、后向截断等技术
  2. 文件名构造
    • 使用/\进行路径截断
    • 使用\0进行后缀截断
    • 使用;进行参数分隔
  3. Content-Disposition构造
    • 双写filename参数
    • 使用特殊字符干扰解析

六、防御建议

  1. 对上传文件名进行严格过滤
  2. 检查NULL字节等特殊字符
  3. 限制上传文件扩展名
  4. 对上传文件内容进行验证
  5. 使用随机重命名策略

七、参考资源

  1. PHP文件上传流量层面WAF绕过
  2. Java文件上传流量层面WAF绕过

通过深入理解PHP文件上传的内部机制,可以发现更多潜在的绕过技术。建议开发者结合PHP源码进行更深入的分析,以构建更安全的文件上传功能。

PHP文件上传流量层面WAF绕过技术详解 一、简介 PHP文件上传实现规范为RFC1867,本文基于PHP 7.3.4 + nginx 1.20.1环境分析,相关源码可在GitHub获取。PHP解析multipart/form-data HTTP请求体的入口函数是 SAPI_POST_HANDLER_FUNC 。 二、文件解析流程 PHP处理文件上传的基本流程如下: 接收HTTP请求 解析multipart/form-data格式 处理Content-Disposition头部 解析文件名和其他元数据 存储上传文件到临时目录 填充 $_FILES 超全局变量 三、关键绕过技术 1. 前向截断 技术原理 : \ 和 / 字符会对文件名进行前向截断。例如 info.txt/info.php 会被处理为 info.php 。 关键函数 : php_ap_basename() 函数会寻找 \ 和 / 字符最后出现的位置并进行截断: 设计初衷 : 解决IE浏览器上传文件时传递全路径名的问题。 2. 后向截断 技术原理 : \0 (NULL字节)会对文件名进行后向截断。例如 info.php\0xxx 会被处理为 info.php 。 实现细节 : 解析header时仅对内存进行copy 使用 strlen 获取filename长度,遇到 \0 即终止 同样适用于 $_POST 变量名,但不适用于变量值 3. 文件名末尾反斜杠忽略 技术原理 : 当出现 \ 后紧跟引号字符时,PHP会忽略 \ 只取引号的值。 关键函数 : php_ap_getword() 中的处理逻辑: 4. 分号影响解析 技术原理 : 类似 filename=info.php;.txt; 的字符串会被解析为 info.php 。 解析流程 : 先使用 ; 进行分词,得到 filename=info.php 和 .txt 然后使用 = 进行分词,将filename解析为 info.php 5. 双写filename 技术原理 : PHP按照从前到后的顺序解析Content-Disposition,后面相同的变量名会覆盖前面的值。 四、上传失败场景分析 1. 文件名首字符为NULL 当filename首字符为 \0 时,上传会失败: 2. name首字符为右方括号 当name首字符为 ] 时,会导致上传失败: 五、实战应用技巧 组合使用技术 :可以灵活组合前向截断、后向截断等技术 文件名构造 : 使用 / 或 \ 进行路径截断 使用 \0 进行后缀截断 使用 ; 进行参数分隔 Content-Disposition构造 : 双写filename参数 使用特殊字符干扰解析 六、防御建议 对上传文件名进行严格过滤 检查NULL字节等特殊字符 限制上传文件扩展名 对上传文件内容进行验证 使用随机重命名策略 七、参考资源 PHP文件上传流量层面WAF绕过 Java文件上传流量层面WAF绕过 通过深入理解PHP文件上传的内部机制,可以发现更多潜在的绕过技术。建议开发者结合PHP源码进行更深入的分析,以构建更安全的文件上传功能。