Coverage for /root/GitHubProjects/impacket/impacket/krb5/gssapi.py : 70%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
2#
3# This software is provided under under a slightly modified version
4# of the Apache Software License. See the accompanying LICENSE file
5# for more information.
6#
7# Author: Alberto Solino (@agsolino)
8#
9# Description:
10# RFC 1964 Partial Implementation
11# RFC 4757 Partial Implementation
12# RFC 4121 Partial Implementation
13# RFC 3962 Partial Implementation
15import struct
16import random
17import string
18from six import b
20from Cryptodome.Hash import HMAC, MD5
21from Cryptodome.Cipher import ARC4
23from impacket.structure import Structure
24from impacket.krb5 import constants, crypto
26# Our random number generator
27try:
28 rand = random.SystemRandom()
29except NotImplementedError:
30 rand = random
31 pass
33# Constants
34GSS_C_DCE_STYLE = 0x1000
35GSS_C_DELEG_FLAG = 1
36GSS_C_MUTUAL_FLAG = 2
37GSS_C_REPLAY_FLAG = 4
38GSS_C_SEQUENCE_FLAG = 8
39GSS_C_CONF_FLAG = 0x10
40GSS_C_INTEG_FLAG = 0x20
42# Mic Semantics
43GSS_HMAC = 0x11
44# Wrap Semantics
45GSS_RC4 = 0x10
47# 2. Key Derivation for Per-Message Tokens
48KG_USAGE_ACCEPTOR_SEAL = 22
49KG_USAGE_ACCEPTOR_SIGN = 23
50KG_USAGE_INITIATOR_SEAL = 24
51KG_USAGE_INITIATOR_SIGN = 25
53KRB5_AP_REQ = struct.pack('<H', 0x1)
55# 1.1.1. Initial Token - Checksum field
56class CheckSumField(Structure):
57 structure = (
58 ('Lgth','<L=16'),
59 ('Bnd','16s=b""'),
60 ('Flags','<L=0'),
61 )
63def GSSAPI(cipher):
64 if cipher.enctype == constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value: 64 ↛ 65line 64 didn't jump to line 65, because the condition on line 64 was never true
65 return GSSAPI_AES256()
66 if cipher.enctype == constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value: 66 ↛ 67line 66 didn't jump to line 67, because the condition on line 66 was never true
67 return GSSAPI_AES128()
68 elif cipher.enctype == constants.EncryptionTypes.rc4_hmac.value: 68 ↛ 71line 68 didn't jump to line 71, because the condition on line 68 was never false
69 return GSSAPI_RC4()
70 else:
71 raise Exception('Unsupported etype 0x%x' % cipher.enctype)
73# 7.2. GSS-API MIC Semantics
74class GSSAPI_RC4:
75 # 1.2.1. Per-message Tokens - MIC
76 class MIC(Structure):
77 structure = (
78 ('TOK_ID','<H=0x0101'),
79 ('SGN_ALG','<H=0'),
80 ('Filler','<L=0xffffffff'),
81 ('SND_SEQ','8s=b""'),
82 ('SGN_CKSUM','8s=b""'),
83 )
85 # 1.2.2. Per-message Tokens - Wrap
86 class WRAP(Structure):
87 structure = (
88 ('TOK_ID','<H=0x0102'),
89 ('SGN_ALG','<H=0'),
90 ('SEAL_ALG','<H=0'),
91 ('Filler','<H=0xffff'),
92 ('SND_SEQ','8s=b""'),
93 ('SGN_CKSUM','8s=b""'),
94 ('Confounder','8s=b""'),
95 )
97 def GSS_GetMIC(self, sessionKey, data, sequenceNumber, direction = 'init'):
98 GSS_GETMIC_HEADER = b'\x60\x23\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02'
99 token = self.MIC()
101 # Let's pad the data
102 pad = (4 - (len(data) % 4)) & 0x3
103 padStr = b(chr(pad)) * pad
104 data += padStr
106 token['SGN_ALG'] = GSS_HMAC
107 if direction == 'init': 107 ↛ 110line 107 didn't jump to line 110, because the condition on line 107 was never false
108 token['SND_SEQ'] = struct.pack('>L', sequenceNumber) + b'\x00'*4
109 else:
110 token['SND_SEQ'] = struct.pack('>L', sequenceNumber) + b'\xff'*4
112 Ksign = HMAC.new(sessionKey.contents, b'signaturekey\0', MD5).digest()
113 Sgn_Cksum = MD5.new( struct.pack('<L',15) + token.getData()[:8] + data).digest()
114 Sgn_Cksum = HMAC.new(Ksign, Sgn_Cksum, MD5).digest()
115 token['SGN_CKSUM'] = Sgn_Cksum[:8]
117 Kseq = HMAC.new(sessionKey.contents, struct.pack('<L',0), MD5).digest()
118 Kseq = HMAC.new(Kseq, token['SGN_CKSUM'], MD5).digest()
119 token['SND_SEQ'] = ARC4.new(Kseq).encrypt(token['SND_SEQ'])
120 finalData = GSS_GETMIC_HEADER + token.getData()
121 return finalData
123 def GSS_Wrap(self, sessionKey, data, sequenceNumber, direction = 'init', encrypt=True, authData=None):
124 # Damn inacurate RFC, useful info from here
125 # https://social.msdn.microsoft.com/Forums/en-US/fb98e8f4-e697-4652-bcb7-604e027e14cc/gsswrap-token-size-kerberos-and-rc4hmac?forum=os_windowsprotocols
126 # and here
127 # http://www.rfc-editor.org/errata_search.php?rfc=4757
128 GSS_WRAP_HEADER = b'\x60\x2b\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02'
129 token = self.WRAP()
131 # Let's pad the data
132 pad = (8 - (len(data) % 8)) & 0x7
133 padStr = b(chr(pad)) * pad
134 data += padStr
136 token['SGN_ALG'] = GSS_HMAC
137 token['SEAL_ALG'] = GSS_RC4
139 if direction == 'init': 139 ↛ 142line 139 didn't jump to line 142, because the condition on line 139 was never false
140 token['SND_SEQ'] = struct.pack('>L', sequenceNumber) + b'\x00'*4
141 else:
142 token['SND_SEQ'] = struct.pack('>L', sequenceNumber) + b'\xff'*4
144 # Random confounder :)
145 token['Confounder'] = b(''.join([rand.choice(string.ascii_letters) for _ in range(8)]))
147 Ksign = HMAC.new(sessionKey.contents, b'signaturekey\0', MD5).digest()
148 Sgn_Cksum = MD5.new(struct.pack('<L',13) + token.getData()[:8] + token['Confounder'] + data).digest()
150 Klocal = bytearray()
151 from builtins import bytes
152 for n in bytes(sessionKey.contents):
153 Klocal.append( n ^ 0xF0)
155 Kcrypt = HMAC.new(Klocal,struct.pack('<L',0), MD5).digest()
156 Kcrypt = HMAC.new(Kcrypt,struct.pack('>L', sequenceNumber), MD5).digest()
158 Sgn_Cksum = HMAC.new(Ksign, Sgn_Cksum, MD5).digest()
160 token['SGN_CKSUM'] = Sgn_Cksum[:8]
162 Kseq = HMAC.new(sessionKey.contents, struct.pack('<L',0), MD5).digest()
163 Kseq = HMAC.new(Kseq, token['SGN_CKSUM'], MD5).digest()
165 token['SND_SEQ'] = ARC4.new(Kseq).encrypt(token['SND_SEQ'])
167 if authData is not None:
168 from impacket.dcerpc.v5.rpcrt import SEC_TRAILER
169 wrap = self.WRAP(authData[len(SEC_TRAILER()) + len(GSS_WRAP_HEADER):])
170 snd_seq = wrap['SND_SEQ']
172 Kseq = HMAC.new(sessionKey.contents, struct.pack('<L',0), MD5).digest()
173 Kseq = HMAC.new(Kseq, wrap['SGN_CKSUM'], MD5).digest()
175 snd_seq = ARC4.new(Kseq).encrypt(wrap['SND_SEQ'])
177 Kcrypt = HMAC.new(Klocal,struct.pack('<L',0), MD5).digest()
178 Kcrypt = HMAC.new(Kcrypt,snd_seq[:4], MD5).digest()
179 rc4 = ARC4.new(Kcrypt)
180 cipherText = rc4.decrypt(token['Confounder'] + data)[8:]
181 elif encrypt is True: 181 ↛ 186line 181 didn't jump to line 186, because the condition on line 181 was never false
182 rc4 = ARC4.new(Kcrypt)
183 token['Confounder'] = rc4.encrypt(token['Confounder'])
184 cipherText = rc4.encrypt(data)
185 else:
186 cipherText = data
188 finalData = GSS_WRAP_HEADER + token.getData()
189 return cipherText, finalData
191 def GSS_Unwrap(self, sessionKey, data, sequenceNumber, direction = 'init', encrypt=True, authData=None):
192 return self.GSS_Wrap(sessionKey, data, sequenceNumber, direction, encrypt, authData)
194class GSSAPI_AES():
195 checkSumProfile = None
196 cipherType = None
198 class MIC(Structure):
199 structure = (
200 ('TOK_ID','>H=0x0404'),
201 ('Flags','B=0'),
202 ('Filler0','B=0xff'),
203 ('Filler','>L=0xffffffff'),
204 ('SND_SEQ','8s=b""'),
205 ('SGN_CKSUM','12s=b""'),
206 )
208 # 1.2.2. Per-message Tokens - Wrap
209 class WRAP(Structure):
210 structure = (
211 ('TOK_ID','>H=0x0504'),
212 ('Flags','B=0'),
213 ('Filler','B=0xff'),
214 ('EC','>H=0'),
215 ('RRC','>H=0'),
216 ('SND_SEQ','8s=b""'),
217 )
219 def GSS_GetMIC(self, sessionKey, data, sequenceNumber, direction = 'init'):
220 token = self.MIC()
222 # Let's pad the data
223 pad = (4 - (len(data) % 4)) & 0x3
224 padStr = chr(pad) * pad
225 data += padStr
227 checkSumProfile = self.checkSumProfile()
229 token['Flags'] = 4
230 token['SND_SEQ'] = struct.pack('>Q',sequenceNumber)
231 token['SGN_CKSUM'] = checkSumProfile.checksum(sessionKey, KG_USAGE_INITIATOR_SIGN, data + token.getData()[:16])
233 return token.getData()
235 def rotate(self, data, numBytes):
236 numBytes %= len(data)
237 left = len(data) - numBytes
238 result = data[left:] + data[:left]
239 return result
241 def unrotate(self, data, numBytes):
242 numBytes %= len(data)
243 result = data[numBytes:] + data[:numBytes]
244 return result
246 def GSS_Wrap(self, sessionKey, data, sequenceNumber, direction = 'init', encrypt=True):
247 token = self.WRAP()
249 cipher = self.cipherType()
251 # Let's pad the data
252 pad = (cipher.blocksize - (len(data) % cipher.blocksize)) & 15
253 padStr = b'\xFF' * pad
254 data += padStr
256 # The RRC field ([RFC4121] section 4.2.5) is 12 if no encryption is requested or 28 if encryption
257 # is requested. The RRC field is chosen such that all the data can be encrypted in place.
258 rrc = 28
260 token['Flags'] = 6
261 token['EC'] = pad
262 token['RRC'] = 0
263 token['SND_SEQ'] = struct.pack('>Q',sequenceNumber)
265 cipherText = cipher.encrypt(sessionKey, KG_USAGE_INITIATOR_SEAL, data + token.getData(), None)
266 token['RRC'] = rrc
268 cipherText = self.rotate(cipherText, token['RRC'] + token['EC'])
270 #nn = self.unrotate(cipherText, token['RRC'] + token['EC'])
271 ret1 = cipherText[len(self.WRAP()) + token['RRC'] + token['EC']:]
272 ret2 = token.getData() + cipherText[:len(self.WRAP()) + token['RRC'] + token['EC']]
274 return ret1, ret2
276 def GSS_Unwrap(self, sessionKey, data, sequenceNumber, direction = 'init', encrypt=True, authData=None):
277 from impacket.dcerpc.v5.rpcrt import SEC_TRAILER
279 cipher = self.cipherType()
280 token = self.WRAP(authData[len(SEC_TRAILER()):])
282 rotated = authData[len(self.WRAP())+len(SEC_TRAILER()):] + data
284 cipherText = self.unrotate(rotated, token['RRC'] + token['EC'])
285 plainText = cipher.decrypt(sessionKey, KG_USAGE_ACCEPTOR_SEAL, cipherText)
287 return plainText[:-(token['EC']+len(self.WRAP()))], None
289class GSSAPI_AES256(GSSAPI_AES):
290 checkSumProfile = crypto._SHA1AES256
291 cipherType = crypto._AES256CTS
293class GSSAPI_AES128(GSSAPI_AES):
294 checkSumProfile = crypto._SHA1AES128
295 cipherType = crypto._AES128CTS