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

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

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

# 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 (beto@coresecurity.com) 

# 

# Description: 

# SPNEGO functions used by SMB, SMB2/3 and DCERPC 

# 

from __future__ import division 

from __future__ import print_function 

from struct import pack, unpack, calcsize 

 

############### GSS Stuff ################ 

GSS_API_SPNEGO_UUID = b'\x2b\x06\x01\x05\x05\x02' 

ASN1_SEQUENCE = 0x30 

ASN1_AID = 0x60 

ASN1_OID = 0x06 

ASN1_OCTET_STRING = 0x04 

ASN1_MECH_TYPE = 0xa0 

ASN1_MECH_TOKEN = 0xa2 

ASN1_SUPPORTED_MECH = 0xa1 

ASN1_RESPONSE_TOKEN = 0xa2 

ASN1_ENUMERATED = 0x0a 

MechTypes = { 

b'+\x06\x01\x04\x01\x827\x02\x02\n': 'NTLMSSP - Microsoft NTLM Security Support Provider', 

b'*\x86H\x82\xf7\x12\x01\x02\x02': 'MS KRB5 - Microsoft Kerberos 5', 

b'*\x86H\x86\xf7\x12\x01\x02\x02': 'KRB5 - Kerberos 5', 

b'*\x86H\x86\xf7\x12\x01\x02\x02\x03': 'KRB5 - Kerberos 5 - User to User', 

b'\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x1e': 'NEGOEX - SPNEGO Extended Negotiation Security Mechanism' 

} 

 

TypesMech = dict((v,k) for k, v in MechTypes.items()) 

 

def asn1encode(data = ''): 

#res = asn1.SEQUENCE(str).encode() 

#import binascii 

#print '\nalex asn1encode str: %s\n' % binascii.hexlify(str) 

if 0 <= len(data) <= 0x7F: 

res = pack('B', len(data)) + data 

43 ↛ 44line 43 didn't jump to line 44, because the condition on line 43 was never true elif 0x80 <= len(data) <= 0xFF: 

res = pack('BB', 0x81, len(data)) + data 

45 ↛ 47line 45 didn't jump to line 47, because the condition on line 45 was never false elif 0x100 <= len(data) <= 0xFFFF: 

res = pack('!BH', 0x82, len(data)) + data 

elif 0x10000 <= len(data) <= 0xffffff: 

res = pack('!BBH', 0x83, len(data) >> 16, len(data) & 0xFFFF) + data 

elif 0x1000000 <= len(data) <= 0xffffffff: 

res = pack('!BL', 0x84, len(data)) + data 

else: 

raise Exception('Error in asn1encode') 

return res 

 

def asn1decode(data = ''): 

len1 = unpack('B', data[:1])[0] 

data = data[1:] 

if len1 == 0x81: 

pad = calcsize('B') 

len2 = unpack('B',data[:pad])[0] 

data = data[pad:] 

ans = data[:len2] 

elif len1 == 0x82: 

pad = calcsize('H') 

len2 = unpack('!H', data[:pad])[0] 

data = data[pad:] 

ans = data[:len2] 

68 ↛ 69line 68 didn't jump to line 69, because the condition on line 68 was never true elif len1 == 0x83: 

pad = calcsize('B') + calcsize('!H') 

len2, len3 = unpack('!BH', data[:pad]) 

data = data[pad:] 

ans = data[:len2 << 16 + len3] 

73 ↛ 74line 73 didn't jump to line 74, because the condition on line 73 was never true elif len1 == 0x84: 

pad = calcsize('!L') 

len2 = unpack('!L', data[:pad])[0] 

data = data[pad:] 

ans = data[:len2] 

# 1 byte length, string <= 0x7F 

else: 

pad = 0 

ans = data[:len1] 

return ans, len(ans)+pad+1 

 

class GSSAPI: 

# Generic GSSAPI Header Format  

def __init__(self, data = None): 

self.fields = {} 

self['UUID'] = GSS_API_SPNEGO_UUID 

89 ↛ 90line 89 didn't jump to line 90, because the condition on line 89 was never true if data: 

self.fromString(data) 

pass 

 

def __setitem__(self,key,value): 

self.fields[key] = value 

 

def __getitem__(self, key): 

return self.fields[key] 

 

def __delitem__(self, key): 

del self.fields[key] 

 

def __len__(self): 

return len(self.getData()) 

 

def __str__(self): 

return len(self.getData()) 

 

def fromString(self, data = None): 

# Manual parse of the GSSAPI Header Format 

# It should be something like 

# AID = 0x60 TAG, BER Length 

# OID = 0x06 TAG 

# GSSAPI OID 

# UUID data (BER Encoded) 

# Payload 

next_byte = unpack('B',data[:1])[0] 

if next_byte != ASN1_AID: 

raise Exception('Unknown AID=%x' % next_byte) 

data = data[1:] 

decode_data, total_bytes = asn1decode(data) 

# Now we should have a OID tag 

next_byte = unpack('B',decode_data[:1])[0] 

if next_byte != ASN1_OID: 

raise Exception('OID tag not found %x' % next_byte) 

decode_data = decode_data[1:] 

# Now the OID contents, should be SPNEGO UUID 

uuid, total_bytes = asn1decode(decode_data) 

self['OID'] = uuid 

# the rest should be the data 

self['Payload'] = decode_data[total_bytes:] 

#pass 

 

def dump(self): 

for i in list(self.fields.keys()): 

print("%s: {%r}" % (i,self[i])) 

 

def getData(self): 

ans = pack('B',ASN1_AID) 

ans += asn1encode( 

pack('B',ASN1_OID) + 

asn1encode(self['UUID']) + 

self['Payload'] ) 

return ans 

 

class SPNEGO_NegTokenResp: 

# https://tools.ietf.org/html/rfc4178#page-9 

# NegTokenResp ::= SEQUENCE { 

# negState [0] ENUMERATED { 

# accept-completed (0), 

# accept-incomplete (1), 

# reject (2), 

# request-mic (3) 

# } OPTIONAL, 

# -- REQUIRED in the first reply from the target 

# supportedMech [1] MechType OPTIONAL, 

# -- present only in the first reply from the target 

# responseToken [2] OCTET STRING OPTIONAL, 

# mechListMIC [3] OCTET STRING OPTIONAL, 

# ... 

# } 

# This structure is not prepended by a GSS generic header! 

SPNEGO_NEG_TOKEN_RESP = 0xa1 

SPNEGO_NEG_TOKEN_TARG = 0xa0 

 

def __init__(self, data = None): 

self.fields = {} 

if data: 

self.fromString(data) 

pass 

 

def __setitem__(self,key,value): 

self.fields[key] = value 

 

def __getitem__(self, key): 

return self.fields[key] 

 

def __delitem__(self, key): 

del self.fields[key] 

 

def __len__(self): 

return len(self.getData()) 

 

def __str__(self): 

return self.getData() 

 

def fromString(self, data = 0): 

payload = data 

next_byte = unpack('B', payload[:1])[0] 

189 ↛ 190line 189 didn't jump to line 190, because the condition on line 189 was never true if next_byte != SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP: 

raise Exception('NegTokenResp not found %x' % next_byte) 

payload = payload[1:] 

decode_data, total_bytes = asn1decode(payload) 

next_byte = unpack('B', decode_data[:1])[0] 

194 ↛ 195line 194 didn't jump to line 195, because the condition on line 194 was never true if next_byte != ASN1_SEQUENCE: 

raise Exception('SEQUENCE tag not found %x' % next_byte) 

decode_data = decode_data[1:] 

decode_data, total_bytes = asn1decode(decode_data) 

next_byte = unpack('B',decode_data[:1])[0] 

 

200 ↛ 202line 200 didn't jump to line 202, because the condition on line 200 was never true if next_byte != ASN1_MECH_TYPE: 

# MechType not found, could be an AUTH answer 

if next_byte != ASN1_RESPONSE_TOKEN: 

raise Exception('MechType/ResponseToken tag not found %x' % next_byte) 

else: 

decode_data2 = decode_data[1:] 

decode_data2, total_bytes = asn1decode(decode_data2) 

next_byte = unpack('B', decode_data2[:1])[0] 

208 ↛ 209line 208 didn't jump to line 209, because the condition on line 208 was never true if next_byte != ASN1_ENUMERATED: 

raise Exception('Enumerated tag not found %x' % next_byte) 

item, total_bytes2 = asn1decode(decode_data2[1:]) 

self['NegResult'] = item 

decode_data = decode_data[1:] 

decode_data = decode_data[total_bytes:] 

 

# Do we have more data? 

216 ↛ 217line 216 didn't jump to line 217, because the condition on line 216 was never true if len(decode_data) == 0: 

return 

 

next_byte = unpack('B', decode_data[:1])[0] 

220 ↛ 221line 220 didn't jump to line 221, because the condition on line 220 was never true if next_byte != ASN1_SUPPORTED_MECH: 

if next_byte != ASN1_RESPONSE_TOKEN: 

raise Exception('Supported Mech/ResponseToken tag not found %x' % next_byte) 

else: 

decode_data2 = decode_data[1:] 

decode_data2, total_bytes = asn1decode(decode_data2) 

next_byte = unpack('B', decode_data2[:1])[0] 

227 ↛ 228line 227 didn't jump to line 228, because the condition on line 227 was never true if next_byte != ASN1_OID: 

raise Exception('OID tag not found %x' % next_byte) 

decode_data2 = decode_data2[1:] 

item, total_bytes2 = asn1decode(decode_data2) 

self['SupportedMech'] = item 

 

decode_data = decode_data[1:] 

decode_data = decode_data[total_bytes:] 

next_byte = unpack('B', decode_data[:1])[0] 

236 ↛ 237line 236 didn't jump to line 237, because the condition on line 236 was never true if next_byte != ASN1_RESPONSE_TOKEN: 

raise Exception('Response token tag not found %x' % next_byte) 

 

decode_data = decode_data[1:] 

decode_data, total_bytes = asn1decode(decode_data) 

next_byte = unpack('B', decode_data[:1])[0] 

242 ↛ 243line 242 didn't jump to line 243, because the condition on line 242 was never true if next_byte != ASN1_OCTET_STRING: 

raise Exception('Octet string token tag not found %x' % next_byte) 

decode_data = decode_data[1:] 

decode_data, total_bytes = asn1decode(decode_data) 

self['ResponseToken'] = decode_data 

 

def dump(self): 

for i in list(self.fields.keys()): 

print("%s: {%r}" % (i,self[i])) 

def getData(self): 

ans = pack('B',SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_RESP) 

253 ↛ 255line 253 didn't jump to line 255, because the condition on line 253 was never true if 'NegResult' in self.fields and 'SupportedMech' in self.fields: 

# Server resp 

ans += asn1encode( 

pack('B', ASN1_SEQUENCE) + 

asn1encode( 

pack('B',SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_TARG) + 

asn1encode( 

pack('B',ASN1_ENUMERATED) + 

asn1encode( self['NegResult'] )) + 

pack('B',ASN1_SUPPORTED_MECH) + 

asn1encode( 

pack('B',ASN1_OID) + 

asn1encode(self['SupportedMech'])) + 

pack('B',ASN1_RESPONSE_TOKEN ) + 

asn1encode( 

pack('B', ASN1_OCTET_STRING) + asn1encode(self['ResponseToken'])))) 

269 ↛ 271line 269 didn't jump to line 271, because the condition on line 269 was never true elif 'NegResult' in self.fields: 

# Server resp 

ans += asn1encode( 

pack('B', ASN1_SEQUENCE) + 

asn1encode( 

pack('B', SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_TARG) + 

asn1encode( 

pack('B',ASN1_ENUMERATED) + 

asn1encode( self['NegResult'] )))) 

else: 

# Client resp 

ans += asn1encode( 

pack('B', ASN1_SEQUENCE) + 

asn1encode( 

pack('B', ASN1_RESPONSE_TOKEN) + 

asn1encode( 

pack('B', ASN1_OCTET_STRING) + asn1encode(self['ResponseToken'])))) 

return ans 

 

class SPNEGO_NegTokenInit(GSSAPI): 

# https://tools.ietf.org/html/rfc4178#page-8 

# NegTokeInit :: = SEQUENCE { 

# mechTypes [0] MechTypeList, 

# reqFlags [1] ContextFlags OPTIONAL, 

# mechToken [2] OCTET STRING OPTIONAL, 

# mechListMIC [3] OCTET STRING OPTIONAL, 

# } 

SPNEGO_NEG_TOKEN_INIT = 0xa0 

def fromString(self, data = 0): 

GSSAPI.fromString(self, data) 

payload = self['Payload'] 

next_byte = unpack('B', payload[:1])[0] 

if next_byte != SPNEGO_NegTokenInit.SPNEGO_NEG_TOKEN_INIT: 

raise Exception('NegTokenInit not found %x' % next_byte) 

payload = payload[1:] 

decode_data, total_bytes = asn1decode(payload) 

# Now we should have a SEQUENCE Tag 

next_byte = unpack('B', decode_data[:1])[0] 

if next_byte != ASN1_SEQUENCE: 

raise Exception('SEQUENCE tag not found %x' % next_byte) 

decode_data = decode_data[1:] 

decode_data, total_bytes2 = asn1decode(decode_data) 

next_byte = unpack('B',decode_data[:1])[0] 

if next_byte != ASN1_MECH_TYPE: 

raise Exception('MechType tag not found %x' % next_byte) 

decode_data = decode_data[1:] 

remaining_data = decode_data 

decode_data, total_bytes3 = asn1decode(decode_data) 

next_byte = unpack('B', decode_data[:1])[0] 

if next_byte != ASN1_SEQUENCE: 

raise Exception('SEQUENCE tag not found %x' % next_byte) 

decode_data = decode_data[1:] 

decode_data, total_bytes4 = asn1decode(decode_data) 

# And finally we should have the MechTypes 

self['MechTypes'] = [] 

while decode_data: 

next_byte = unpack('B', decode_data[:1])[0] 

if next_byte != ASN1_OID: 

# Not a valid OID, there must be something else we won't unpack 

break 

decode_data = decode_data[1:] 

item, total_bytes = asn1decode(decode_data) 

self['MechTypes'].append(item) 

decode_data = decode_data[total_bytes:] 

 

# Do we have MechTokens as well? 

decode_data = remaining_data[total_bytes3:] 

if len(decode_data) > 0: 

next_byte = unpack('B', decode_data[:1])[0] 

if next_byte == ASN1_MECH_TOKEN: 

# We have tokens in here! 

decode_data = decode_data[1:] 

decode_data, total_bytes = asn1decode(decode_data) 

next_byte = unpack('B', decode_data[:1])[0] 

if next_byte == ASN1_OCTET_STRING: 

decode_data = decode_data[1:] 

decode_data, total_bytes = asn1decode(decode_data) 

self['MechToken'] = decode_data 

 

def getData(self): 

mechTypes = b'' 

for i in self['MechTypes']: 

mechTypes += pack('B', ASN1_OID) 

mechTypes += asn1encode(i) 

 

mechToken = b'' 

# Do we have tokens to send? 

356 ↛ 361line 356 didn't jump to line 361, because the condition on line 356 was never false if 'MechToken' in self.fields: 

mechToken = pack('B', ASN1_MECH_TOKEN) + asn1encode( 

pack('B', ASN1_OCTET_STRING) + asn1encode( 

self['MechToken'])) 

 

ans = pack('B',SPNEGO_NegTokenInit.SPNEGO_NEG_TOKEN_INIT) 

ans += asn1encode( 

pack('B', ASN1_SEQUENCE) + 

asn1encode( 

pack('B', ASN1_MECH_TYPE) + 

asn1encode( 

pack('B', ASN1_SEQUENCE) + 

asn1encode(mechTypes)) + mechToken )) 

 

 

self['Payload'] = ans 

return GSSAPI.getData(self)