flawopen.com/Incidents/equifax-struts-ognl-rce-july-2017
Equifax:データ約1億5,000万人に影響
CVE-2017-5638 により、未修正の Equifax Struts ポータルで Content-Type ヘッダー経由の OGNL インジェクションが可能でした。攻撃は約 76 日続き、約 1 億 5,000 万人のデータが影響を受けました。[1], [4]
配送ソフトが、拒否した送り状ラベルをエラー通知に取り込むと想像してください。その通知がラベルを実行可能な命令テンプレートとして扱うと、差出人は住所の誤りを知らせるだけでなく、ソフトに命令を実行させられます。Struts の修正はすでにありましたが、Equifax は古い管理者リストに通知を送り、ネットワークスキャンでもポータル上の脆弱性を検出できませんでした。[1], [4]
主要な概念と専門用語
OGNL- Object-Graph Navigation Language。Struts が Java オブジェクトのプロパティの読み書きに使う式言語です。攻撃者の文字列を OGNL として評価することは、攻撃者のコードを実行することを意味します。[1], [6]
Jakarta マルチパートパーサー- ファイルのアップロードを扱う Struts のコンポーネント。不正な Content-Type を拒否するとき、後で評価されるエラーメッセージを組み立てていました。[1], [3], [2]
式インジェクション(CWE-917)- 信頼できない入力がインタープリターの評価する式の中に入り込み、データが命令になってしまうことです。[6]
資産インベントリ- どのシステムでどのソフトウェアのどのバージョンが動いているかの記録。これがないと、パッチの通知を必要なマシンに結びつけられません。[4]
フレームワーク対応
- このガイドで説明する弱点の CWE 参照: CWE-20 CWE-917
- CISA KEV — 追加日 2021-11-03 — CVE-2017-5638
インシデントのタイムライン
Apache がセキュリティ公告 S2-045 と、修正版の Struts 2.3.32 および 2.5.10.1 を公開する。[1]
Equifax は後に GAO に対し、別の侵入が 5 月 13 日に始まり、攻撃者が個人データの抽出を開始したと説明しました。それ以前にも不正アクセスはありましたが、その時点ではデータは盗まれていません。[4]
Equifax が、トラフィック検査装置の期限切れ証明書を更新した後に不審な通信に気づく。[4]
Equifax が侵害を公表する。後に GAO は、米国内の少なくとも 1 億 4,550 万人と国外の約 100 万人のデータが影響を受けたと報告する。[4]
ステップ・バイ・ステップの攻撃フロー
ソースコード比較:脆弱 vs 堅牢化
説明用の Java で、Struts のソースコードではありません。2.3.32 と 2.5.10.1 の実際の修正は、マルチパートのエラーの組み立て方を変えるものでした。強化版は固定のメッセージキーを使い、ヘッダーは位置引数としてだけ渡すので、テキストとして整形されるだけで式の一部にはなりません。さらに、メディアタイプらしくない値はプレースホルダーに置き換えるため、後でメッセージを変更しても、ヘッダーが評価される文字列に戻ってくることはありません。[3], [2], [6]
import com.opensymphony.xwork2.TextProvider;
import javax.servlet.http.HttpServletRequest;
public final class UploadErrors {
private final TextProvider text; // resolves messages and evaluates expressions in them
public UploadErrors(TextProvider text) {
this.text = text;
}
public String describe(HttpServletRequest request) {
String contentType = request.getHeader("Content-Type");
// 生のヘッダーが評価対象のメッセージの一部になる。
return text.getText("Invalid content type: " + contentType);
}
}import com.opensymphony.xwork2.TextProvider;
import java.util.Locale;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
public final class UploadErrors {
private static final Pattern MEDIA_TYPE = Pattern.compile(
"^[a-z]+/[a-z0-9.+-]+(;\\s*[a-z0-9-]+=[a-z0-9._-]+)*$");
private final TextProvider text;
public UploadErrors(TextProvider text) {
this.text = text;
}
public String describe(HttpServletRequest request) {
String contentType = request.getHeader("Content-Type");
boolean wellFormed = contentType != null && contentType.length() <= 256
&& MEDIA_TYPE.matcher(contentType.toLowerCase(Locale.ROOT)).matches();
// 固定のメッセージキーを使い、ヘッダーは位置引数としてのみ渡す。
// さらに、メディアタイプの形式である場合に限る。
return text.getText("upload.invalid.content.type",
new String[] { wellFormed ? contentType : "(malformed)" });
}
}エンジニアリング&システム堅牢化チェックリスト
- ✓デプロイしたすべてのアプリケーションの SBOM を保持し、S2-045 のような公告から数時間以内に「Struts 2.3.x はどこで動いているか」に答えられるようにする。[4]
- ✓脆弱性スキャナーの対象を資産インベントリと突き合わせる。一度も対象になっていないホストのクリーンなスキャン結果は、パッチ適用の証拠にならない。[4]
- ✓TLS 検査装置や監視装置の証明書の期限切れを、少なくとも 30 日前にアラートする。今回は期限切れの証明書が数か月分の持ち出しを隠した。[4]
- ✓コードレビューでは、信頼できない文字列を連結してメッセージリゾルバー、テンプレートエンジン、式評価器に渡す呼び出しを却下し、引数として渡させる。[6]
- ✓データベースをインターネットに面したアプリケーションから分離し、認証情報をアプリケーションのファイルに置かず、アプリケーション用アカウントの異常な問い合わせ量にアラートを出す。[4]
参考資料
- Apache Struts — セキュリティ公告 S2-045(CVE-2017-5638)
- Apache Struts — multipart parser error-message fix (commit 3523064)
- Apache Struts — upstream multipart error-handling fix (commit 6b8272c)
- 米国 GAO — データ保護:2017 年の侵害を受けた Equifax と連邦機関の対応(GAO-18-559)
- CVE-2017-5638 — CVE レコード
- MITRE CWE-917 — 式言語インジェクション
- Log4Shell:ログを書くことがリモートコード実行になったとき
- コマンドインジェクション — クラス全体のリファレンス