インフラセキュリティの処方箋

第32回2017年3月~Struts 2脆弱性(S2-045~S2-046)俯瞰してみた

2017年3月は、Struts 2の大型脆弱性で、対応に追われていた方も多いのではないでしょうか。今月は、Struts 2脆弱性の件について取り上げます。

Struts 2脆弱性~S2-045~S2-046の件

2017年3月7日に、Apache Struts 2の脆弱性情報S2-045が、2017年3月20日に脆弱性情報S2-046が確認されました。

正式な脆弱性情報のページは、Struts ProjectのWebサイト内に置かれますが、正式なWebサイトに置かれる前に、Wikiサイトでの編集を行われることがあります。今回のS2-045については、Wikiサイト上でその内容が確認されました。S2-046についても、同様の経緯を経て正式なWebサイトに置かれています。

S2-045とS2-046のヤバさ~HTTPヘッダに実行コードを書けてしまう脆弱性

S2-045とS2-046は、いずれもHTTPヘッダに実行コードを書けてしまう類の脆弱性です。

実行コードは、OGNL式と呼ばれる言語(Javaみたいなもの)で記述が可能であり、OGNLに関する知識があれば、悪質な実行コードの記述も普通にできてしまいます。

S2-045特有のまずさ

S2-045は、脆弱性情報が(正式な情報ではなく、Wikiサイト上での編集途上であるとはいえ)公開されてしまったことと、この脆弱性情報が公開された時点で、脆弱性対策版のStruts 2がまだリリースされていなかったこと、そして脆弱性対策版Struts 2が出る前にPoCが公開されてしまったことが、被害を拡大させる結果につながってしまったと考えています。

とくに、脆弱性情報(のドラフト)と脆弱性のPoCが、脆弱性修正案のStruts 2リリースより前に公開され、当該脆弱性がゼロデイ状態になってしまったことは、脆弱性対策をする側からしてみたら、かなり嫌な状態と言えます。なお、S2-046は、S2-045の対策がなされたStruts 2にしていれば影響を受けないことから、S2-045が公開された時よりはマシとも言えます。

どの類のヘッダに実行コードを含められるのか

S2-045は通常のリクエストに付加したContent-Typeヘッダに、S2-046はマルチパート電文中に付加されたContent-Dispositionヘッダに実行コードを含めることが可能です。繰り返しになりますが、S2-045およびS2-046は、脆弱性および検証コード(PoC)が公開された時点で、脆弱性対策を行ったStruts 2が公開されていませんでした。この時点では、Webアプリケーションファイアウォール(WAF)などで攻撃リクエストを止めるなどの手段くらいしか考えられなかったとは思いますが、⁠しょうがないとはいえ)対策がきわめて場当たり的なものにならざるを得なかったという点も、まずいと言えます。

PoCが意味するところはそれぞれ、⁠特定の演算結果をレスポンスに含める(S2-045のPoC⁠⁠レスポンスに任意のヘッダを付加させる(S2-046のPoC⁠⁠」というものですが、これらの処理は、悪意ある処理内容に容易に置き換えることが可能です図1図2⁠。

図1 HTTPヘッダ中に埋め込まれるPoCの例
図1 HTTPヘッダ中に埋め込まれるPoCの例
図2 マルチパート電文中のヘッダに埋め込まれるPoCの例
図2 マルチパート電文中のヘッダに埋め込まれるPoCの例

S2-045を用いた攻撃は、ログ形式を拡張することで記録できる可能性がある

S2-045脆弱性は、HTTPリクエストに含まれる Content-Typeヘッダ埋め込んだOGNL式のコードを実行させられる脆弱性です。Content-Typeヘッダは、たとえばApache2のログで、以下のようなLogFormatを定義することで、ログに含めることが可能です。

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"  \"%{Content-Type}i\"" combined

LogFormat中、\"%{Content-Type}i\" としている部分が、Content-Typeを記録する設定です。ヘッダ名の後に"i"とついていますが、これはリクエストに含まれるヘッダを記録するという意味です。これを"o"に変えると、レスポンスに含まれるヘッダを記録するようになります。

以下、実際に上記のLogFormat設定を施したWebサーバに対し、S2-045のPoCを実行した結果記録されたログの内容を示します。

xxx.xxx.xxx.xxx - - [27/Mar/2017:03:09:16 +0900] "GET / HTTP/1.1" 200 56 "-" "python-requests/2.13.0"  "%{(#test='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(#ros.println(102*102*102*99)).(#ros.flush())}"

ログを何らかのツールを用いて処理している場合には、ログ形式が変わることで不具合を起こす可能性がありますので、実施する際には運用等も勘案して変更してください。

日を追うごとに増えていく被害~脆弱性対策を考え直すきっかけとしてほしい

今回のS2-045およびS2-046脆弱性ですが、⁠とくに)S2-045および脆弱性対策がされた版のStruts 2が出た時点で Struts 2を脆弱でないものに入れ替える判断を行わなかったところが、その後の脆弱性対応においても苦しむ結果になっているように見えます。安定稼働しているシステムのフレームワークを入れ替える際に、どこに影響が出てくるか?は、Changelogおよびソースコードの差分を確認することで推し量ることが可能です。

本連載の第1回目も、脆弱性対策に関するヒントになると考えます。

あたりまえですが、どこかで被害が出てから考えるのは、現状では得策ではありません。⁠どこか=自分のところ」の可能性もあります。是非、被害が出る前に脆弱性対応を行っていただければと思います。

おすすめ記事

記事・ニュース一覧