はまやんはまやんはまやん

hamayanhamayan's blog

SQL Truncation [SQL Column Truncation Vulnerabilities]

この前のFacebook CTFで出てたし、まとめておく。

起きる現象1

MySQLで起こる。
わかりやすい例で言うとこんな感じ。

mysql> select * from users where username = 'admin';
Empty set (0.00 sec)

mysql> insert into users values('admin', 'pass');
Query OK, 1 row affected (0.01 sec)

mysql> select * from users where username = 'admin';
+----------+----------+
| username | password |
+----------+----------+
| admin    | pass     |
+----------+----------+
1 row in set (0.00 sec)

mysql> insert into users values('admin ', 'pass2');
Query OK, 1 row affected (0.02 sec)

mysql> select * from users where username = 'admin';
+----------+----------+
| username | password |
+----------+----------+
| admin    | pass     |
| admin    | pass2    |
+----------+----------+
2 rows in set (0.00 sec)

selectのwhereで検索するときに末尾に空白が入っていても無視して取ってきてしまう。
つまり、何らかの方法で

  • ユーザID「攻撃したいユーザID+空白」
  • パスワード「好きに決定」

を入れることができれば、ログイン時にユーザID「攻撃したいユーザID」パスワード「好きに決定」でログインができてしまう。

起きる現象2

後ろの空白を消して入れるみたいな処理を入れてやれば良さそう。
だけど、カラムのサイズの上限で切り捨てされるというトリックを使えば、無理矢理空白で終わるユーザー名をねじ込める。
これを使えば、「admin a」で重複チェックをすり抜けて、実際は「admin 」を入れるということもできる。
つまり、チェック時の文字列と実際に入る文字列を変えることができる。

mysql> select * from users where username = 'admin';
Empty set (0.00 sec)

mysql> insert into users values('admin       a', 'pass');
Query OK, 1 row affected, 1 warning (0.04 sec)

mysql> select * from users where username = 'admin';
+------------+----------+
| username   | password |
+------------+----------+
| admin      | pass     |
+------------+----------+
1 row in set (0.00 sec)

軽減策

  • 徳丸さん記事
    • 【おすすめ!】SQL文に入れる前に長さチェックをする
    • MySQLをやめる
    • MySQLsql_modeをstrictモードにする
    • 【おすすめ!】username列に一意制約を指定する
      • 一意制約があると、後ろに空白があり、whereで同じであると判定されるものでも弾いてくれる