Hide keyboard shortcuts

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

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

# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved. 

# 

# This software is provided under under a slightly modified version 

# of the Apache Software License. See the accompanying LICENSE file 

# for more information. 

# 

# Author: Alberto Solino (@agsolino) 

# 

# Description: 

# RFC 1964 Partial Implementation 

# RFC 4757 Partial Implementation 

# RFC 4121 Partial Implementation 

# RFC 3962 Partial Implementation 

 

import struct 

import random 

import string 

from six import b 

 

from Cryptodome.Hash import HMAC, MD5 

from Cryptodome.Cipher import ARC4 

 

from impacket.structure import Structure 

from impacket.krb5 import constants, crypto 

 

# Our random number generator 

try: 

rand = random.SystemRandom() 

except NotImplementedError: 

rand = random 

pass 

 

# Constants 

GSS_C_DCE_STYLE = 0x1000 

GSS_C_DELEG_FLAG = 1 

GSS_C_MUTUAL_FLAG = 2 

GSS_C_REPLAY_FLAG = 4 

GSS_C_SEQUENCE_FLAG = 8 

GSS_C_CONF_FLAG = 0x10 

GSS_C_INTEG_FLAG = 0x20 

 

# Mic Semantics 

GSS_HMAC = 0x11 

# Wrap Semantics 

GSS_RC4 = 0x10 

 

# 2. Key Derivation for Per-Message Tokens 

KG_USAGE_ACCEPTOR_SEAL = 22 

KG_USAGE_ACCEPTOR_SIGN = 23 

KG_USAGE_INITIATOR_SEAL = 24 

KG_USAGE_INITIATOR_SIGN = 25 

 

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

 

# 1.1.1. Initial Token - Checksum field 

class CheckSumField(Structure): 

structure = ( 

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

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

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

) 

 

def GSSAPI(cipher): 

64 ↛ 65line 64 didn't jump to line 65, because the condition on line 64 was never true if cipher.enctype == constants.EncryptionTypes.aes256_cts_hmac_sha1_96.value: 

return GSSAPI_AES256() 

66 ↛ 67line 66 didn't jump to line 67, because the condition on line 66 was never true if cipher.enctype == constants.EncryptionTypes.aes128_cts_hmac_sha1_96.value: 

return GSSAPI_AES128() 

68 ↛ 71line 68 didn't jump to line 71, because the condition on line 68 was never false elif cipher.enctype == constants.EncryptionTypes.rc4_hmac.value: 

return GSSAPI_RC4() 

else: 

raise Exception('Unsupported etype 0x%x' % cipher.enctype) 

 

# 7.2. GSS-API MIC Semantics 

class GSSAPI_RC4: 

# 1.2.1. Per-message Tokens - MIC 

class MIC(Structure): 

structure = ( 

('TOK_ID','<H=0x0101'), 

('SGN_ALG','<H=0'), 

('Filler','<L=0xffffffff'), 

('SND_SEQ','8s=b""'), 

('SGN_CKSUM','8s=b""'), 

) 

 

# 1.2.2. Per-message Tokens - Wrap 

class WRAP(Structure): 

structure = ( 

('TOK_ID','<H=0x0102'), 

('SGN_ALG','<H=0'), 

('SEAL_ALG','<H=0'), 

('Filler','<H=0xffff'), 

('SND_SEQ','8s=b""'), 

('SGN_CKSUM','8s=b""'), 

('Confounder','8s=b""'), 

) 

 

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

GSS_GETMIC_HEADER = b'\x60\x23\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02' 

token = self.MIC() 

 

# Let's pad the data 

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

padStr = b(chr(pad)) * pad 

data += padStr 

 

token['SGN_ALG'] = GSS_HMAC 

107 ↛ 110line 107 didn't jump to line 110, because the condition on line 107 was never false if direction == 'init': 

token['SND_SEQ'] = struct.pack('>L', sequenceNumber) + b'\x00'*4 

else: 

token['SND_SEQ'] = struct.pack('>L', sequenceNumber) + b'\xff'*4 

 

Ksign = HMAC.new(sessionKey.contents, b'signaturekey\0', MD5).digest() 

Sgn_Cksum = MD5.new( struct.pack('<L',15) + token.getData()[:8] + data).digest() 

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

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

 

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

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

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

finalData = GSS_GETMIC_HEADER + token.getData() 

return finalData 

 

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

# Damn inacurate RFC, useful info from here 

# https://social.msdn.microsoft.com/Forums/en-US/fb98e8f4-e697-4652-bcb7-604e027e14cc/gsswrap-token-size-kerberos-and-rc4hmac?forum=os_windowsprotocols 

# and here 

# http://www.rfc-editor.org/errata_search.php?rfc=4757 

GSS_WRAP_HEADER = b'\x60\x2b\x06\x09\x2a\x86\x48\x86\xf7\x12\x01\x02\x02' 

token = self.WRAP() 

 

# Let's pad the data 

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

padStr = b(chr(pad)) * pad 

data += padStr 

 

token['SGN_ALG'] = GSS_HMAC 

token['SEAL_ALG'] = GSS_RC4 

 

139 ↛ 142line 139 didn't jump to line 142, because the condition on line 139 was never false if direction == 'init': 

token['SND_SEQ'] = struct.pack('>L', sequenceNumber) + b'\x00'*4 

else: 

token['SND_SEQ'] = struct.pack('>L', sequenceNumber) + b'\xff'*4 

 

# Random confounder :) 

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

 

Ksign = HMAC.new(sessionKey.contents, b'signaturekey\0', MD5).digest() 

Sgn_Cksum = MD5.new(struct.pack('<L',13) + token.getData()[:8] + token['Confounder'] + data).digest() 

 

Klocal = bytearray() 

from builtins import bytes 

for n in bytes(sessionKey.contents): 

Klocal.append( n ^ 0xF0) 

 

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

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

 

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

 

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

 

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

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

 

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

 

if authData is not None: 

from impacket.dcerpc.v5.rpcrt import SEC_TRAILER 

wrap = self.WRAP(authData[len(SEC_TRAILER()) + len(GSS_WRAP_HEADER):]) 

snd_seq = wrap['SND_SEQ'] 

 

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

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

 

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

 

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

Kcrypt = HMAC.new(Kcrypt,snd_seq[:4], MD5).digest() 

rc4 = ARC4.new(Kcrypt) 

cipherText = rc4.decrypt(token['Confounder'] + data)[8:] 

181 ↛ 186line 181 didn't jump to line 186, because the condition on line 181 was never false elif encrypt is True: 

rc4 = ARC4.new(Kcrypt) 

token['Confounder'] = rc4.encrypt(token['Confounder']) 

cipherText = rc4.encrypt(data) 

else: 

cipherText = data 

 

finalData = GSS_WRAP_HEADER + token.getData() 

return cipherText, finalData 

 

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

return self.GSS_Wrap(sessionKey, data, sequenceNumber, direction, encrypt, authData) 

 

class GSSAPI_AES(): 

checkSumProfile = None 

cipherType = None 

 

class MIC(Structure): 

structure = ( 

('TOK_ID','>H=0x0404'), 

('Flags','B=0'), 

('Filler0','B=0xff'), 

('Filler','>L=0xffffffff'), 

('SND_SEQ','8s=b""'), 

('SGN_CKSUM','12s=b""'), 

) 

 

# 1.2.2. Per-message Tokens - Wrap 

class WRAP(Structure): 

structure = ( 

('TOK_ID','>H=0x0504'), 

('Flags','B=0'), 

('Filler','B=0xff'), 

('EC','>H=0'), 

('RRC','>H=0'), 

('SND_SEQ','8s=b""'), 

) 

 

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

token = self.MIC() 

 

# Let's pad the data 

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

padStr = chr(pad) * pad 

data += padStr 

 

checkSumProfile = self.checkSumProfile() 

 

token['Flags'] = 4 

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

token['SGN_CKSUM'] = checkSumProfile.checksum(sessionKey, KG_USAGE_INITIATOR_SIGN, data + token.getData()[:16]) 

 

return token.getData() 

 

def rotate(self, data, numBytes): 

numBytes %= len(data) 

left = len(data) - numBytes 

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

return result 

 

def unrotate(self, data, numBytes): 

numBytes %= len(data) 

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

return result 

 

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

token = self.WRAP() 

 

cipher = self.cipherType() 

 

# Let's pad the data 

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

padStr = b'\xFF' * pad 

data += padStr 

 

# The RRC field ([RFC4121] section 4.2.5) is 12 if no encryption is requested or 28 if encryption  

# is requested. The RRC field is chosen such that all the data can be encrypted in place. 

rrc = 28 

 

token['Flags'] = 6 

token['EC'] = pad 

token['RRC'] = 0 

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

 

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

token['RRC'] = rrc 

 

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

 

#nn = self.unrotate(cipherText, token['RRC'] + token['EC']) 

ret1 = cipherText[len(self.WRAP()) + token['RRC'] + token['EC']:] 

ret2 = token.getData() + cipherText[:len(self.WRAP()) + token['RRC'] + token['EC']] 

 

return ret1, ret2 

 

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

from impacket.dcerpc.v5.rpcrt import SEC_TRAILER 

 

cipher = self.cipherType() 

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

 

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

 

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

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

 

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

 

class GSSAPI_AES256(GSSAPI_AES): 

checkSumProfile = crypto._SHA1AES256 

cipherType = crypto._AES256CTS 

 

class GSSAPI_AES128(GSSAPI_AES): 

checkSumProfile = crypto._SHA1AES128 

cipherType = crypto._AES128CTS