×
  • Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
 

とある診断員とSQLインジェクション

on

  • 285 views

5/23に開催したssmjpでLTした資料です。一部内容の追記をしています。

5/23に開催したssmjpでLTした資料です。一部内容の追記をしています。

Statistics

Views

Total Views
285
Views on SlideShare
250
Embed Views
35

Actions

Likes
1
Downloads
1
Comments
0

1 Embed 35

https://twitter.com 32

Accessibility

Categories

Upload Details

Uploaded via SlideShare as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
Post Comment
Edit your comment
  • 7
  • 8
  • ENT

とある診断員とSQLインジェクション とある診断員とSQLインジェクション Presentation Transcript

  • SQLインジェクション
  • 自己紹介  セキュリティエンジニアやってます。  脆弱性診断業務を担当しており、専門はWebセキュリティです。  趣味で脆弱性の検証したり、最近はCTFイベントなどに参加して たりします。 TwitterID: tigerszk
  • 本題に入るその前に
  • 脆弱性診断って? FW Webサーバ 診断環境 診断対象システム Webアプリ NW機器 診断対象のシステムに対して、疑似攻撃を試行し、対象システム の挙動から脆弱性の存在有無を調査します。 ※ちなみにこれはブラックボックス型と呼ばれる手法で、他に設計書、仕様書、コンフィグ ファイル、ソースコードなどを精査することで、脆弱性を検出するホワイトボックス型と呼ば れる手法もあったりします。 Internet 疑似攻撃 × × 診断員
  • 診断の種類 OS ミドルウェア Webコンテンツ Webアプリケーション診断 ネットワーク診断 (プラットフォーム診断) Webシステム なお、対象にする領域によって実施する診断の種類が違ったりします。 今回はWebアプリケーション診断についてお話します。
  • 30秒でわかるWebアプリケーション診断
  • 通常のWebサーバとの通信 <html> <body> <form action=“register” method=“POST”> 氏名:vultest<BR> メールアドレス:vultest@example.jp<BR> 性別:男<BR> (以下略) </html> POST /confirm.php HTTP/1.1 Host: example.jp (以下略) Cookie: PHPSESSID=xxxxxxxxxx name=vultest&mail=vultest%40example.jp&gender=1 HTTP Response HTTP Request
  • 色々いじってみてどういう応答があるか確認 POST /confirm.php HTTP/1.1 Host: example.jp (以下略) Cookie: PHPSESSID=xxxxxxxxxx name=vultest&mail=vultest%40example.jp”>xss&gender=1 <html> <body> <form action=“register” method=“POST”> 氏名:vultest<BR> メールアドレス:vultest@example.jp”>xss<BR> 性別:男<BR> (以下略) </html> HTTP Response HTTP Request じゃあパラメータ値で こんなの送ったらどう なるの? おお!出力時に「”」,「>」が エスケープされてないね。 XSSあるんじゃね?
  • 基本的にはこれを様々なパターンでひたすら繰り 返し行う苦行です。 以上。 ※厳密いえばもっと他にも色々な作業があります。
  • ツールとかも使います 当たり前ですが、全部手動でやっていたら死亡するので、ある程度 自動的に検査可能な項目はツールやスクリプトなどを利用してます。 ただ、ツールを使う場合にはちょっとしたコツが必要だったりします。 【Webアプリケーション診断における診断の大前提】 正確に診断を実施するためには値改変前のリクエストについては、ちゃんと正常遷 移するリクエストである必要があります。 【サイトの特性や機能に合わせたツールのチューニングが必要】 診断時には通信内容などを解析して、検査時に正常遷移時のリクエストを再現できるように 色々設定や工夫をしてやる必要があります。 チューニングが必要となる機能の例: 登録データの削除処理、重複登録不可の画面、全画面でトークンの値をチェックしている、etc… ※これについての詳しい話は長くなるのでまた別の機会にでも、、、
  • 必須アイテム 手動での診断作業や通信解析時にはローカル Proxyを非常に良く利用します。 HTTP Response HTTP Request HTTP Response HTTP Request ローカルProxy ブラウザからの通信をローカルProxyを経由させることで下記が可能 です。診断だけじゃなく問題の切り分けとかにも非常に便利です。 通信内容の詳細を確認可能 通信内容を編集して送信することも可能
  • お勧めのローカルProxyツール Burp Suite http://portswigger.net/burp/ OWASP ZAP https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project Fiddler http://www.telerik.com/fiddler ※ちなみにこれらのツールはMan In the Middle(中間者攻撃)的な方法で HTTPSの通信にも対応しています。
  • 診断する時の注意点 セキュリティ診断を円滑に実施するためには担当者に対して以下の ような注意事項の説明など、事前調整を入念に行う必要があります。 (※これ超重要) 調整事項の例  診断による影響についてちゃんと担当者に事前に説明する。 Webアプリケーション診断は検査対象のWebサーバに通信を行い実際にアプリケーションを動作させて 行います。そのため以下のような事象が発生する可能性があります。 • DBへの大量のダミーデータの書き込み • 大量のメール送信 • アプリケーションの作りによっては、本来意図しない動作の発生 …etc 上記のようなことが、発生する可能性があるため、担当者の方には事前にテスト環境での診断 実施&診断前のデータバックアップなどをお願いしています。  ホスティング環境やクラウド環境を利用している場合には、あらかじめインフラ提供者側に対して 診断実施をしてよいかの確認をしてもらう。(絶対勝手にやらない)  IDSやIPSなどで監視している場合には、診断時の通信を非監視にしてもらうようにお願いする。
  • はい!ここからが本題 「とある診断員」のお話が始まります
  • こんな条件の診断だった 昔々、ある所にまだ駆け出しのとある診断員がいました。 その診断員はある時以下のような条件のWebサイトを診断しました。  診断対象は本番環境だった。 ※もちろんとある診断員は事前に注意点を入念に説明し、テストサイトで の実施を勧めたが、「そんなもんねえから本番でやって」と一蹴された。  パッケージ利用とかではなく、自作のECサイトだった。  いわゆるLAMP環境で開発されたアプリだった。 (CENTOS,Apache,PHP,Mysql) かなり前に作られていたこともあり、どうやらあまり(というか全然)セキュリティ を考慮して作られていないサイトのようでした。 診断途中の段階で既にかなり多数の脆弱性が見つかっており、かわいそうな 診断員はヒイヒイ言いながら診断をしていました。
  • 突然アカウントが利用できなくなった とある診断員は一生懸命頑張って診断していましたが、ある日突然、診断に 使っていたアカウントでログインできなくなりました。 診断員: 「あれ?アカウントロックでもされたのかな?でもログイン処理とかまだ診断して ないけど、、、」 真面目な診断員はすぐに担当者に連絡しました 診断員: 「テスト用のアカウントでなんかログインできなくなったようなので、確認してもら えないですか?ロックされてしまったかもしれないです。」 担当者: 「了解しました。確認します。」 すると暫くした後、担当者から連絡がきました。
  • その時事件は起きた(いや起こってた) 担当者: 「か、、、確認したんですが、パ、、、パスワードが、、、 診断員: 「( ゜Д゜)」 全ユーザのパスワードが初期化されてます!!」
  • \(^o^)/
  • そりゃ当然こんな感じになるよね
  • 周りの人は目を合わせてくれない、、、 その時、不思議にも周りの人たちの心の声が聞こえた。 「あいつはもう駄目だ。今近寄ったら巻き込まれる。」 こんな感じの扱いだった
  • 最終的には
  • かくして孤独な戦いが始まった とある診断員は、危うく全ユーザの強制パスワード定期変更執行者となってし まうとこでしたが、幸いなことに担当者側で、診断事前にDBのバックアップは 取得していたので、取り急ぎ切戻しを行い最悪の事態は回避しました。 その後の調査で、パスワードがリセットされた時間帯に、他メンテナンス作業が なかったことや、アクセスログなどの状況から本件の原因が診断にあることは どうやら間違いなさそうでした。 診断作業は一時中断され、作業の再開と原因究明のために、速攻でテスト環 境が構築されました。 そんなに早くできるなら最初からテスト環境で、、、 かくして、とある診断員の自分が引き起こしたインシデント調査が始まりました。
  • 実は正直大体見当はついていた とある診断員は、その日ちょうどパスワードリマインダ機 能をツールで診断していました。しかも診断結果の精査 前だったが、とある脆弱性を検知していました。 とあるパスワードリマインダの仕様 1. ユーザのメールアドレス(アカウントID)と登録された誕生日を入力して値を照合する。 2. その後値が正しければ、入力されたメールアドレス宛にリセットされたパスワード文字列 が送信される。 ※このリマインダの仕様自体に色々ツッコミ所もあるかと思いますが、まあそれはこの際、 置いといてください。 ← こんな感じ
  • おそらく原因は、、、 この事象のトリガーとなった原因はこの機能にある 「SQLインジェクション」 の脆弱性なんだろうなとあらかじめ想定していました。
  • SQLインジェクションって?
  • 例えばこんなWebアプリあったとします  会員制ECサイトの商品検索ページ的なもの  値段や名前を条件として検索が可能  条件にマッチした商品名を画面に表示するだけの機能
  • 通常のアプリの処理の流れ Webサイト側の内部処理 パラメータ値などが含まれた問い合わせを Webサイトに要求する 価格が100円の商品を検索 http://exmple.jp?cost = 100 Webアプリケーション データベース 受信したパラメータ値からSQL文(データベースへの命令 文)が組み立てられデータベースに命令を送信 SELECT name FROM goods WHERE cost = 100 ; データベース内でSQL文が実行された結果 が返る SQL文の実行結果を元にアプリケーション側で生成されたHTTPレスポン スの内容がサーバからの応答として返る 「価格が100円の商品は下記です。缶コーヒー、ガム、アメ」と表示する HTML <html><body>….. サイト利用者 HTTP通信 HTTP Request HTTP Response SQL命令 ① ② ③ 下記は検索ボタンを押してから検索結果がブラウザに表示される までの処理の流れのイメージです。 ④ name 缶コーヒー ガム アメ
  • SQLインジェクション攻撃 Webサイト側の内部処理 ユーザ情報を削除するようなSQL文を構成するような文字列を パラメータ値に入力して送信する http://exmple.jp?cost=100; DELETE FROM users 脆弱な Webアプリケーション データベース 受信した値からパラメータ値からSQL文が組み立てられデータ ベースに命令を送信される。 この時、パラメータ値として入力した文字列の一部がそのまま SQL文として解釈され実行される。 これにより本来意図しない命令がデータベースで実行されてし まう。 SELECT name FROM goods WHERE cost = 100 ; DELETE FROM users ; 攻撃者 HTTP通信 HTTP Request HTTP Response 発生しうる脅威  データベースに蓄積された非公開情報閲覧、改ざん、消去  認証回避による不正ログイン  ストアドプロシージャ等を利用したOSコマンドの実行 ユーザ情報 を削除 SQL命令 脆弱な作りのWebアプリケーションの場合、SQL文の断片を含んだ不正なHTTPリクエストを 送信されることによって、外部からデータベースを操作されてしまう。
  • どうやって直せばよいの? Webアプリケーション開発時に脆弱性を作りこまないよう なコーディングをする必要があります。 SQLインジェクションの根本的対策 1. SQL文の実行方式にPrepared Statement(準備済みの命令文)を利用する。 2. SQL文の特殊記号となりうる入力値が存在しないか確認し、存在した場合 はエラーとするか適切なエスケープ処理を施す。(特殊文字はDBMSごとに異 なるのでそれぞれの環境に合わせた対策が必要。) ※詳細については徳丸本や「安全なSQLの呼び出し方」を参照するのが良いと思 います。
  • SQLインジェクションの見つけ方の例-その1- SQL文の断片を送信して、レスポンスにSQLエラー(DBMS等が 出力するエラーメッセージ)が出力されるか確かめたりする。 SELECT * FROM member WHERE name = 'tanaka';  通常時 送信値:tanaka 構成されるSQL文 検索結果:1件 田中 SELECT * FROM member WHERE name =''';  検査時 送信値:' 構成されるSQL文 SQL error
  • SQLインジェクションの見つけ方の例-その2- 送信した値ごとの応答結果の差分を見て判定したりもする。 SELECT * FROM member WHERE name = 'tanaka' AND 'a' = 'a';  条件式が真 送信値:tanaka' AND 'a' = 'a 構成されるSQL文 検索結果:1件 田中 SELECT * FROM member WHERE name = 'tanaka' AND 'a' = 'b';  条件式が偽 送信値:tanaka' AND 'a' = 'b 構成されるSQL文 検索結果:0件 なし SELECT * FROM member WHERE name = 'tanaka' BND 'a' = 'a';  文法として逸脱しているもの 送信値:tanaka' BND 'a' = 'b 構成されるSQL文 エラー
  • 送信すると危険な文字列がある SQLインジェクションの脆弱性があった場合、挿入する文字列に よっては、本来実行を意図しているSQL文の意味が変わってしま い大変なことになる場合があります。 ※先日楽しく拝見させていただいたabendさんのスライドから画像を拝借してきましたw これから書こうとしていることと全く同じことについてスライド内で言及されてます。 フリーでできるセキュリティWeb編(SQLMapを楽しもう)」 http://www.slideshare.net/abend_cve_9999_0001/websqlm
  • 例えばor条件だと UPDATE member SET password = 'xxxx' WHERE id = 1;  本来の意図する処理 受け渡す値:1 UPDATE member SET password = 'xxxx' WHERE id = 1 OR 1 = 1; カラム名「id」が1の値となっているレコードのみ、カラム名 「password」の値をxxxxに更新する  or条件のSQL文の断片を渡した場合 受け渡す値:1 OR 1 = 1 全てのレコードのカラム名「password」の値をxxxxに更新するこ とになってしまう!
  • 複文もヤバい場合がある UPDATE member SET password = 'xxxx' WHERE flag = 1 AND id = 1;  本来の意図する処理 受け渡す値:1 カラム名「flag」が1の値かつカラム名「id」が1の値となっているレ コードのみ、カラム名「password」の値をxxxxに更新する  複文となるSQL文の断片を渡した場合 受け渡す値: 1; SELECT 1;-- カラム名「flag」が1の値となっている全てのレコードのカラム名 「password」の値をxxxxに更新することになってしまう! UPDATE member SET password = 'xxxx' WHERE flag = 1; SELECT 1;-- AND id = 1;
  • でも、、、そういうのは送信していないはず SQLインジェクションに限ったことではないですが、 通常セキュリティ診断時に送信されるシグネチャ (検査文字列)は、なるべく検査対象に影響を与 えないようにするものを利用するように配慮して いたつもりでした。 とある診断員はその時自分が利用していたツー ルでは、当然さっきのような危険と思われるSQL 文のシグネチャなどは送信していない認識でした。
  • では何故こんなことが起きた?
  • なんとかログから答えを割り出した その後、とある診断員は診断時のHTTP通信のログ(※) などを頼りに、色々頑張って該当の時間帯にトリガーと なったと想定される文字列を割り出しました。 ※ちなみにこういう時のために普段から診断時のログは必ず全部取得するよ うにしてます。 それは「 '+' 」という値だった。
  • やっぱりSQLインジェクションの診断が原因 メールアドレスを入力するパラメータに以下のような値 を追加した場合に、他ユーザのパスワードがすべてリ セットされる事象が再現しました! 送信したパラメータ:aaa@vultest.com'+' や aaa@vulte'+'st.com など
  • ちなみにこの文字列の本来の用途 SQLインジェクションのシグネチャの一つで、SQL文における文字 列連結演算子を送信して応答結果から脆弱性があるかどうかを 判断するためのもの。ツールには「 '+' 」や「 '||' 」などの文字列を 送信するシグネチャが存在した。 ちなみに文字列連結演算子はDBMSごとに異なります。 || Oracle, DB2, Postgre等 + SQL Server, Sybase等 スペース、CONCAT() MySQL SELECT * FROM member WHERE name = 'tan'+'aka';  SQLインジェクションが存在する場合 送信値: tan'+'aka 構成されるSQL文 検索結果:1件 田中 挿入した文字列連結演算子がSQL文にて評価されている場合に脆弱性として検出
  • この事件の真相(推測)
  • まずはこちらをご覧ください MySQLでSQL文を実行しています。 上記のSQL文を実行した場合には、vultestテーブルの内容が全部表示されています。 このようなSQL文の場合には当然ですが、vultestテーブル中のカラム名「id」の値が aaa@vultest.comと等しい1レコードが返ります。
  • MySQLではこう解釈される 問題となっている「'+'」を含んだSELECT文をMySQLで実行するとなんと全件のレコードが 返ってきてしまいます! WHERE句の中身をわかりやすく表示してみると、'aaa@vultest.com'+''の結果が0となって います。
  • 理由はMySQLの「暗黙の型変換」 MySQLの文字列連結演算子はスペースやCONCAT()関数です。MySQLでは「+」記号は、 数値の和算として解釈されます。 MySQLは数値が要求される文脈に文字列があった場合は数値(浮動小数点数)に暗黙 の型変換されます。数値として正しくない文字列の場合は 0 になるそうです。 そのため下記のように判定されたと判断しています。 この結果WHERE句についてはid = 0となりますが、idカラムは文字列なので、また暗黙の 型変換が行われてから比較されます。その結果、idカラムの値が数値に変換できない文 字列の場合は0に変換されるため条件が真となり、全件のレコードが該当してしまうと思 われます。 ※以下の徳丸さんのブログに「暗黙の型変換」についてのわかりやすい解説があり参考 にさせていただきました。 「SQLインジェクションゴルフ - なんと3文字で認証回避が可能に」 http://blog.tokumaru.org/2013/06/sql-injection-golf-3-letters-bypass-login-authentication.html 「SQLの暗黙の型変換はワナがいっぱい」 http://www.tokumaru.org/d/20090924.html 'aaa@vultest.com'+'' ⇒ 0 + 0 ⇒ 0
  • アプリ側で生成されるSQL文について リマインダ機能で生成されるSQL文について下記のような推測をしました。 1. 入力値(メールアドレスと誕生日)の照合とパスワードの更新はそれぞれ別のSQL文だった のではないか。 2. SELECT文でなんらかの結果が返ってきた場合に、UPDATE文を実行するような感じだった のではないか。 3. UPDATE文のWHEHE句はSELECT文の実行結果を利用したわけではなく、リマインダで入力 したメールアドレスのパラメータ値が使われたのではないか。 SELECT * FROM member WHERE id = 'test@vultest.com' AND birthday = 1999523; UPDATE vultest SET password = 'リセットするパスワード値' WHERE id = 'test@vultest.com';  実行されるのはこんなSQL文だったのではないだろうか? 例えばリマインダ機能で下記のような値を入力した場合 メールドレス:test@vultest.com 誕生日:1999年5月23日 メールアドレスと誕生日の値が一致するユーザがいるか検索 該当するメールアドレスのユーザのパスワードを初期化
  • そうであればこうなる SELECT * FROM member WHERE id = 'test@vultest.com'+'' AND birthday = 1999523; UPDATE member SET password = 'リセットするパスワード値' WHERE id = 'test@vultest.com'+''; 下記値をリマインダ機能で入力した場合 メールドレス:test@vultest.com'+' 誕生日:1999年5月23日 カラム名「birthday」の値 が1999523に該当するユーザのレコードが検索結果として返る 結果、全ユーザのパスワードが初期化される  メールアドレスと誕生日の値が一致するユーザがいるか検索  該当するメールアドレスのユーザのパスワードを初期化 ※試しに同様の動きをするWebアプリを作ってみましたが、見事に再現されましたw
  • ちなみにこっちも注意 MySQLの場合には「'+'」だけではなく「'||'」も気をつけなくてはなら ない。 MySQLでは「||」は論理和として解釈されてしまい、なおかつ同様 に暗黙の型変換が行われるためこちらもMySQLを対象とした診 断では危険なので使わないほうが良い。 ※ 実はこの二つのシグネチャの危険性については下記のブログで随分前に指摘されていたの にとある診断員はこの件が起こった後に気づきました。。。(読んでたのにorz...)SQLイン ジェクションの検出方法についてすごく詳細な記載があるので興味がある方は必見です! T.Teradaの日記 自作検査ツール - SQLインジェクション編 http://d.hatena.ne.jp/teracc/20090531 また、またまたabentさんの「Webアプリって奥が深いんです。」でMySQL での「||」の危険性に ついて詳しく検証されていますので詳しくはそちらを参照ください。 「Webアプリって奥が深いんです。」 http://www.slideshare.net/abend_cve_9999_0001/web-22186183
  • まとめ MySQLの暗黙の型変換は怖い セキュリティ診断に限ることではないけどやっぱり事前 の調整って重要だよね! そもそもSQLインジェクションの脆弱性がなければこん な悲劇は起こらなかったわけなのでちゃんと対策しま しょう。 とある診断員の戦いはその後も続く