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