-
Notifications
You must be signed in to change notification settings - Fork 10
/
eap.py
executable file
·349 lines (283 loc) · 13.7 KB
/
eap.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
#!/usr/bin/env python3
# A Generic MSCHAPv2/MPPE/WPA implementation
# By @singe (research@sensepost.com)
# Used to help understand how PEAP works
# RADIUS server is the Authenticator
# Client is the Station or Peer
from Crypto.Cipher import DES
from Crypto.Cipher import ARC4
from hmac import new as hmac_new
from hashlib import pbkdf2_hmac, sha1, md5, new as hashlib_new
class MSCHAPV2:
# Generic MSCHAP/MPEE implementation taken from:
# https://tools.ietf.org/html/rfc2759#section-8
# https://tools.ietf.org/html/rfc3078
# https://tools.ietf.org/html/rfc3079
def __init__(self, UserName, Password, AuthenticatorChallenge, PeerChallenge, KeyLength=128):
self.UserName = UserName
self.Password = Password
self.AuthenticatorChallenge = AuthenticatorChallenge
self.PeerChallenge = PeerChallenge
self.KeyLength = KeyLength
def ChallengeHash(self, PeerChallenge, AuthenticatorChallenge, UserName):
# Calculate the Challenge both sides(AP + Client) will use
# This is the challenge part asleap/JtR/hashcat will use to crack
if len(UserName.split(b'\\')) > 1:
UserName = UserName.split(b'\\')[1] #Strip DOMAIN\ from front if present
# Calculate SHA1 hash of Peer+Authenticator+UserName
sha = sha1()
sha.update(PeerChallenge)
sha.update(AuthenticatorChallenge)
sha.update(UserName)
Challenge = sha.digest()
# Return first 8 bytes of challenge as hex
Challenge = Challenge[0:8]
return Challenge
def NtPasswordHash(self, Password):
# MD4 the password with right encoding
Password = Password.encode('utf-16le')
md4 = hashlib_new('md4')
md4.update(Password)
PasswordHash = md4.digest()
return PasswordHash
def HashNtPasswordHash(self, PasswordHash):
# Generate the double hash'ed Hash
md4 = hashlib_new('md4')
md4.update(PasswordHash)
PasswordHashHash = md4.digest()
return PasswordHashHash
# Copied from https://github.com/SecureAuthCorp/impacket/blob/1c21a460ae1f8d20e7c35c2d4b123800472feeb3/impacket/ntlm.py#L534
def __expand_DES_key(self, key):
# Expand the key from a 7-byte password key into a 8-byte DES key
key = key[:7]
key += bytearray(7-len(key))
s = bytearray()
s.append(((key[0] >> 1) & 0x7f) << 1)
s.append(((key[0] & 0x01) << 6 | ((key[1] >> 2) & 0x3f)) << 1)
s.append(((key[1] & 0x03) << 5 | ((key[2] >> 3) & 0x1f)) << 1)
s.append(((key[2] & 0x07) << 4 | ((key[3] >> 4) & 0x0f)) << 1)
s.append(((key[3] & 0x0f) << 3 | ((key[4] >> 5) & 0x07)) << 1)
s.append(((key[4] & 0x1f) << 2 | ((key[5] >> 6) & 0x03)) << 1)
s.append(((key[5] & 0x3f) << 1 | ((key[6] >> 7) & 0x01)) << 1)
s.append((key[6] & 0x7f) << 1)
return bytes(s)
def ChallengeResponse(self, Challenge, PasswordHash):
# Generate the NTResponse the client sends the AP
# This is the response part asleap/JtR/hashcat crack
ZPasswordHash = PasswordHash+b'\x00\x00\x00\x00\x00'
des = DES.new(self.__expand_DES_key(ZPasswordHash[0:7]),DES.MODE_ECB)
one = des.encrypt(Challenge)
des = DES.new(self.__expand_DES_key(ZPasswordHash[7:14]),DES.MODE_ECB)
two = des.encrypt(Challenge)
des = DES.new(self.__expand_DES_key(ZPasswordHash[14:21]),DES.MODE_ECB)
tre = des.encrypt(Challenge)
Response = one+two+tre
return Response
def GenerateAuthenticatorResponse(self, Password, NTResponse, PeerChallenge, AuthenticatorChallenge, UserName):
# Create the response the AP sends to the Client to prove it knows the password too
# Defined in https://tools.ietf.org/html/rfc2759#section-8
Magic1 = b'\x4D\x61\x67\x69\x63\x20\x73\x65\x72\x76\x65\x72\x20\x74\x6F\x20\x63\x6C\x69\x65\x6E\x74\x20\x73\x69\x67\x6E\x69\x6E\x67\x20\x63\x6F\x6E\x73\x74\x61\x6E\x74' #39 bytes
Magic2 = b'\x50\x61\x64\x20\x74\x6F\x20\x6D\x61\x6B\x65\x20\x69\x74\x20\x64\x6F\x20\x6D\x6F\x72\x65\x20\x74\x68\x61\x6E\x20\x6F\x6E\x65\x20\x69\x74\x65\x72\x61\x74\x69\x6F\x6E' #41 bytes
PasswordHash = self.NtPasswordHash(Password)
PasswordHashHash = self.HashNtPasswordHash(PasswordHash)
Challenge = self.ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName)
sha = sha1()
sha.update(PasswordHashHash)
sha.update(NTResponse)
sha.update(Magic1)
Digest = sha.digest()
sha = sha1()
sha.update(Digest)
sha.update(Challenge)
sha.update(Magic2)
AuthenticatorResponse = sha.digest()
AuthenticatorResponse = "S="+AuthenticatorResponse.hex().upper()
return AuthenticatorResponse
def GetMasterKey(self, PasswordHashHash, NTResponse):
# Generate Master Key used to derive PMK part of MPPE not MSCHAP
# https://tools.ietf.org/html/rfc3079#section-3.4
# Taken from RFC 3079
Magic1 = b'\x54\x68\x69\x73\x20\x69\x73\x20\x74\x68\x65\x20\x4d\x50\x50\x45\x20\x4d\x61\x73\x74\x65\x72\x20\x4b\x65\x79' #27 bytes
sha = sha1()
sha.update(PasswordHashHash)
sha.update(NTResponse)
sha.update(Magic1)
MasterKey = sha.digest()
return MasterKey[:16]
def GetAsymetricStartKey(self, MasterKey, SessionKeyLength, IsSend, IsServer):
# Generate MS-MPEE-Send/Recv-Key
# From https://tools.ietf.org/html/rfc3079#section-3.4
# IsSend & IsServer == True - master send session key
Magic2 = b'\x4f\x6e\x20\x74\x68\x65\x20\x63\x6c\x69\x65\x6e\x74\x20\x73\x69\x64\x65\x2c\x20\x74\x68\x69\x73\x20\x69\x73\x20\x74\x68\x65\x20\x73\x65\x6e\x64\x20\x6b\x65\x79\x3b\x20\x6f\x6e\x20\x74\x68\x65\x20\x73\x65\x72\x76\x65\x72\x20\x73\x69\x64\x65\x2c\x20\x69\x74\x20\x69\x73\x20\x74\x68\x65\x20\x72\x65\x63\x65\x69\x76\x65\x20\x6b\x65\x79\x2e' #84 Bytes
Magic3 = b'\x4f\x6e\x20\x74\x68\x65\x20\x63\x6c\x69\x65\x6e\x74\x20\x73\x69\x64\x65\x2c\x20\x74\x68\x69\x73\x20\x69\x73\x20\x74\x68\x65\x20\x72\x65\x63\x65\x69\x76\x65\x20\x6b\x65\x79\x3b\x20\x6f\x6e\x20\x74\x68\x65\x20\x73\x65\x72\x76\x65\x72\x20\x73\x69\x64\x65\x2c\x20\x69\x74\x20\x69\x73\x20\x74\x68\x65\x20\x73\x65\x6e\x64\x20\x6b\x65\x79\x2e' #84 Bytes
SHSpad1 = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #40 Bytes
SHSpad2 = b'\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2' #40 Bytes
if IsSend:
if IsServer:
s = Magic3
else:
s = Magic2
else:
if IsServer:
s = Magic2
else:
s = Magic3
sha = sha1()
sha.update(MasterKey)
sha.update(SHSpad1)
sha.update(s)
sha.update(SHSpad2)
SessionKey = sha.digest()
return SessionKey[:SessionKeyLength]
def GetNewKeyFromSHA(self, StartKey, SessionKey, SessionKeyLength):
# Generate the initial send session key MPEE
# https://tools.ietf.org/html/rfc3078 Section 7.3
SHApad1 = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' #40 Bytes
SHApad2 = b'\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2\xf2' #40 Bytes
sha = sha1()
sha.update(StartKey[:SessionKeyLength])
sha.update(SHApad1)
sha.update(SessionKey[:SessionKeyLength])
sha.update(SHApad2)
InterimKey = sha.digest()
return InterimKey[:SessionKeyLength]
def ReduceSessionKey(self, SendSessionKey, KeyLength):
# Reduce key size appropriately
# https://tools.ietf.org/html/rfc3079#section-3.1 3.2 & 3.3
if KeyLength == 40:
return b'\xd1\x26\x9e'+SendSessionKey[3:]
if KeyLength == 56:
return b'\xd1'+SendSessionKey[1:]
if KeyLength == 128 or KeyLength == 256:
return SendSessionKey
def rc4_key(self, SessionKey):
# Initialise RC4 tables with Session key
# https://tools.ietf.org/html/rfc3079#section-3.3
rc4 = ARC4.new(SessionKey)
return rc4
def Run(self):
PasswordHash = self.NtPasswordHash(self.Password)
PasswordHashHash = self.HashNtPasswordHash(PasswordHash)
Challenge = self.ChallengeHash(self.PeerChallenge, self.AuthenticatorChallenge, self.UserName)
NTResponse = self.ChallengeResponse(Challenge, PasswordHash)
MasterKey = self.GetMasterKey(PasswordHashHash, NTResponse)
AuthenticatorResponse = self.GenerateAuthenticatorResponse(self.Password, NTResponse, self.PeerChallenge, self.AuthenticatorChallenge, self.UserName)
if self.KeyLength == 40 or self.KeyLength == 56:
length = 8
elif self.KeyLength == 128:
length = 16
elif self.KeyLength == 256:
length = 32
MasterSendKey = self.GetAsymetricStartKey(MasterKey, length, True, True)
MasterReceiveKey = self.GetAsymetricStartKey(MasterKey, length, False, True)
SendSessionKey = self.GetNewKeyFromSHA(MasterSendKey, MasterSendKey, length)
ReceiveSessionKey = self.GetNewKeyFromSHA(MasterReceiveKey, MasterReceiveKey, length)
SendSessionKey = self.ReduceSessionKey(SendSessionKey, self.KeyLength)
ReceiveSessionKey = self.ReduceSessionKey(ReceiveSessionKey, self.KeyLength)
SendRC4 = self.rc4_key(SendSessionKey)
ReceiveRC4 = self.rc4_key(ReceiveSessionKey)
print('UserName: '+self.UserName.hex())
print('Password: '+self.Password)
print('AuthenticatorChallenge: '+self.AuthenticatorChallenge.hex())
print('PeerChallenge: '+self.PeerChallenge.hex())
print('Challenge: '+Challenge.hex())
print('NTResponse: '+NTResponse.hex())
print('PasswordHash: '+PasswordHash.hex())
print('PasswordHashHash: '+PasswordHashHash.hex())
print('MasterKey: '+MasterKey.hex()+' (EAP-MSCHAPV2: Derived Master Key)')
print('MasterSendKey: '+MasterSendKey.hex())
print('MasterReceiveKey: '+MasterReceiveKey.hex())
print('EAP-MSCHAPV2: Derived key: '+MasterReceiveKey.hex()+MasterSendKey.hex())
print('SendSessionKey: '+SendSessionKey.hex())
print('ReceiveSessionKey: '+ReceiveSessionKey.hex())
print('Send RC4(test messages): '+SendRC4.encrypt('test message').hex())
print('Receive RC4(test messages): '+ReceiveRC4.encrypt('test message').hex())
self.Challenge = Challenge
self.NTResponse = NTResponse
self.AuthenticatorResponse = AuthenticatorResponse
self.PasswordHash = PasswordHash
self.PasswordHashHash = PasswordHashHash
self.MasterKey = MasterKey
self.MasterSendKey = MasterSendKey
self.MasterReceiveKey = MasterReceiveKey
self.SendSessionKey = SendSessionKey
self.ReceiveSessionKey = ReceiveSessionKey
self.SendRC4 = SendRC4
self.ReceiveRC4 = ReceiveRC4
return self.Challenge, self.NTResponse, self.AuthenticatorResponse, self.PasswordHash, self.PasswordHashHash, self.MasterKey, self.MasterSendKey, self.MasterReceiveKey, self.SendSessionKey, self.ReceiveSessionKey, self.SendRC4, self.ReceiveRC4
class WPA:
# Generic WPA/2 implementation
# Original source https://nicholastsmith.wordpress.com/2016/11/15/wpa2-key-derivation-with-anaconda-python/
def __init__(self, password, ssid, aNonce, sNonce, apMac, staMac):
self.password = password
self.ssid = ssid
self.aNonce = aNonce
self.sNonce = sNonce
self.apMac = apMac
self.staMac = staMac
def __init__(self, PMK, aNonce, sNonce, apMac, staMac):
self.pmk = PMK
self.aNonce = aNonce
self.sNonce = sNonce
self.apMac = apMac
self.staMac = staMac
def MakePMK(self, pwd, ssid):
#Create the pairwise master key using 4096 iterations of hmac-sha1
#to generate a 32 byte value
pmk = pbkdf2_hmac('sha1', pwd.encode('ascii'), ssid.encode('ascii'), 4096, 32)
return pmk
def MakeAB(self, aNonce, sNonce, apMac, staMac):
#Make parameters for the generation of the PTK
#aNonce: The aNonce from the 4-way handshake
#sNonce: The sNonce from the 4-way handshake
#apMac: The MAC address of the access point
#staMac: The MAC address of the client
#return: (A, B) where A and B are parameters for the generation of the PTK
A = b"Pairwise key expansion"
B = min(apMac, staMac) + max(apMac, staMac) + min(aNonce, sNonce) + max(aNonce, sNonce)
return (A, B)
def MakePTK(self, pmk, A, B):
#Pseudo-random function for generation of
#the pairwise transient key (PTK)
#key: The PMK
#A: b'Pairwise key expansion'
#B: The apMac, staMac, aNonce, and sNonce concatenated
# like mac1 mac2 nonce1 nonce2
# such that mac1 < mac2 and nonce1 < nonce2
#return: The ptk
#Number of bytes in the PTK
nByte = 48
i = 0
R = b''
#Each iteration produces 160-bit value and 512 bits are required
while(i <= ((nByte * 8 + 159) / 160)):
hmacsha1 = hmac_new(pmk, A + chr(0x00).encode() + B + chr(i).encode(), sha1)
R = R + hmacsha1.digest()
i += 1
return R[0:nByte]
def MakeMIC(self, ptk, data, wpa = False):
#Compute the 1st message integrity check for a WPA 4-way handshake
#ptk: The Pairwise Transient Key
#data: A list of 802.1x frames with the MIC field zeroed
#WPA uses md5 to compute the MIC while WPA2 uses sha1
hmacFunc = md5 if wpa else sha1
#Create the MICs using HMAC-SHA1 of data and return all computed values
mic = hmac_new(ptk[0:16], data, hmacFunc).digest()
return mic[:16]
def Run(self, data, wpa = False):
A,B = self.MakeAB(self.aNonce, self.sNonce, self.apMac, self.staMac)
if hasattr(self, 'password'):
PMK = self.MakePMK(self.password, self.ssid)
else:
PMK = self.pmk
PTK = self.MakePTK(PMK, A, B)
MIC = self.MakeMIC(PTK, data, wpa)
print('PMK: '+PMK.hex())
print('PTK: '+PTK.hex())
print('MIC: '+MIC.hex())
self.A = A
self.B = B
self.PMK = PMK
self.PTK = PTK
self.MIC = MIC
return A, B, PMK, PTK, MIC