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

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

## 163 statements

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#

18 import struct

19 import random

20 import string

21 from six import b

22

23 from Cryptodome.Hash import HMAC, MD5

24 from Cryptodome.Cipher import ARC4

25

26 from impacket.structure import Structure

27 from impacket.krb5 import constants, crypto

28

29# Our random number generator

30 try:

31 rand = random.SystemRandom()

32 except NotImplementedError:

33 rand = random

34 pass

35

36# Constants

37 GSS_C_DCE_STYLE = 0x1000

38 GSS_C_DELEG_FLAG = 1

39 GSS_C_MUTUAL_FLAG = 2

40 GSS_C_REPLAY_FLAG = 4

41 GSS_C_SEQUENCE_FLAG = 8

42 GSS_C_CONF_FLAG = 0x10

43 GSS_C_INTEG_FLAG = 0x20

44

45# Mic Semantics

46 GSS_HMAC = 0x11

47# Wrap Semantics

48 GSS_RC4 = 0x10

49

50# 2. Key Derivation for Per-Message Tokens

51 KG_USAGE_ACCEPTOR_SEAL = 22

52 KG_USAGE_ACCEPTOR_SIGN = 23

53 KG_USAGE_INITIATOR_SEAL = 24

54 KG_USAGE_INITIATOR_SIGN = 25

55

56 KRB5_AP_REQ = struct.pack('<H', 0x1)

57

58# 1.1.1. Initial Token - Checksum field

59 class CheckSumField(Structure):

60 structure = (

61 ('Lgth','<L=16'),

62 ('Bnd','16s=b""'),

63 ('Flags','<L=0'),

64 )

65

66 def 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)

75

76# 7.2. GSS-API MIC Semantics

77 class 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 )

87

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 )

99

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()

103

104 # Let's pad the data

105 pad = (4 - (len(data) % 4)) & 0x3

106 padStr = b(chr(pad)) * pad

107 data += padStr

108

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

114

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]

119

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

125

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()

133

134 # Let's pad the data

135 pad = (8 - (len(data) % 8)) & 0x7

136 padStr = b(chr(pad)) * pad

137 data += padStr

138

139 token['SGN_ALG'] = GSS_HMAC

140 token['SEAL_ALG'] = GSS_RC4

141

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

146

147 # Random confounder :)

148 token['Confounder'] = b(''.join([rand.choice(string.ascii_letters) for _ in range(8)]))

149

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()

152

153 Klocal = bytearray()

154 from builtins import bytes

155 for n in bytes(sessionKey.contents):

156 Klocal.append( n ^ 0xF0)

157

158 Kcrypt = HMAC.new(Klocal,struct.pack('<L',0), MD5).digest()

159 Kcrypt = HMAC.new(Kcrypt,struct.pack('>L', sequenceNumber), MD5).digest()

160

161 Sgn_Cksum = HMAC.new(Ksign, Sgn_Cksum, MD5).digest()

162

163 token['SGN_CKSUM'] = Sgn_Cksum[:8]

164

165 Kseq = HMAC.new(sessionKey.contents, struct.pack('<L',0), MD5).digest()

166 Kseq = HMAC.new(Kseq, token['SGN_CKSUM'], MD5).digest()

167

168 token['SND_SEQ'] = ARC4.new(Kseq).encrypt(token['SND_SEQ'])

169

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']

174

175 Kseq = HMAC.new(sessionKey.contents, struct.pack('<L',0), MD5).digest()

176 Kseq = HMAC.new(Kseq, wrap['SGN_CKSUM'], MD5).digest()

177

178 snd_seq = ARC4.new(Kseq).encrypt(wrap['SND_SEQ'])

179

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

190

191 finalData = GSS_WRAP_HEADER + token.getData()

192 return cipherText, finalData

193

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)

196

197 class GSSAPI_AES():

198 checkSumProfile = None

199 cipherType = None

200

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 )

210

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 )

221

222 def GSS_GetMIC(self, sessionKey, data, sequenceNumber, direction = 'init'):

223 token = self.MIC()

224

225 # Let's pad the data

226 pad = (4 - (len(data) % 4)) & 0x3

227 padStr = chr(pad) * pad

228 data += padStr

229

230 checkSumProfile = self.checkSumProfile()

231

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])

235

236 return token.getData()

237

238 def rotate(self, data, numBytes):

239 numBytes %= len(data)

240 left = len(data) - numBytes

241 result = data[left:] + data[:left]

242 return result

243

244 def unrotate(self, data, numBytes):

245 numBytes %= len(data)

246 result = data[numBytes:] + data[:numBytes]

247 return result

248

249 def GSS_Wrap(self, sessionKey, data, sequenceNumber, direction = 'init', encrypt=True):

250 token = self.WRAP()

251

252 cipher = self.cipherType()

253

254 # Let's pad the data

255 pad = (cipher.blocksize - (len(data) % cipher.blocksize)) & 15

256 padStr = b'\xFF' * pad

257 data += padStr

258

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

262

263 token['Flags'] = 6

264 token['EC'] = pad

265 token['RRC'] = 0

266 token['SND_SEQ'] = struct.pack('>Q',sequenceNumber)

267

268 cipherText = cipher.encrypt(sessionKey, KG_USAGE_INITIATOR_SEAL, data + token.getData(), None)

269 token['RRC'] = rrc

270

271 cipherText = self.rotate(cipherText, token['RRC'] + token['EC'])

272

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']]

276

277 return ret1, ret2

278

279 def GSS_Unwrap(self, sessionKey, data, sequenceNumber, direction = 'init', encrypt=True, authData=None):

280 from impacket.dcerpc.v5.rpcrt import SEC_TRAILER

281

282 cipher = self.cipherType()

283 token = self.WRAP(authData[len(SEC_TRAILER()):])

284

285 rotated = authData[len(self.WRAP())+len(SEC_TRAILER()):] + data

286

287 cipherText = self.unrotate(rotated, token['RRC'] + token['EC'])

288 plainText = cipher.decrypt(sessionKey, KG_USAGE_ACCEPTOR_SEAL, cipherText)

289

290 return plainText[:-(token['EC']+len(self.WRAP()))], None

291

292 class GSSAPI_AES256(GSSAPI_AES):

293 checkSumProfile = crypto._SHA1AES256

294 cipherType = crypto._AES256CTS

295

296 class GSSAPI_AES128(GSSAPI_AES):

297 checkSumProfile = crypto._SHA1AES128

298 cipherType = crypto._AES128CTS