S2-062(CVE-2021-31805) に関する任意のコード実行について
2022年4月19日 00:22 PM (JST)
こんにちは、くまさかです。
本脆弱性に関しては既に様々な情報が出ておりますが、それらの情報に関し少し注意すべき点があるため、その共有と検証結果等を紹介します。
既に、OGNL式%{.*}
等にてシグネチャを書かれた防御側の皆様は、本ページの項目「攻撃クエリの特徴は%{.*}
ではない」 をご覧ください。
概要
既にJPCRERTより、S2-062について注意喚起が出ている ので、ご存じの方も多いかと思いますが、
Apache Struts 2.0.0 から、2.5.29 までで、
任意のコード実行 が可能な脆弱性があります。
なお、既に修正版の、Apache Struts 2.5.30 がリリース されているので、こちらへのアップデートを推奨します。
発動条件
本脆弱性は、OGNL式が存在するnameエレメントがあり、且つ、同一タグ上でvalue
エレメントがが存在しない場合に攻撃が可能です。
(そのため、作成されているコンテンツの形によっては、影響を受けない可能性もあります。)
labelタグで例を表すと、以下のような表記になります。
<s:label id="rabbit" name="%{tank}" />
この例では、tank
に、細工した文字列を挿入することで、任意のコード実行が可能となります。
攻撃クエリの特徴は、%{.*}
ではない
よく紹介されるApache Struts 2 の、OGNL式な攻撃コードの特徴としては、%{.*}
や、EL式も動作する際には、${.*}
といった文字列があります。
しかしながら、今回の脆弱性では、それらの特徴が無い形にて攻撃を成功させます。
もし、「OGNL式による攻撃なので、%{.*}
をひっかけよう。」と検討された方がおりましたら、それでは不十分であり、対策の軌道修正が必要です。
以下は、弊社検証環境構成での、攻撃が成功するクエリの画像です。
赤枠で囲んでいるところからわかるように、%28%23
から始まっています。(デコードすると、(#
)
このように、%{}
というような文字列を含めない形で攻撃が可能なため、%{.*}
という単純なルールでは検出できません。
また、今回は、S2-061とは異なり、攻撃コードの実行結果(stdout)がHTTPレスポンスのbodyに、含まれないという特徴もあります。レスポンスで攻撃成功の有無などを判断しようとしている場合は、この点も注意が必要です。
本脆弱性の原因
本脆弱性は、S2-061の修正が不十分であったためといった表現を目にします。
具体的にどのような修正であったのかを確認しつつ、原因箇所を少し解説します。
S2-061では、OGNL式の多重評価により任意のコード実行が可能だった為、修正が行われました。
https://github.com/apache/struts/commit/0a75d8e8fa3e75d538fb0fcbc75473bdbff9209e
しかし、この修正にて、OGNL式へ変換したexpr
インスタンスを用意する※1 も、expr
ではなく、変換前のname
インスタンスを評価に利用する※2 といった修正が加えられてしまいました。
この実装により、評価されていないOGNL式なexpr
インスタンス が実行されてしまうといった挙動をしています。
該当箇所は、parameters.containsKey("value")
のelse上に存在する ※3なので、同一タグ上のエレメントに、value
のエントリが無いと、任意のOGNL式が実行できます。
※1 https://github.com/apache/struts/commit/0a75d8e8fa3e75d538fb0fcbc75473bdbff9209e#diff-cfe644a2b24b492d6835fa1f38e7a770dad354b286cbe6b056a5fe7e80e669caR797
※2 https://github.com/apache/struts/commit/0a75d8e8fa3e75d538fb0fcbc75473bdbff9209e#diff-cfe644a2b24b492d6835fa1f38e7a770dad354b286cbe6b056a5fe7e80e669caR798
※3 https://github.com/apache/struts/commit/0a75d8e8fa3e75d538fb0fcbc75473bdbff9209e#diff-cfe644a2b24b492d6835fa1f38e7a770dad354b286cbe6b056a5fe7e80e669caR787
なお、Apache Struts 2.5.30 に含まれる最終的な修正では、expr
インスタンスの用意をする直前に処理が移されています。
https://github.com/apache/struts/commit/e1209fde05a34b28e747f1fa858c82d2a32cb018#diff-cfe644a2b24b492d6835fa1f38e7a770dad354b286cbe6b056a5fe7e80e669caR804
(同じような修正漏れを誘発させないためか、オブジェクト名をname
のみから、translatedName
等と差分も持たせているので、差分が多くなっていますね。)
ワークアラウンド
前述の通り、value のエントリが無い場合に限り、任意のOGNL式が実行できるという条件のため、ワークアラウンドとして、value
のエントリを付加する事も挙げられます。
用途が無いのであれば、以下の例のようにvalue
に空の値を入れるといった対応でも防ぐことが可能です。
<s:label id="rabbit" name="%{tank}" value="" />
弊社検証環境構成では、実際にvalue
有無それぞれのコンテンツを作成し、空のvalueがある場合には攻撃を防げることを確認しています。
なお、攻撃コードの実行結果は、HTTPレスポンスに含まれず攻撃が失敗するワークアラウンドの例であっても、200OKが返されます。
そのため、防御可否はサーバ側の実行ログ等で確認するしかありません。
シグネチャで守るのであれば。
前述したように、%{.*}
だけでは、すり抜けます。
よく見かけるRCEのPoCでは、OGNL Sandbox回避の為の特徴的な文字列((#request.map=#@org.apache.commons.collections.B...
)が見られるので、世に出ているRCEなPoCを防ぐには、こちらを特徴とすることも検討に挙げられます。
しかし、RCEではなく、#application['org.apache.*************'].getResource('/mycontent.html').*********()
のように、リソースを取得し、書き換える事が可能なOGNL構文は異なる特徴をしているので、注意が必要です。
(公開されているPoCの調査中に、リソースへの変更を加えるPoCのソースコードも確認しています。)
攻撃者の調査を阻むことはできませんが、実害を防ぐためのシグネチャとしては、
「Apacheのリソースにアクセスでき、OGNL構文の中身に特徴的に表れる文字列が含まれること」を念頭に、条件としては少し広めですが、以下のようなパターンが考えられます。
# が含まれ、
#@org\.apacheか、'org\.apache のいずれかまたはその両方が含まれること
やはり、根本的な対策を推奨
以上のように、単純なワークアラウンドでは対応が漏れる可能性が考えられますし、それをどうにかしようと思うと、シグネチャで少し広めの検知するしかありません。
そのため(本件に限ったことではないですが)、可能な限り、バージョンアップを推奨いたします。
(本ブログの最初に記載した通り、既に修正版のApache Struts 2.5.30 がリリース されています。)
実は今回、S2-062 についてはだいぶ乗り遅れ感があったので、ブログの見送りを考えておりました。
しかし、S2-062を簡単に調べて出てくる情報では、残念ながらOGNL式というキーワードや、%{}
での対応策が目立っておりました。
そのため、防御側の誤認が発生している可能性もあるかと考え、ここに掲載するに至りました。
参考
- https://www.jpcert.or.jp/at/2022/at220011.html
- https://cwiki.apache.org/confluence/display/WW/Version+Notes+2.5.30
- https://cwiki.apache.org/confluence/display/WW/S2-062
2022年04月19日 00:22 PM (JST): 公開
2022年04月21日 10:00 AM (JST): スタイルを一部修正
2023年11月21日 09:00 PM (JST): 記事更新日時のシステムアップデート