Coverage for /root/GitHubProjects/impacket/impacket/spnego.py: 53%

# Coverage for /root/GitHubProjects/impacket/impacket/spnego.py : 53%

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

8#

9# Description:

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

11#

12 from __future__ import division

13 from __future__ import print_function

14 from struct import pack, unpack, calcsize

15

16############### GSS Stuff ################

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

18 ASN1_SEQUENCE = 0x30

19 ASN1_AID = 0x60

20 ASN1_OID = 0x06

21 ASN1_OCTET_STRING = 0x04

22 ASN1_MECH_TYPE = 0xa0

23 ASN1_MECH_TOKEN = 0xa2

24 ASN1_SUPPORTED_MECH = 0xa1

25 ASN1_RESPONSE_TOKEN = 0xa2

26 ASN1_ENUMERATED = 0x0a

27 MechTypes = {

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

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

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

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

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

33}

34

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

36

37 def asn1encode(data = ''):

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

39 #import binascii

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

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

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

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

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

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

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

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

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

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

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

51 else:

52 raise Exception('Error in asn1encode')

53 return res

54

55 def asn1decode(data = ''):

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

57 data = data[1:]

58 if len1 == 0x81:

59 pad = calcsize('B')

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

61 data = data[pad:]

62 ans = data[:len2]

63 elif len1 == 0x82: 63 ↛ 64line 63 didn't jump to line 64, because the condition on line 63 was never true

64 pad = calcsize('H')

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

66 data = data[pad:]

67 ans = data[:len2]

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

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

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

71 data = data[pad:]

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

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

74 pad = calcsize('!L')

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

76 data = data[pad:]

77 ans = data[:len2]

78 # 1 byte length, string <= 0x7F

79 else:

80 pad = 0

81 ans = data[:len1]

82 return ans, len(ans)+pad+1

83

84 class GSSAPI:

85# Generic GSSAPI Header Format

86 def __init__(self, data = None):

87 self.fields = {}

88 self['UUID'] = GSS_API_SPNEGO_UUID

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

90 self.fromString(data)

91 pass

92

93 def __setitem__(self,key,value):

94 self.fields[key] = value

95

96 def __getitem__(self, key):

97 return self.fields[key]

98

99 def __delitem__(self, key):

100 del self.fields[key]

101

102 def __len__(self):

103 return len(self.getData())

104

105 def __str__(self):

106 return len(self.getData())

107

108 def fromString(self, data = None):

109 # Manual parse of the GSSAPI Header Format

110 # It should be something like

111 # AID = 0x60 TAG, BER Length

112 # OID = 0x06 TAG

113 # GSSAPI OID

114 # UUID data (BER Encoded)

115 # Payload

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

117 if next_byte != ASN1_AID:

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

119 data = data[1:]

120 decode_data, total_bytes = asn1decode(data)

121 # Now we should have a OID tag

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

123 if next_byte != ASN1_OID:

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

125 decode_data = decode_data[1:]

126 # Now the OID contents, should be SPNEGO UUID

127 uuid, total_bytes = asn1decode(decode_data)

128 self['OID'] = uuid

129 # the rest should be the data

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

131 #pass

132

133 def dump(self):

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

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

136

137 def getData(self):

138 ans = pack('B',ASN1_AID)

139 ans += asn1encode(

140 pack('B',ASN1_OID) +

141 asn1encode(self['UUID']) +

142 self['Payload'] )

143 return ans

144

145 class SPNEGO_NegTokenResp:

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

147 # NegTokenResp ::= SEQUENCE {

148 # negState [0] ENUMERATED {

149 # accept-completed (0),

150 # accept-incomplete (1),

151 # reject (2),

152 # request-mic (3)

153 # } OPTIONAL,

154 # -- REQUIRED in the first reply from the target

155 # supportedMech [1] MechType OPTIONAL,

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

157 # responseToken [2] OCTET STRING OPTIONAL,

158 # mechListMIC [3] OCTET STRING OPTIONAL,

159 # ...

160 # }

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

162 SPNEGO_NEG_TOKEN_RESP = 0xa1

163 SPNEGO_NEG_TOKEN_TARG = 0xa0

164

165 def __init__(self, data = None):

166 self.fields = {}

167 if data:

168 self.fromString(data)

169 pass

170

171 def __setitem__(self,key,value):

172 self.fields[key] = value

173

174 def __getitem__(self, key):

175 return self.fields[key]

176

177 def __delitem__(self, key):

178 del self.fields[key]

179

180 def __len__(self):

181 return len(self.getData())

182

183 def __str__(self):

184 return self.getData()

185

186 def fromString(self, data = 0):

187 payload = data

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

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

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

191 payload = payload[1:]

192 decode_data, total_bytes = asn1decode(payload)

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

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

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

196 decode_data = decode_data[1:]

197 decode_data, total_bytes = asn1decode(decode_data)

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

199

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

201 # MechType not found, could be an AUTH answer

202 if next_byte != ASN1_RESPONSE_TOKEN:

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

204 else:

205 decode_data2 = decode_data[1:]

206 decode_data2, total_bytes = asn1decode(decode_data2)

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

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

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

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

211 self['NegState'] = item

212 decode_data = decode_data[1:]

213 decode_data = decode_data[total_bytes:]

214

215 # Do we have more data?

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

217 return

218

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

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

221 if next_byte != ASN1_RESPONSE_TOKEN:

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

223 else:

224 decode_data2 = decode_data[1:]

225 decode_data2, total_bytes = asn1decode(decode_data2)

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

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

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

229 decode_data2 = decode_data2[1:]

230 item, total_bytes2 = asn1decode(decode_data2)

231 self['SupportedMech'] = item

232

233 decode_data = decode_data[1:]

234 decode_data = decode_data[total_bytes:]

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

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

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

238

239 decode_data = decode_data[1:]

240 decode_data, total_bytes = asn1decode(decode_data)

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

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

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

244 decode_data = decode_data[1:]

245 decode_data, total_bytes = asn1decode(decode_data)

246 self['ResponseToken'] = decode_data

247

248 def dump(self):

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

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

251 def getData(self):

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

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

254 # Server resp

255 ans += asn1encode(

256 pack('B', ASN1_SEQUENCE) +

257 asn1encode(

258 pack('B',SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_TARG) +

259 asn1encode(

260 pack('B',ASN1_ENUMERATED) +

261 asn1encode( self['NegState'] )) +

262 pack('B',ASN1_SUPPORTED_MECH) +

263 asn1encode(

264 pack('B',ASN1_OID) +

265 asn1encode(self['SupportedMech'])) +

266 pack('B',ASN1_RESPONSE_TOKEN ) +

267 asn1encode(

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

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

270 # Server resp

271 ans += asn1encode(

272 pack('B', ASN1_SEQUENCE) +

273 asn1encode(

274 pack('B',SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_TARG) +

275 asn1encode(

276 pack('B',ASN1_ENUMERATED) +

277 asn1encode( self['NegState'] )) +

278 pack('B',ASN1_SUPPORTED_MECH) +

279 asn1encode(

280 pack('B',ASN1_OID) +

281 asn1encode(self['SupportedMech']))))

282 elif 'NegState' in self.fields: 282 ↛ 284line 282 didn't jump to line 284, because the condition on line 282 was never true

283 # Server resp

284 ans += asn1encode(

285 pack('B', ASN1_SEQUENCE) +

286 asn1encode(

287 pack('B', SPNEGO_NegTokenResp.SPNEGO_NEG_TOKEN_TARG) +

288 asn1encode(

289 pack('B',ASN1_ENUMERATED) +

290 asn1encode( self['NegState'] ))))

291 else:

292 # Client resp

293 ans += asn1encode(

294 pack('B', ASN1_SEQUENCE) +

295 asn1encode(

296 pack('B', ASN1_RESPONSE_TOKEN) +

297 asn1encode(

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

299 return ans

300

301 class SPNEGO_NegTokenInit(GSSAPI):

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

303 # NegTokeInit :: = SEQUENCE {

304 # mechTypes [0] MechTypeList,

305 # reqFlags [1] ContextFlags OPTIONAL,

306 # mechToken [2] OCTET STRING OPTIONAL,

307 # mechListMIC [3] OCTET STRING OPTIONAL,

308 # }

309 SPNEGO_NEG_TOKEN_INIT = 0xa0

310 def fromString(self, data = 0):

311 GSSAPI.fromString(self, data)

312 payload = self['Payload']

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

314 if next_byte != SPNEGO_NegTokenInit.SPNEGO_NEG_TOKEN_INIT:

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

316 payload = payload[1:]

317 decode_data, total_bytes = asn1decode(payload)

318 # Now we should have a SEQUENCE Tag

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

320 if next_byte != ASN1_SEQUENCE:

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

322 decode_data = decode_data[1:]

323 decode_data, total_bytes2 = asn1decode(decode_data)

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

325 if next_byte != ASN1_MECH_TYPE:

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

327 decode_data = decode_data[1:]

328 remaining_data = decode_data

329 decode_data, total_bytes3 = asn1decode(decode_data)

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

331 if next_byte != ASN1_SEQUENCE:

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

333 decode_data = decode_data[1:]

334 decode_data, total_bytes4 = asn1decode(decode_data)

335 # And finally we should have the MechTypes

336 self['MechTypes'] = []

337 while decode_data:

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

339 if next_byte != ASN1_OID:

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

341 break

342 decode_data = decode_data[1:]

343 item, total_bytes = asn1decode(decode_data)

344 self['MechTypes'].append(item)

345 decode_data = decode_data[total_bytes:]

346

347 # Do we have MechTokens as well?

348 decode_data = remaining_data[total_bytes3:]

349 if len(decode_data) > 0:

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

351 if next_byte == ASN1_MECH_TOKEN:

352 # We have tokens in here!

353 decode_data = decode_data[1:]

354 decode_data, total_bytes = asn1decode(decode_data)

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

356 if next_byte == ASN1_OCTET_STRING:

357 decode_data = decode_data[1:]

358 decode_data, total_bytes = asn1decode(decode_data)

359 self['MechToken'] = decode_data

360

361 def getData(self):

362 mechTypes = b''

363 for i in self['MechTypes']:

364 mechTypes += pack('B', ASN1_OID)

365 mechTypes += asn1encode(i)

366

367 mechToken = b''

368 # Do we have tokens to send?

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

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

371 pack('B', ASN1_OCTET_STRING) + asn1encode(

372 self['MechToken']))

373

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

375 ans += asn1encode(

376 pack('B', ASN1_SEQUENCE) +

377 asn1encode(

378 pack('B', ASN1_MECH_TYPE) +

379 asn1encode(

380 pack('B', ASN1_SEQUENCE) +

381 asn1encode(mechTypes)) + mechToken ))

382

383

384 self['Payload'] = ans

385 return GSSAPI.getData(self)