smb3.py 70 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630
  1. from __future__ import print_function
  2. # Copyright (c) 2003-2016 CORE Security Technologies
  3. #
  4. # This software is provided under under a slightly modified version
  5. # of the Apache Software License. See the accompanying LICENSE file
  6. # for more information.
  7. #
  8. # Author: Alberto Solino (@agsolino)
  9. #
  10. # Description:
  11. # [MS-SMB2] Protocol Implementation (SMB2 and SMB3)
  12. # As you might see in the code, it's implemented strictly following
  13. # the structures defined in the protocol specification. This may
  14. # not be the most efficient way (e.g. self._Connection is the
  15. # same to self._Session in the context of this library ) but
  16. # it certainly helps following the document way easier.
  17. #
  18. # ToDo:
  19. # [X] Implement SMB2_CHANGE_NOTIFY
  20. # [X] Implement SMB2_QUERY_INFO
  21. # [X] Implement SMB2_SET_INFO
  22. # [ ] Implement SMB2_OPLOCK_BREAK
  23. # [X] Implement SMB3 signing
  24. # [ ] Implement SMB3 encryption
  25. # [ ] Add more backward compatible commands from the smb.py code
  26. # [ ] Fix up all the 'ToDo' comments inside the code
  27. #
  28. import socket
  29. import ntpath
  30. import random
  31. import string
  32. import struct
  33. from binascii import a2b_hex
  34. from contextlib import contextmanager
  35. from impacket import nmb, ntlm, uuid, crypto, LOG
  36. from impacket.smb3structs import *
  37. from impacket.nt_errors import STATUS_SUCCESS, STATUS_MORE_PROCESSING_REQUIRED, STATUS_INVALID_PARAMETER, \
  38. STATUS_NO_MORE_FILES, STATUS_PENDING, STATUS_NOT_IMPLEMENTED, ERROR_MESSAGES
  39. from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp
  40. # For signing
  41. import hashlib, hmac, copy
  42. # Structs to be used
  43. TREE_CONNECT = {
  44. 'ShareName' : '',
  45. 'TreeConnectId' : 0,
  46. 'Session' : 0,
  47. 'IsDfsShare' : False,
  48. # If the client implements the SMB 3.0 dialect,
  49. # the client MUST also implement the following
  50. 'IsCAShare' : False,
  51. 'EncryptData' : False,
  52. 'IsScaleoutShare' : False,
  53. # Outside the protocol
  54. 'NumberOfUses' : 0,
  55. }
  56. FILE = {
  57. 'OpenTable' : [],
  58. 'LeaseKey' : '',
  59. 'LeaseState' : 0,
  60. 'LeaseEpoch' : 0,
  61. }
  62. OPEN = {
  63. 'FileID' : '',
  64. 'TreeConnect' : 0,
  65. 'Connection' : 0, # Not Used
  66. 'Oplocklevel' : 0,
  67. 'Durable' : False,
  68. 'FileName' : '',
  69. 'ResilientHandle' : False,
  70. 'LastDisconnectTime' : 0,
  71. 'ResilientTimeout' : 0,
  72. 'OperationBuckets' : [],
  73. # If the client implements the SMB 3.0 dialect,
  74. # the client MUST implement the following
  75. 'CreateGuid' : '',
  76. 'IsPersistent' : False,
  77. 'DesiredAccess' : '',
  78. 'ShareMode' : 0,
  79. 'CreateOption' : '',
  80. 'FileAttributes' : '',
  81. 'CreateDisposition' : '',
  82. }
  83. REQUEST = {
  84. 'CancelID' : '',
  85. 'Message' : '',
  86. 'Timestamp' : 0,
  87. }
  88. CHANNEL = {
  89. 'SigningKey' : '',
  90. 'Connection' : 0,
  91. }
  92. class SessionError(Exception):
  93. def __init__( self, error = 0, packet=0):
  94. Exception.__init__(self)
  95. self.error = error
  96. self.packet = packet
  97. def get_error_code( self ):
  98. return self.error
  99. def get_error_packet( self ):
  100. return self.packet
  101. def __str__( self ):
  102. return 'SMB SessionError: %s(%s)' % (ERROR_MESSAGES[self.error])
  103. class SMB3:
  104. def __init__(self, remote_name, remote_host, my_name = None, host_type = nmb.TYPE_SERVER, sess_port = 445, timeout=60, UDP = 0, preferredDialect = None, session = None):
  105. # [MS-SMB2] Section 3
  106. self.RequireMessageSigning = False #
  107. self.ConnectionTable = {}
  108. self.GlobalFileTable = {}
  109. self.ClientGuid = ''.join([random.choice(string.letters) for i in range(16)])
  110. # Only for SMB 3.0
  111. self.EncryptionAlgorithmList = ['AES-CCM']
  112. self.MaxDialect = []
  113. self.RequireSecureNegotiate = False
  114. # Per Transport Connection Data
  115. self._Connection = {
  116. # Indexed by SessionID
  117. #'SessionTable' : {},
  118. # Indexed by MessageID
  119. 'OutstandingRequests' : {},
  120. 'OutstandingResponses' : {}, #
  121. 'SequenceWindow' : 0, #
  122. 'GSSNegotiateToken' : '', #
  123. 'MaxTransactSize' : 0, #
  124. 'MaxReadSize' : 0, #
  125. 'MaxWriteSize' : 0, #
  126. 'ServerGuid' : '', #
  127. 'RequireSigning' : False, #
  128. 'ServerName' : '', #
  129. # If the client implements the SMB 2.1 or SMB 3.0 dialects, it MUST
  130. # also implement the following
  131. 'Dialect' : '', #
  132. 'SupportsFileLeasing' : False, #
  133. 'SupportsMultiCredit' : False, #
  134. # If the client implements the SMB 3.0 dialect,
  135. # it MUST also implement the following
  136. 'SupportsDirectoryLeasing' : False, #
  137. 'SupportsMultiChannel' : False, #
  138. 'SupportsPersistentHandles': False, #
  139. 'SupportsEncryption' : False, #
  140. 'ClientCapabilities' : 0,
  141. 'ServerCapabilities' : 0, #
  142. 'ClientSecurityMode' : 0, #
  143. 'ServerSecurityMode' : 0, #
  144. # Outside the protocol
  145. 'ServerIP' : '', #
  146. }
  147. self._Session = {
  148. 'SessionID' : 0, #
  149. 'TreeConnectTable' : {}, #
  150. 'SessionKey' : '', #
  151. 'SigningRequired' : False, #
  152. 'Connection' : 0, #
  153. 'UserCredentials' : '', #
  154. 'OpenTable' : {}, #
  155. # If the client implements the SMB 3.0 dialect,
  156. # it MUST also implement the following
  157. 'ChannelList' : [],
  158. 'ChannelSequence' : 0,
  159. #'EncryptData' : False,
  160. 'EncryptData' : True,
  161. 'EncryptionKey' : '',
  162. 'DecryptionKey' : '',
  163. 'SigningKey' : '',
  164. 'ApplicationKey' : '',
  165. # Outside the protocol
  166. 'SessionFlags' : 0, #
  167. 'ServerName' : '', #
  168. 'ServerDomain' : '', #
  169. 'ServerDNSDomainName' : '', #
  170. 'ServerOS' : '', #
  171. 'SigningActivated' : False, #
  172. }
  173. self.SMB_PACKET = SMB2Packet
  174. self._timeout = timeout
  175. self._Connection['ServerIP'] = remote_host
  176. self._NetBIOSSession = None
  177. self.__userName = ''
  178. self.__password = ''
  179. self.__domain = ''
  180. self.__lmhash = ''
  181. self.__nthash = ''
  182. self.__kdc = ''
  183. self.__aesKey = ''
  184. self.__TGT = None
  185. self.__TGS = None
  186. if sess_port == 445 and remote_name == '*SMBSERVER':
  187. self._Connection['ServerName'] = remote_host
  188. else:
  189. self._Connection['ServerName'] = remote_name
  190. if session is None:
  191. if not my_name:
  192. my_name = socket.gethostname()
  193. i = string.find(my_name, '.')
  194. if i > -1:
  195. my_name = my_name[:i]
  196. if UDP:
  197. self._NetBIOSSession = nmb.NetBIOSUDPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout)
  198. else:
  199. self._NetBIOSSession = nmb.NetBIOSTCPSession(my_name, self._Connection['ServerName'], remote_host, host_type, sess_port, self._timeout)
  200. self.negotiateSession(preferredDialect)
  201. else:
  202. self._NetBIOSSession = session
  203. # We should increase the SequenceWindow since a packet was already received.
  204. self._Connection['SequenceWindow'] += 1
  205. # Let's negotiate again using the same connection
  206. self.negotiateSession(preferredDialect)
  207. def printStatus(self):
  208. print("CONNECTION")
  209. for i in self._Connection.items():
  210. print("%-40s : %s" % i)
  211. print()
  212. print("SESSION")
  213. for i in self._Session.items():
  214. print("%-40s : %s" % i)
  215. def getServerName(self):
  216. return self._Session['ServerName']
  217. def getServerIP(self):
  218. return self._Connection['ServerIP']
  219. def getServerDomain(self):
  220. return self._Session['ServerDomain']
  221. def getServerDNSDomainName(self):
  222. return self._Session['ServerDNSDomainName']
  223. def getServerOS(self):
  224. return self._Session['ServerOS']
  225. def getServerOSMajor(self):
  226. return self._Session['ServerOSMajor']
  227. def getServerOSMinor(self):
  228. return self._Session['ServerOSMinor']
  229. def getServerOSBuild(self):
  230. return self._Session['ServerOSBuild']
  231. def isGuestSession(self):
  232. return self._Session['SessionFlags'] & SMB2_SESSION_FLAG_IS_GUEST
  233. def setTimeout(self, timeout):
  234. self._timeout = timeout
  235. @contextmanager
  236. def useTimeout(self, timeout):
  237. prev_timeout = self.getTimeout(timeout)
  238. try:
  239. yield
  240. finally:
  241. self.setTimeout(prev_timeout)
  242. def getDialect(self):
  243. return self._Connection['Dialect']
  244. def signSMB(self, packet):
  245. packet['Signature'] = '\x00'*16
  246. if self._Connection['Dialect'] == SMB2_DIALECT_21 or self._Connection['Dialect'] == SMB2_DIALECT_002:
  247. if len(self._Session['SessionKey']) > 0:
  248. signature = hmac.new(self._Session['SessionKey'], str(packet), hashlib.sha256).digest()
  249. packet['Signature'] = signature[:16]
  250. else:
  251. if len(self._Session['SessionKey']) > 0:
  252. p = str(packet)
  253. signature = crypto.AES_CMAC(self._Session['SigningKey'], p, len(p))
  254. packet['Signature'] = signature
  255. def sendSMB(self, packet):
  256. # The idea here is to receive multiple/single commands and create a compound request, and send it
  257. # Should return the MessageID for later retrieval. Implement compounded related requests.
  258. # If Connection.Dialect is equal to "3.000" and if Connection.SupportsMultiChannel or
  259. # Connection.SupportsPersistentHandles is TRUE, the client MUST set ChannelSequence in the
  260. # SMB2 header to Session.ChannelSequence
  261. # Check this is not a CANCEL request. If so, don't consume sequece numbers
  262. if packet['Command'] is not SMB2_CANCEL:
  263. packet['MessageID'] = self._Connection['SequenceWindow']
  264. self._Connection['SequenceWindow'] += 1
  265. packet['SessionID'] = self._Session['SessionID']
  266. # Default the credit charge to 1 unless set by the caller
  267. if ('CreditCharge' in packet.fields) is False:
  268. packet['CreditCharge'] = 1
  269. # Standard credit request after negotiating protocol
  270. if self._Connection['SequenceWindow'] > 3:
  271. packet['CreditRequestResponse'] = 127
  272. messageId = packet['MessageID']
  273. if self._Session['SigningActivated'] is True and self._Connection['SequenceWindow'] > 2:
  274. if packet['TreeID'] > 0 and (packet['TreeID'] in self._Session['TreeConnectTable']) is True:
  275. if self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is False:
  276. packet['Flags'] = SMB2_FLAGS_SIGNED
  277. self.signSMB(packet)
  278. elif packet['TreeID'] == 0:
  279. packet['Flags'] = SMB2_FLAGS_SIGNED
  280. self.signSMB(packet)
  281. if (self._Session['SessionFlags'] & SMB2_SESSION_FLAG_ENCRYPT_DATA) or ( packet['TreeID'] != 0 and self._Session['TreeConnectTable'][packet['TreeID']]['EncryptData'] is True):
  282. plainText = str(packet)
  283. transformHeader = SMB2_TRANSFORM_HEADER()
  284. transformHeader['Nonce'] = ''.join([random.choice(string.letters) for i in range(11)])
  285. transformHeader['OriginalMessageSize'] = len(plainText)
  286. transformHeader['EncryptionAlgorithm'] = SMB2_ENCRYPTION_AES128_CCM
  287. transformHeader['SessionID'] = self._Session['SessionID']
  288. from Crypto.Cipher import AES
  289. try:
  290. AES.MODE_CCM
  291. except:
  292. LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ")
  293. raise
  294. cipher = AES.new(self._Session['EncryptionKey'], AES.MODE_CCM, transformHeader['Nonce'])
  295. cipher.update(str(transformHeader)[20:])
  296. cipherText = cipher.encrypt(plainText)
  297. transformHeader['Signature'] = cipher.digest()
  298. packet = str(transformHeader) + cipherText
  299. self._NetBIOSSession.send_packet(str(packet))
  300. return messageId
  301. def recvSMB(self, packetID = None):
  302. # First, verify we don't have the packet already
  303. if packetID in self._Connection['OutstandingResponses']:
  304. return self._Connection['OutstandingResponses'].pop(packetID)
  305. data = self._NetBIOSSession.recv_packet(self._timeout)
  306. if data.get_trailer().startswith('\xfdSMB'):
  307. # Packet is encrypted
  308. transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())
  309. from Crypto.Cipher import AES
  310. try:
  311. AES.MODE_CCM
  312. except:
  313. LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ")
  314. raise
  315. cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11])
  316. cipher.update(str(transformHeader)[20:])
  317. plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):])
  318. #cipher.verify(transformHeader['Signature'])
  319. packet = SMB2Packet(plainText)
  320. else:
  321. # In all SMB dialects for a response this field is interpreted as the Status field.
  322. # This field can be set to any value. For a list of valid status codes,
  323. # see [MS-ERREF] section 2.3.
  324. packet = SMB2Packet(data.get_trailer())
  325. # Loop while we receive pending requests
  326. if packet['Status'] == STATUS_PENDING:
  327. status = STATUS_PENDING
  328. while status == STATUS_PENDING:
  329. data = self._NetBIOSSession.recv_packet(self._timeout)
  330. if data.get_trailer().startswith('\xfeSMB'):
  331. packet = SMB2Packet(data.get_trailer())
  332. else:
  333. # Packet is encrypted
  334. transformHeader = SMB2_TRANSFORM_HEADER(data.get_trailer())
  335. from Crypto.Cipher import AES
  336. try:
  337. AES.MODE_CCM
  338. except:
  339. LOG.critical("Your pycrypto doesn't support AES.MODE_CCM. Currently only pycrypto experimental supports this mode.\nDownload it from https://www.dlitz.net/software/pycrypto ")
  340. raise
  341. cipher = AES.new(self._Session['DecryptionKey'], AES.MODE_CCM, transformHeader['Nonce'][:11])
  342. cipher.update(str(transformHeader)[20:])
  343. plainText = cipher.decrypt(data.get_trailer()[len(SMB2_TRANSFORM_HEADER()):])
  344. #cipher.verify(transformHeader['Signature'])
  345. packet = SMB2Packet(plainText)
  346. status = packet['Status']
  347. if packet['MessageID'] == packetID or packetID is None:
  348. # if self._Session['SigningRequired'] is True:
  349. # self.signSMB(packet)
  350. # Let's update the sequenceWindow based on the CreditsCharged
  351. self._Connection['SequenceWindow'] += (packet['CreditCharge'] - 1)
  352. return packet
  353. else:
  354. self._Connection['OutstandingResponses'][packet['MessageID']] = packet
  355. return self.recvSMB(packetID)
  356. def negotiateSession(self, preferredDialect = None):
  357. packet = self.SMB_PACKET()
  358. packet['Command'] = SMB2_NEGOTIATE
  359. negSession = SMB2Negotiate()
  360. negSession['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
  361. if self.RequireMessageSigning is True:
  362. negSession['SecurityMode'] |= SMB2_NEGOTIATE_SIGNING_REQUIRED
  363. negSession['Capabilities'] = SMB2_GLOBAL_CAP_ENCRYPTION
  364. negSession['ClientGuid'] = self.ClientGuid
  365. if preferredDialect is not None:
  366. negSession['Dialects'] = [preferredDialect]
  367. else:
  368. negSession['Dialects'] = [SMB2_DIALECT_002, SMB2_DIALECT_21, SMB2_DIALECT_30]
  369. negSession['DialectCount'] = len(negSession['Dialects'])
  370. packet['Data'] = negSession
  371. # Storing this data for later use
  372. self._Connection['ClientSecurityMode'] = negSession['SecurityMode']
  373. self._Connection['Capabilities'] = negSession['Capabilities']
  374. packetID = self.sendSMB(packet)
  375. ans = self.recvSMB(packetID)
  376. if ans.isValidAnswer(STATUS_SUCCESS):
  377. # ToDo this:
  378. # If the DialectRevision in the SMB2 NEGOTIATE Response is 0x02FF, the client MUST issue a new
  379. # SMB2 NEGOTIATE request as described in section 3.2.4.2.2.2 with the only exception
  380. # that the client MUST allocate sequence number 1 from Connection.SequenceWindow, and MUST set
  381. # MessageId field of the SMB2 header to 1. Otherwise, the client MUST proceed as follows.
  382. negResp = SMB2Negotiate_Response(ans['Data'])
  383. self._Connection['MaxTransactSize'] = min(0x100000,negResp['MaxTransactSize'])
  384. self._Connection['MaxReadSize'] = min(0x100000,negResp['MaxReadSize'])
  385. self._Connection['MaxWriteSize'] = min(0x100000,negResp['MaxWriteSize'])
  386. self._Connection['ServerGuid'] = negResp['ServerGuid']
  387. self._Connection['GSSNegotiateToken'] = negResp['Buffer']
  388. self._Connection['Dialect'] = negResp['DialectRevision']
  389. if (negResp['SecurityMode'] & SMB2_NEGOTIATE_SIGNING_REQUIRED) == SMB2_NEGOTIATE_SIGNING_REQUIRED:
  390. self._Connection['RequireSigning'] = True
  391. if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LEASING) == SMB2_GLOBAL_CAP_LEASING:
  392. self._Connection['SupportsFileLeasing'] = True
  393. if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_LARGE_MTU) == SMB2_GLOBAL_CAP_LARGE_MTU:
  394. self._Connection['SupportsMultiCredit'] = True
  395. if self._Connection['Dialect'] == SMB2_DIALECT_30:
  396. # Switching to the right packet format
  397. self.SMB_PACKET = SMB3Packet
  398. if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) == SMB2_GLOBAL_CAP_DIRECTORY_LEASING:
  399. self._Connection['SupportsDirectoryLeasing'] = True
  400. if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_MULTI_CHANNEL) == SMB2_GLOBAL_CAP_MULTI_CHANNEL:
  401. self._Connection['SupportsMultiChannel'] = True
  402. if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES) == SMB2_GLOBAL_CAP_PERSISTENT_HANDLES:
  403. self._Connection['SupportsPersistentHandles'] = True
  404. if (negResp['Capabilities'] & SMB2_GLOBAL_CAP_ENCRYPTION) == SMB2_GLOBAL_CAP_ENCRYPTION:
  405. self._Connection['SupportsEncryption'] = True
  406. self._Connection['ServerCapabilities'] = negResp['Capabilities']
  407. self._Connection['ServerSecurityMode'] = negResp['SecurityMode']
  408. def getCredentials(self):
  409. return (
  410. self.__userName,
  411. self.__password,
  412. self.__domain,
  413. self.__lmhash,
  414. self.__nthash,
  415. self.__aesKey,
  416. self.__TGT,
  417. self.__TGS)
  418. def kerberosLogin(self, user, password, domain = '', lmhash = '', nthash = '', aesKey='', kdcHost = '', TGT=None, TGS=None):
  419. # If TGT or TGS are specified, they are in the form of:
  420. # TGS['KDC_REP'] = the response from the server
  421. # TGS['cipher'] = the cipher used
  422. # TGS['sessionKey'] = the sessionKey
  423. # If we have hashes, normalize them
  424. if lmhash != '' or nthash != '':
  425. if len(lmhash) % 2: lmhash = '0%s' % lmhash
  426. if len(nthash) % 2: nthash = '0%s' % nthash
  427. try: # just in case they were converted already
  428. lmhash = a2b_hex(lmhash)
  429. nthash = a2b_hex(nthash)
  430. except:
  431. pass
  432. self.__userName = user
  433. self.__password = password
  434. self.__domain = domain
  435. self.__lmhash = lmhash
  436. self.__nthash = nthash
  437. self.__kdc = kdcHost
  438. self.__aesKey = aesKey
  439. self.__TGT = TGT
  440. self.__TGS = TGS
  441. sessionSetup = SMB2SessionSetup()
  442. if self.RequireMessageSigning is True:
  443. sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED
  444. else:
  445. sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
  446. sessionSetup['Flags'] = 0
  447. #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS
  448. # Importing down here so pyasn1 is not required if kerberos is not used.
  449. from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set
  450. from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
  451. from impacket.krb5 import constants
  452. from impacket.krb5.types import Principal, KerberosTime, Ticket
  453. from pyasn1.codec.der import decoder, encoder
  454. import datetime
  455. # First of all, we need to get a TGT for the user
  456. userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
  457. if TGT is None:
  458. if TGS is None:
  459. tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost)
  460. else:
  461. tgt = TGT['KDC_REP']
  462. cipher = TGT['cipher']
  463. sessionKey = TGT['sessionKey']
  464. # Save the ticket
  465. # If you want, for debugging purposes
  466. # from impacket.krb5.ccache import CCache
  467. # ccache = CCache()
  468. # try:
  469. # if TGS is None:
  470. # ccache.fromTGT(tgt, oldSessionKey, sessionKey)
  471. # else:
  472. # ccache.fromTGS(TGS['KDC_REP'], TGS['oldSessionKey'], TGS['sessionKey'] )
  473. # ccache.saveFile('/tmp/ticket.bin')
  474. # except Exception, e:
  475. # print e
  476. # pass
  477. # Now that we have the TGT, we should ask for a TGS for cifs
  478. if TGS is None:
  479. serverName = Principal('cifs/%s' % (self._Connection['ServerName']), type=constants.PrincipalNameType.NT_SRV_INST.value)
  480. tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey)
  481. else:
  482. tgs = TGS['KDC_REP']
  483. cipher = TGS['cipher']
  484. sessionKey = TGS['sessionKey']
  485. # Let's build a NegTokenInit with a Kerberos REQ_AP
  486. blob = SPNEGO_NegTokenInit()
  487. # Kerberos
  488. blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]
  489. # Let's extract the ticket from the TGS
  490. tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0]
  491. ticket = Ticket()
  492. ticket.from_asn1(tgs['ticket'])
  493. # Now let's build the AP_REQ
  494. apReq = AP_REQ()
  495. apReq['pvno'] = 5
  496. apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)
  497. opts = list()
  498. apReq['ap-options'] = constants.encodeFlags(opts)
  499. seq_set(apReq,'ticket', ticket.to_asn1)
  500. authenticator = Authenticator()
  501. authenticator['authenticator-vno'] = 5
  502. authenticator['crealm'] = domain
  503. seq_set(authenticator, 'cname', userName.components_to_asn1)
  504. now = datetime.datetime.utcnow()
  505. authenticator['cusec'] = now.microsecond
  506. authenticator['ctime'] = KerberosTime.to_asn1(now)
  507. encodedAuthenticator = encoder.encode(authenticator)
  508. # Key Usage 11
  509. # AP-REQ Authenticator (includes application authenticator
  510. # subkey), encrypted with the application session key
  511. # (Section 5.5.1)
  512. encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None)
  513. apReq['authenticator'] = None
  514. apReq['authenticator']['etype'] = cipher.enctype
  515. apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator
  516. blob['MechToken'] = encoder.encode(apReq)
  517. sessionSetup['SecurityBufferLength'] = len(blob)
  518. sessionSetup['Buffer'] = blob.getData()
  519. packet = self.SMB_PACKET()
  520. packet['Command'] = SMB2_SESSION_SETUP
  521. packet['Data'] = sessionSetup
  522. packetID = self.sendSMB(packet)
  523. ans = self.recvSMB(packetID)
  524. if ans.isValidAnswer(STATUS_SUCCESS):
  525. self._Session['SessionID'] = ans['SessionID']
  526. self._Session['SigningRequired'] = self._Connection['RequireSigning']
  527. self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash)
  528. self._Session['Connection'] = self._NetBIOSSession.get_socket()
  529. self._Session['SessionKey'] = sessionKey.contents[:16]
  530. if self._Session['SigningRequired'] is True and self._Connection['Dialect'] == SMB2_DIALECT_30:
  531. self._Session['SigningKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCMAC\x00", "SmbSign\x00", 128)
  532. # Calculate the key derivations for dialect 3.0
  533. if self._Session['SigningRequired'] is True:
  534. self._Session['SigningActivated'] = True
  535. if self._Connection['Dialect'] == SMB2_DIALECT_30:
  536. self._Session['ApplicationKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2APP\x00", "SmbRpc\x00", 128)
  537. self._Session['EncryptionKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCCM\x00", "ServerIn \x00", 128)
  538. self._Session['DecryptionKey'] = crypto.KDF_CounterMode(self._Session['SessionKey'], "SMB2AESCCM\x00", "ServerOut\x00", 128)
  539. return True
  540. else:
  541. # We clean the stuff we used in case we want to authenticate again
  542. # within the same connection
  543. self._Session['UserCredentials'] = ''
  544. self._Session['Connection'] = 0
  545. self._Session['SessionID'] = 0
  546. self._Session['SigningRequired'] = False
  547. self._Session['SigningKey'] = ''
  548. self._Session['SessionKey'] = ''
  549. self._Session['SigningActivated'] = False
  550. raise
  551. def login(self, user, password, domain = '', lmhash = '', nthash = ''):
  552. # If we have hashes, normalize them
  553. if lmhash != '' or nthash != '':
  554. if len(lmhash) % 2: lmhash = '0%s' % lmhash
  555. if len(nthash) % 2: nthash = '0%s' % nthash
  556. try: # just in case they were converted already
  557. lmhash = a2b_hex(lmhash)
  558. nthash = a2b_hex(nthash)
  559. except:
  560. pass
  561. self.__userName = user
  562. self.__password = password
  563. self.__domain = domain
  564. self.__lmhash = lmhash
  565. self.__nthash = nthash
  566. self.__aesKey = ''
  567. self.__TGT = None
  568. self.__TGS = None
  569. sessionSetup = SMB2SessionSetup()
  570. if self.RequireMessageSigning is True:
  571. sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_REQUIRED
  572. else:
  573. sessionSetup['SecurityMode'] = SMB2_NEGOTIATE_SIGNING_ENABLED
  574. sessionSetup['Flags'] = 0
  575. #sessionSetup['Capabilities'] = SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_DFS
  576. # Let's build a NegTokenInit with the NTLMSSP
  577. # TODO: In the future we should be able to choose different providers
  578. blob = SPNEGO_NegTokenInit()
  579. # NTLMSSP
  580. blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
  581. auth = ntlm.getNTLMSSPType1('','', self._Connection['RequireSigning'])
  582. blob['MechToken'] = str(auth)
  583. sessionSetup['SecurityBufferLength'] = len(blob)
  584. sessionSetup['Buffer'] = blob.getData()
  585. # ToDo:
  586. # If this authentication is for establishing an alternative channel for an existing Session, as specified
  587. # in section 3.2.4.1.7, the client MUST also set the following values:
  588. # The SessionId field in the SMB2 header MUST be set to the Session.SessionId for the new
  589. # channel being established.
  590. # The SMB2_SESSION_FLAG_BINDING bit MUST be set in the Flags field.
  591. # The PreviousSessionId field MUST be set to zero.
  592. packet = self.SMB_PACKET()
  593. packet['Command'] = SMB2_SESSION_SETUP
  594. packet['Data'] = sessionSetup
  595. packetID = self.sendSMB(packet)
  596. ans = self.recvSMB(packetID)
  597. if ans.isValidAnswer(STATUS_MORE_PROCESSING_REQUIRED):
  598. self._Session['SessionID'] = ans['SessionID']
  599. self._Session['SigningRequired'] = self._Connection['RequireSigning']
  600. self._Session['UserCredentials'] = (user, password, domain, lmhash, nthash)
  601. self._Session['Connection'] = self._NetBIOSSession.get_socket()
  602. sessionSetupResponse = SMB2SessionSetup_Response(ans['Data'])
  603. respToken = SPNEGO_NegTokenResp(sessionSetupResponse['Buffer'])
  604. # Let's parse some data and keep it to ourselves in case it is asked
  605. ntlmChallenge = ntlm.NTLMAuthChallenge(respToken['ResponseToken'])
  606. if ntlmChallenge['TargetInfoFields_len'] > 0:
  607. av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']])
  608. if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None:
  609. try:
  610. self._Session['ServerName'] = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le')
  611. except:
  612. # For some reason, we couldn't decode Unicode here.. silently discard the operation
  613. pass
  614. if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None:
  615. try:
  616. if self._Session['ServerName'] != av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le'):
  617. self._Session['ServerDomain'] = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le')
  618. except:
  619. # For some reason, we couldn't decode Unicode here.. silently discard the operation
  620. pass
  621. if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None:
  622. try:
  623. self._Session['ServerDNSDomainName'] = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode('utf-16le')
  624. except:
  625. # For some reason, we couldn't decode Unicode here.. silently discard the operation
  626. pass
  627. # Parse Version to know the target Operating system name. Not provided elsewhere anymore
  628. if 'Version' in ntlmChallenge.fields:
  629. version = ntlmChallenge['Version']
  630. if len(version) >= 4:
  631. self._Session['ServerOS'] = "Windows %d.%d Build %d" % (ord(version[0]), ord(version[1]), struct.unpack('<H',version[2:4])[0])
  632. self._Session["ServerOSMajor"] = ord(version[0])
  633. self._Session["ServerOSMinor"] = ord(version[1])
  634. self._Session["ServerOSBuild"] = struct.unpack('<H',version[2:4])[0]
  635. type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, respToken['ResponseToken'], user, password, domain, lmhash, nthash)
  636. if exportedSessionKey is not None:
  637. self._Session['SessionKey'] = exportedSessionKey
  638. if self._Session['SigningRequired'] is True and self._Connection['Dialect'] == SMB2_DIALECT_30:
  639. self._Session['SigningKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCMAC\x00", "SmbSign\x00", 128)
  640. respToken2 = SPNEGO_NegTokenResp()
  641. respToken2['ResponseToken'] = str(type3)
  642. # Reusing the previous structure
  643. sessionSetup['SecurityBufferLength'] = len(respToken2)
  644. sessionSetup['Buffer'] = respToken2.getData()
  645. packetID = self.sendSMB(packet)
  646. packet = self.recvSMB(packetID)
  647. try:
  648. if packet.isValidAnswer(STATUS_SUCCESS):
  649. sessionSetupResponse = SMB2SessionSetup_Response(packet['Data'])
  650. self._Session['SessionFlags'] = sessionSetupResponse['SessionFlags']
  651. # Calculate the key derivations for dialect 3.0
  652. if self._Session['SigningRequired'] is True:
  653. self._Session['SigningActivated'] = True
  654. if self._Connection['Dialect'] == SMB2_DIALECT_30:
  655. self._Session['ApplicationKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2APP\x00", "SmbRpc\x00", 128)
  656. self._Session['EncryptionKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCCM\x00", "ServerIn \x00", 128)
  657. self._Session['DecryptionKey'] = crypto.KDF_CounterMode(exportedSessionKey, "SMB2AESCCM\x00", "ServerOut\x00", 128)
  658. return True
  659. except:
  660. # We clean the stuff we used in case we want to authenticate again
  661. # within the same connection
  662. self._Session['UserCredentials'] = ''
  663. self._Session['Connection'] = 0
  664. self._Session['SessionID'] = 0
  665. self._Session['SigningRequired'] = False
  666. self._Session['SigningKey'] = ''
  667. self._Session['SessionKey'] = ''
  668. self._Session['SigningActivated'] = False
  669. raise
  670. def connectTree(self, share):
  671. # Just in case this came with the full path (maybe an SMB1 client), let's just leave
  672. # the sharename, we'll take care of the rest
  673. #print self._Session['TreeConnectTable']
  674. share = share.split('\\')[-1]
  675. if share in self._Session['TreeConnectTable']:
  676. # Already connected, no need to reconnect
  677. treeEntry = self._Session['TreeConnectTable'][share]
  678. treeEntry['NumberOfUses'] += 1
  679. self._Session['TreeConnectTable'][treeEntry['TreeConnectId']]['NumberOfUses'] += 1
  680. return treeEntry['TreeConnectId']
  681. #path = share
  682. try:
  683. _, _, _, _, sockaddr = socket.getaddrinfo(self._Connection['ServerIP'], 80, 0, 0, socket.IPPROTO_TCP)[0]
  684. remoteHost = sockaddr[0]
  685. except:
  686. remoteHost = self._Connection['ServerIP']
  687. path = '\\\\' + remoteHost + '\\' +share
  688. treeConnect = SMB2TreeConnect()
  689. treeConnect['Buffer'] = path.encode('utf-16le')
  690. treeConnect['PathLength'] = len(path)*2
  691. packet = self.SMB_PACKET()
  692. packet['Command'] = SMB2_TREE_CONNECT
  693. packet['Data'] = treeConnect
  694. packetID = self.sendSMB(packet)
  695. packet = self.recvSMB(packetID)
  696. if packet.isValidAnswer(STATUS_SUCCESS):
  697. treeConnectResponse = SMB2TreeConnect_Response(packet['Data'])
  698. treeEntry = copy.deepcopy(TREE_CONNECT)
  699. treeEntry['ShareName'] = share
  700. treeEntry['TreeConnectId'] = packet['TreeID']
  701. treeEntry['Session'] = packet['SessionID']
  702. treeEntry['NumberOfUses'] += 1
  703. if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_DFS) == SMB2_SHARE_CAP_DFS:
  704. treeEntry['IsDfsShare'] = True
  705. if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY) == SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY:
  706. treeEntry['IsCAShare'] = True
  707. if self._Connection['Dialect'] == SMB2_DIALECT_30:
  708. if (self._Connection['SupportsEncryption'] is True) and ((treeConnectResponse['ShareFlags'] & SMB2_SHAREFLAG_ENCRYPT_DATA) == SMB2_SHAREFLAG_ENCRYPT_DATA):
  709. treeEntry['EncryptData'] = True
  710. # ToDo: This and what follows
  711. # If Session.EncryptData is FALSE, the client MUST then generate an encryption key, a
  712. # decryption key as specified in section 3.1.4.2, by providing the following inputs and store
  713. # them in Session.EncryptionKey and Session.DecryptionKey:
  714. if (treeConnectResponse['Capabilities'] & SMB2_SHARE_CAP_SCALEOUT) == SMB2_SHARE_CAP_SCALEOUT:
  715. treeEntry['IsScaleoutShare'] = True
  716. self._Session['TreeConnectTable'][packet['TreeID']] = treeEntry
  717. self._Session['TreeConnectTable'][share] = treeEntry
  718. return packet['TreeID']
  719. def disconnectTree(self, treeId):
  720. if (treeId in self._Session['TreeConnectTable']) is False:
  721. raise SessionError(STATUS_INVALID_PARAMETER)
  722. if treeId in self._Session['TreeConnectTable']:
  723. # More than 1 use? descrease it and return, if not, send the packet
  724. if self._Session['TreeConnectTable'][treeId]['NumberOfUses'] > 1:
  725. treeEntry = self._Session['TreeConnectTable'][treeId]
  726. treeEntry['NumberOfUses'] -= 1
  727. self._Session['TreeConnectTable'][treeEntry['ShareName']]['NumberOfUses'] -= 1
  728. return True
  729. packet = self.SMB_PACKET()
  730. packet['Command'] = SMB2_TREE_DISCONNECT
  731. packet['TreeID'] = treeId
  732. treeDisconnect = SMB2TreeDisconnect()
  733. packet['Data'] = treeDisconnect
  734. packetID = self.sendSMB(packet)
  735. packet = self.recvSMB(packetID)
  736. if packet.isValidAnswer(STATUS_SUCCESS):
  737. shareName = self._Session['TreeConnectTable'][treeId]['ShareName']
  738. del(self._Session['TreeConnectTable'][shareName])
  739. del(self._Session['TreeConnectTable'][treeId])
  740. return True
  741. def create(self, treeId, fileName, desiredAccess, shareMode, creationOptions, creationDisposition, fileAttributes, impersonationLevel = SMB2_IL_IMPERSONATION, securityFlags = 0, oplockLevel = SMB2_OPLOCK_LEVEL_NONE, createContexts = None):
  742. if (treeId in self._Session['TreeConnectTable']) is False:
  743. raise SessionError(STATUS_INVALID_PARAMETER)
  744. fileName = string.replace(fileName, '/', '\\')
  745. if len(fileName) > 0:
  746. fileName = ntpath.normpath(fileName)
  747. if fileName[0] == '\\':
  748. fileName = fileName[1:]
  749. if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True:
  750. pathName = fileName
  751. else:
  752. pathName = '\\\\' + self._Connection['ServerName'] + '\\' + fileName
  753. fileEntry = copy.deepcopy(FILE)
  754. fileEntry['LeaseKey'] = uuid.generate()
  755. fileEntry['LeaseState'] = SMB2_LEASE_NONE
  756. self.GlobalFileTable[pathName] = fileEntry
  757. if self._Connection['Dialect'] == SMB2_DIALECT_30 and self._Connection['SupportsDirectoryLeasing'] is True:
  758. # Is this file NOT on the root directory?
  759. if len(fileName.split('\\')) > 2:
  760. parentDir = ntpath.dirname(pathName)
  761. if parentDir in self.GlobalFileTable:
  762. LOG.critical("Don't know what to do now! :-o")
  763. raise
  764. else:
  765. parentEntry = copy.deepcopy(FILE)
  766. parentEntry['LeaseKey'] = uuid.generate()
  767. parentEntry['LeaseState'] = SMB2_LEASE_NONE
  768. self.GlobalFileTable[parentDir] = parentEntry
  769. packet = self.SMB_PACKET()
  770. packet['Command'] = SMB2_CREATE
  771. packet['TreeID'] = treeId
  772. if self._Session['TreeConnectTable'][treeId]['IsDfsShare'] is True:
  773. packet['Flags'] = SMB2_FLAGS_DFS_OPERATIONS
  774. smb2Create = SMB2Create()
  775. smb2Create['SecurityFlags'] = 0
  776. smb2Create['RequestedOplockLevel'] = oplockLevel
  777. smb2Create['ImpersonationLevel'] = impersonationLevel
  778. smb2Create['DesiredAccess'] = desiredAccess
  779. smb2Create['FileAttributes'] = fileAttributes
  780. smb2Create['ShareAccess'] = shareMode
  781. smb2Create['CreateDisposition'] = creationDisposition
  782. smb2Create['CreateOptions'] = creationOptions
  783. smb2Create['NameLength'] = len(fileName)*2
  784. if fileName != '':
  785. smb2Create['Buffer'] = fileName.encode('utf-16le')
  786. else:
  787. smb2Create['Buffer'] = '\x00'
  788. if createContexts is not None:
  789. smb2Create['Buffer'] += createContexts
  790. smb2Create['CreateContextsOffset'] = len(SMB2Packet()) + SMB2Create.SIZE + smb2Create['NameLength']
  791. smb2Create['CreateContextsLength'] = len(createContexts)
  792. else:
  793. smb2Create['CreateContextsOffset'] = 0
  794. smb2Create['CreateContextsLength'] = 0
  795. packet['Data'] = smb2Create
  796. packetID = self.sendSMB(packet)
  797. ans = self.recvSMB(packetID)
  798. if ans.isValidAnswer(STATUS_SUCCESS):
  799. createResponse = SMB2Create_Response(ans['Data'])
  800. openFile = copy.deepcopy(OPEN)
  801. openFile['FileID'] = createResponse['FileID']
  802. openFile['TreeConnect'] = treeId
  803. openFile['Oplocklevel'] = oplockLevel
  804. openFile['Durable'] = False
  805. openFile['ResilientHandle'] = False
  806. openFile['LastDisconnectTime'] = 0
  807. openFile['FileName'] = pathName
  808. # ToDo: Complete the OperationBuckets
  809. if self._Connection['Dialect'] == SMB2_DIALECT_30:
  810. openFile['DesiredAccess'] = oplockLevel
  811. openFile['ShareMode'] = oplockLevel
  812. openFile['CreateOptions'] = oplockLevel
  813. openFile['FileAttributes'] = oplockLevel
  814. openFile['CreateDisposition'] = oplockLevel
  815. # ToDo: Process the contexts
  816. self._Session['OpenTable'][str(createResponse['FileID'])] = openFile
  817. # The client MUST generate a handle for the Open, and it MUST
  818. # return success and the generated handle to the calling application.
  819. # In our case, str(FileID)
  820. return str(createResponse['FileID'])
  821. def close(self, treeId, fileId):
  822. if (treeId in self._Session['TreeConnectTable']) is False:
  823. raise SessionError(STATUS_INVALID_PARAMETER)
  824. if (fileId in self._Session['OpenTable']) is False:
  825. raise SessionError(STATUS_INVALID_PARAMETER)
  826. packet = self.SMB_PACKET()
  827. packet['Command'] = SMB2_CLOSE
  828. packet['TreeID'] = treeId
  829. smbClose = SMB2Close()
  830. smbClose['Flags'] = 0
  831. smbClose['FileID'] = fileId
  832. packet['Data'] = smbClose
  833. packetID = self.sendSMB(packet)
  834. ans = self.recvSMB(packetID)
  835. if ans.isValidAnswer(STATUS_SUCCESS):
  836. del(self.GlobalFileTable[self._Session['OpenTable'][fileId]['FileName']])
  837. del(self._Session['OpenTable'][fileId])
  838. # ToDo Remove stuff from GlobalFileTable
  839. return True
  840. def read(self, treeId, fileId, offset = 0, bytesToRead = 0, waitAnswer = True):
  841. # IMPORTANT NOTE: As you can see, this was coded as a recursive function
  842. # Hence, you can exhaust the memory pretty easy ( large bytesToRead )
  843. # This function should NOT be used for reading files directly, but another higher
  844. # level function should be used that will break the read into smaller pieces
  845. if (treeId in self._Session['TreeConnectTable']) is False:
  846. raise SessionError(STATUS_INVALID_PARAMETER)
  847. if (fileId in self._Session['OpenTable']) is False:
  848. raise SessionError(STATUS_INVALID_PARAMETER)
  849. packet = self.SMB_PACKET()
  850. packet['Command'] = SMB2_READ
  851. packet['TreeID'] = treeId
  852. if self._Connection['MaxReadSize'] < bytesToRead:
  853. maxBytesToRead = self._Connection['MaxReadSize']
  854. else:
  855. maxBytesToRead = bytesToRead
  856. if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
  857. packet['CreditCharge'] = ( 1 + (maxBytesToRead - 1) / 65536)
  858. else:
  859. maxBytesToRead = min(65536,bytesToRead)
  860. smbRead = SMB2Read()
  861. smbRead['Padding'] = 0x50
  862. smbRead['FileID'] = fileId
  863. smbRead['Length'] = maxBytesToRead
  864. smbRead['Offset'] = offset
  865. packet['Data'] = smbRead
  866. packetID = self.sendSMB(packet)
  867. ans = self.recvSMB(packetID)
  868. if ans.isValidAnswer(STATUS_SUCCESS):
  869. readResponse = SMB2Read_Response(ans['Data'])
  870. retData = readResponse['Buffer']
  871. if readResponse['DataRemaining'] > 0:
  872. retData += self.read(treeId, fileId, offset+len(retData), readResponse['DataRemaining'], waitAnswer)
  873. return retData
  874. def write(self, treeId, fileId, data, offset = 0, bytesToWrite = 0, waitAnswer = True):
  875. # IMPORTANT NOTE: As you can see, this was coded as a recursive function
  876. # Hence, you can exhaust the memory pretty easy ( large bytesToWrite )
  877. # This function should NOT be used for writing directly to files, but another higher
  878. # level function should be used that will break the writes into smaller pieces
  879. if (treeId in self._Session['TreeConnectTable']) is False:
  880. raise SessionError(STATUS_INVALID_PARAMETER)
  881. if (fileId in self._Session['OpenTable']) is False:
  882. raise SessionError(STATUS_INVALID_PARAMETER)
  883. packet = self.SMB_PACKET()
  884. packet['Command'] = SMB2_WRITE
  885. packet['TreeID'] = treeId
  886. if self._Connection['MaxWriteSize'] < bytesToWrite:
  887. maxBytesToWrite = self._Connection['MaxWriteSize']
  888. else:
  889. maxBytesToWrite = bytesToWrite
  890. if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
  891. packet['CreditCharge'] = ( 1 + (maxBytesToWrite - 1) / 65536)
  892. else:
  893. maxBytesToWrite = min(65536,bytesToWrite)
  894. smbWrite = SMB2Write()
  895. smbWrite['FileID'] = fileId
  896. smbWrite['Length'] = maxBytesToWrite
  897. smbWrite['Offset'] = offset
  898. smbWrite['WriteChannelInfoOffset'] = 0
  899. smbWrite['Buffer'] = data[:maxBytesToWrite]
  900. packet['Data'] = smbWrite
  901. packetID = self.sendSMB(packet)
  902. if waitAnswer is True:
  903. ans = self.recvSMB(packetID)
  904. else:
  905. return maxBytesToWrite
  906. if ans.isValidAnswer(STATUS_SUCCESS):
  907. writeResponse = SMB2Write_Response(ans['Data'])
  908. bytesWritten = writeResponse['Count']
  909. if bytesWritten < bytesToWrite:
  910. bytesWritten += self.write(treeId, fileId, data[bytesWritten:], offset+bytesWritten, bytesToWrite-bytesWritten, waitAnswer)
  911. return bytesWritten
  912. def queryDirectory(self, treeId, fileId, searchString = '*', resumeIndex = 0, informationClass = FILENAMES_INFORMATION, maxBufferSize = None, enumRestart = False, singleEntry = False):
  913. if (treeId in self._Session['TreeConnectTable']) is False:
  914. raise SessionError(STATUS_INVALID_PARAMETER)
  915. if (fileId in self._Session['OpenTable']) is False:
  916. raise SessionError(STATUS_INVALID_PARAMETER)
  917. packet = self.SMB_PACKET()
  918. packet['Command'] = SMB2_QUERY_DIRECTORY
  919. packet['TreeID'] = treeId
  920. queryDirectory = SMB2QueryDirectory()
  921. queryDirectory['FileInformationClass'] = informationClass
  922. if resumeIndex != 0 :
  923. queryDirectory['Flags'] = SMB2_INDEX_SPECIFIED
  924. queryDirectory['FileIndex'] = resumeIndex
  925. queryDirectory['FileID'] = fileId
  926. if maxBufferSize is None:
  927. maxBufferSize = self._Connection['MaxReadSize']
  928. queryDirectory['OutputBufferLength'] = maxBufferSize
  929. queryDirectory['FileNameLength'] = len(searchString)*2
  930. queryDirectory['Buffer'] = searchString.encode('utf-16le')
  931. packet['Data'] = queryDirectory
  932. if self._Connection['Dialect'] != SMB2_DIALECT_002 and self._Connection['SupportsMultiCredit'] is True:
  933. packet['CreditCharge'] = ( 1 + (maxBufferSize - 1) / 65536)
  934. packetID = self.sendSMB(packet)
  935. ans = self.recvSMB(packetID)
  936. if ans.isValidAnswer(STATUS_SUCCESS):
  937. queryDirectoryResponse = SMB2QueryDirectory_Response(ans['Data'])
  938. return queryDirectoryResponse['Buffer']
  939. def echo(self):
  940. packet = self.SMB_PACKET()
  941. packet['Command'] = SMB2_ECHO
  942. smbEcho = SMB2Echo()
  943. packet['Data'] = smbEcho
  944. packetID = self.sendSMB(packet)
  945. ans = self.recvSMB(packetID)
  946. if ans.isValidAnswer(STATUS_SUCCESS):
  947. return True
  948. def cancel(self, packetID):
  949. packet = self.SMB_PACKET()
  950. packet['Command'] = SMB2_CANCEL
  951. packet['MessageID'] = packetID
  952. smbCancel = SMB2Cancel()
  953. packet['Data'] = smbCancel
  954. self.sendSMB(packet)
  955. def ioctl(self, treeId, fileId = None, ctlCode = -1, flags = 0, inputBlob = '', maxInputResponse = None, maxOutputResponse = None, waitAnswer = 1):
  956. if (treeId in self._Session['TreeConnectTable']) is False:
  957. raise SessionError(STATUS_INVALID_PARAMETER)
  958. if fileId is None:
  959. fileId = '\xff'*16
  960. else:
  961. if (fileId in self._Session['OpenTable']) is False:
  962. raise SessionError(STATUS_INVALID_PARAMETER)
  963. packet = self.SMB_PACKET()
  964. packet['Command'] = SMB2_IOCTL
  965. packet['TreeID'] = treeId
  966. smbIoctl = SMB2Ioctl()
  967. smbIoctl['FileID'] = fileId
  968. smbIoctl['CtlCode'] = ctlCode
  969. smbIoctl['MaxInputResponse'] = maxInputResponse
  970. smbIoctl['MaxOutputResponse'] = maxOutputResponse
  971. smbIoctl['InputCount'] = len(inputBlob)
  972. if len(inputBlob) == 0:
  973. smbIoctl['InputOffset'] = 0
  974. smbIoctl['Buffer'] = '\x00'
  975. else:
  976. smbIoctl['Buffer'] = inputBlob
  977. smbIoctl['OutputOffset'] = 0
  978. smbIoctl['MaxOutputResponse'] = maxOutputResponse
  979. smbIoctl['Flags'] = flags
  980. packet['Data'] = smbIoctl
  981. packetID = self.sendSMB(packet)
  982. if waitAnswer == 0:
  983. return True
  984. ans = self.recvSMB(packetID)
  985. if ans.isValidAnswer(STATUS_SUCCESS):
  986. smbIoctlResponse = SMB2Ioctl_Response(ans['Data'])
  987. return smbIoctlResponse['Buffer']
  988. def flush(self,treeId, fileId):
  989. if (treeId in self._Session['TreeConnectTable']) is False:
  990. raise SessionError(STATUS_INVALID_PARAMETER)
  991. if (fileId in self._Session['OpenTable']) is False:
  992. raise SessionError(STATUS_INVALID_PARAMETER)
  993. packet = self.SMB_PACKET()
  994. packet['Command'] = SMB2_FLUSH
  995. packet['TreeID'] = treeId
  996. smbFlush = SMB2Flush()
  997. smbFlush['FileID'] = fileId
  998. packet['Data'] = smbFlush
  999. packetID = self.sendSMB(packet)
  1000. ans = self.recvSMB(packetID)
  1001. if ans.isValidAnswer(STATUS_SUCCESS):
  1002. return True
  1003. def lock(self, treeId, fileId, locks, lockSequence = 0):
  1004. if (treeId in self._Session['TreeConnectTable']) is False:
  1005. raise SessionError(STATUS_INVALID_PARAMETER)
  1006. if (fileId in self._Session['OpenTable']) is False:
  1007. raise SessionError(STATUS_INVALID_PARAMETER)
  1008. packet = self.SMB_PACKET()
  1009. packet['Command'] = SMB2_LOCK
  1010. packet['TreeID'] = treeId
  1011. smbLock = SMB2Lock()
  1012. smbLock['FileID'] = fileId
  1013. smbLock['LockCount'] = len(locks)
  1014. smbLock['LockSequence'] = lockSequence
  1015. smbLock['Locks'] = ''.join(str(x) for x in locks)
  1016. packet['Data'] = smbLock
  1017. packetID = self.sendSMB(packet)
  1018. ans = self.recvSMB(packetID)
  1019. if ans.isValidAnswer(STATUS_SUCCESS):
  1020. smbFlushResponse = SMB2Lock_Response(ans['Data'])
  1021. return True
  1022. # ToDo:
  1023. # If Open.ResilientHandle is TRUE or Connection.SupportsMultiChannel is TRUE, the client MUST
  1024. # do the following:
  1025. # The client MUST scan through Open.OperationBuckets and find an element with its Free field
  1026. # set to TRUE. If no such element could be found, an implementation-specific error MUST be
  1027. # returned to the application.
  1028. # Let the zero-based array index of the element chosen above be referred to as BucketIndex, and
  1029. # let BucketNumber = BucketIndex +1.
  1030. # Set Open.OperationBuckets[BucketIndex].Free = FALSE
  1031. # Let the SequenceNumber of the element chosen above be referred to as BucketSequence.
  1032. # The LockSequence field of the SMB2 lock request MUST be set to (BucketNumber<< 4) +
  1033. # BucketSequence.
  1034. # Increment the SequenceNumber of the element chosen above using MOD 16 arithmetic.
  1035. def logoff(self):
  1036. packet = self.SMB_PACKET()
  1037. packet['Command'] = SMB2_LOGOFF
  1038. smbLogoff = SMB2Logoff()
  1039. packet['Data'] = smbLogoff
  1040. packetID = self.sendSMB(packet)
  1041. ans = self.recvSMB(packetID)
  1042. if ans.isValidAnswer(STATUS_SUCCESS):
  1043. # We clean the stuff we used in case we want to authenticate again
  1044. # within the same connection
  1045. self._Session['UserCredentials'] = ''
  1046. self._Session['Connection'] = 0
  1047. self._Session['SessionID'] = 0
  1048. self._Session['SigningRequired'] = False
  1049. self._Session['SigningKey'] = ''
  1050. self._Session['SessionKey'] = ''
  1051. self._Session['SigningActivated'] = False
  1052. return True
  1053. def queryInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0, flags = 0 ):
  1054. if (treeId in self._Session['TreeConnectTable']) is False:
  1055. raise SessionError(STATUS_INVALID_PARAMETER)
  1056. if (fileId in self._Session['OpenTable']) is False:
  1057. raise SessionError(STATUS_INVALID_PARAMETER)
  1058. packet = self.SMB_PACKET()
  1059. packet['Command'] = SMB2_QUERY_INFO
  1060. packet['TreeID'] = treeId
  1061. queryInfo = SMB2QueryInfo()
  1062. queryInfo['FileID'] = fileId
  1063. queryInfo['InfoType'] = SMB2_0_INFO_FILE
  1064. queryInfo['FileInfoClass'] = fileInfoClass
  1065. queryInfo['OutputBufferLength'] = 65535
  1066. queryInfo['AdditionalInformation'] = additionalInformation
  1067. if len(inputBlob) == 0:
  1068. queryInfo['InputBufferOffset'] = 0
  1069. queryInfo['Buffer'] = '\x00'
  1070. else:
  1071. queryInfo['InputBufferLength'] = len(inputBlob)
  1072. queryInfo['Buffer'] = inputBlob
  1073. queryInfo['Flags'] = flags
  1074. packet['Data'] = queryInfo
  1075. packetID = self.sendSMB(packet)
  1076. ans = self.recvSMB(packetID)
  1077. if ans.isValidAnswer(STATUS_SUCCESS):
  1078. queryResponse = SMB2QueryInfo_Response(ans['Data'])
  1079. return queryResponse['Buffer']
  1080. def setInfo(self, treeId, fileId, inputBlob = '', infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_STANDARD_INFO, additionalInformation = 0 ):
  1081. if (treeId in self._Session['TreeConnectTable']) is False:
  1082. raise SessionError(STATUS_INVALID_PARAMETER)
  1083. if (fileId in self._Session['OpenTable']) is False:
  1084. raise SessionError(STATUS_INVALID_PARAMETER)
  1085. packet = self.SMB_PACKET()
  1086. packet['Command'] = SMB2_SET_INFO
  1087. packet['TreeID'] = treeId
  1088. setInfo = SMB2SetInfo()
  1089. setInfo['InfoType'] = SMB2_0_INFO_FILE
  1090. setInfo['FileInfoClass'] = fileInfoClass
  1091. setInfo['BufferLength'] = len(inputBlob)
  1092. setInfo['AdditionalInformation'] = additionalInformation
  1093. setInfo['FileID'] = fileId
  1094. setInfo['Buffer'] = inputBlob
  1095. packet['Data'] = setInfo
  1096. packetID = self.sendSMB(packet)
  1097. ans = self.recvSMB(packetID)
  1098. if ans.isValidAnswer(STATUS_SUCCESS):
  1099. return True
  1100. def getSessionKey(self):
  1101. if self.getDialect() == SMB2_DIALECT_30:
  1102. return self._Session['ApplicationKey']
  1103. else:
  1104. return self._Session['SessionKey']
  1105. def setSessionKey(self, key):
  1106. if self.getDialect() == SMB2_DIALECT_30:
  1107. self._Session['ApplicationKey'] = key
  1108. else:
  1109. self._Session['SessionKey'] = key
  1110. ######################################################################
  1111. # Higher level functions
  1112. def rename(self, shareName, oldPath, newPath):
  1113. oldPath = string.replace(oldPath,'/', '\\')
  1114. oldPath = ntpath.normpath(oldPath)
  1115. if len(oldPath) > 0 and oldPath[0] == '\\':
  1116. oldPath = oldPath[1:]
  1117. newPath = string.replace(newPath,'/', '\\')
  1118. newPath = ntpath.normpath(newPath)
  1119. if len(newPath) > 0 and newPath[0] == '\\':
  1120. newPath = newPath[1:]
  1121. treeId = self.connectTree(shareName)
  1122. fileId = None
  1123. try:
  1124. fileId = self.create(treeId, oldPath, MAXIMUM_ALLOWED ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, 0x200020, FILE_OPEN, 0)
  1125. renameReq = FILE_RENAME_INFORMATION_TYPE_2()
  1126. renameReq['ReplaceIfExists'] = 1
  1127. renameReq['RootDirectory'] = '\x00'*8
  1128. renameReq['FileNameLength'] = len(newPath)*2
  1129. renameReq['FileName'] = newPath.encode('utf-16le')
  1130. self.setInfo(treeId, fileId, renameReq, infoType = SMB2_0_INFO_FILE, fileInfoClass = SMB2_FILE_RENAME_INFO)
  1131. finally:
  1132. if fileId is not None:
  1133. self.close(treeId, fileId)
  1134. self.disconnectTree(treeId)
  1135. return True
  1136. def writeFile(self, treeId, fileId, data, offset = 0):
  1137. finished = False
  1138. writeOffset = offset
  1139. while not finished:
  1140. if len(data) == 0:
  1141. break
  1142. writeData = data[:self._Connection['MaxWriteSize']]
  1143. data = data[self._Connection['MaxWriteSize']:]
  1144. written = self.write(treeId, fileId, writeData, writeOffset, len(writeData))
  1145. writeOffset += written
  1146. return writeOffset - offset
  1147. def listPath(self, shareName, path, password = None):
  1148. # ToDo: Handle situations where share is password protected
  1149. path = string.replace(path,'/', '\\')
  1150. path = ntpath.normpath(path)
  1151. if len(path) > 0 and path[0] == '\\':
  1152. path = path[1:]
  1153. treeId = self.connectTree(shareName)
  1154. fileId = None
  1155. try:
  1156. # ToDo, we're assuming it's a directory, we should check what the file type is
  1157. fileId = self.create(treeId, ntpath.dirname(path), FILE_READ_ATTRIBUTES | FILE_READ_DATA ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_OPEN, 0)
  1158. res = ''
  1159. files = []
  1160. from impacket import smb
  1161. while True:
  1162. try:
  1163. res = self.queryDirectory( treeId, fileId, ntpath.basename(path), maxBufferSize = 65535, informationClass = FILE_FULL_DIRECTORY_INFORMATION )
  1164. nextOffset = 1
  1165. while nextOffset != 0:
  1166. fileInfo = smb.SMBFindFileFullDirectoryInfo(smb.SMB.FLAGS2_UNICODE)
  1167. fileInfo.fromString(res)
  1168. files.append(smb.SharedFile(fileInfo['CreationTime'],fileInfo['LastAccessTime'],fileInfo['LastChangeTime'],fileInfo['EndOfFile'],fileInfo['AllocationSize'],fileInfo['ExtFileAttributes'],fileInfo['FileName'].decode('utf-16le'), fileInfo['FileName'].decode('utf-16le')))
  1169. nextOffset = fileInfo['NextEntryOffset']
  1170. res = res[nextOffset:]
  1171. except SessionError as e:
  1172. if (e.get_error_code()) != STATUS_NO_MORE_FILES:
  1173. raise
  1174. break
  1175. finally:
  1176. if fileId is not None:
  1177. self.close(treeId, fileId)
  1178. self.disconnectTree(treeId)
  1179. return files
  1180. def mkdir(self, shareName, pathName, password = None):
  1181. # ToDo: Handle situations where share is password protected
  1182. pathName = string.replace(pathName,'/', '\\')
  1183. pathName = ntpath.normpath(pathName)
  1184. if len(pathName) > 0 and pathName[0] == '\\':
  1185. pathName = pathName[1:]
  1186. treeId = self.connectTree(shareName)
  1187. fileId = None
  1188. try:
  1189. fileId = self.create(treeId, pathName,GENERIC_ALL ,FILE_SHARE_READ | FILE_SHARE_WRITE |FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, FILE_CREATE, 0)
  1190. finally:
  1191. if fileId is not None:
  1192. self.close(treeId, fileId)
  1193. self.disconnectTree(treeId)
  1194. return True
  1195. def rmdir(self, shareName, pathName, password = None):
  1196. # ToDo: Handle situations where share is password protected
  1197. pathName = string.replace(pathName,'/', '\\')
  1198. pathName = ntpath.normpath(pathName)
  1199. if len(pathName) > 0 and pathName[0] == '\\':
  1200. pathName = pathName[1:]
  1201. treeId = self.connectTree(shareName)
  1202. fileId = None
  1203. try:
  1204. fileId = self.create(treeId, pathName, DELETE, FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0)
  1205. finally:
  1206. if fileId is not None:
  1207. self.close(treeId, fileId)
  1208. self.disconnectTree(treeId)
  1209. return True
  1210. def remove(self, shareName, pathName, password = None):
  1211. # ToDo: Handle situations where share is password protected
  1212. pathName = string.replace(pathName,'/', '\\')
  1213. pathName = ntpath.normpath(pathName)
  1214. if len(pathName) > 0 and pathName[0] == '\\':
  1215. pathName = pathName[1:]
  1216. treeId = self.connectTree(shareName)
  1217. fileId = None
  1218. try:
  1219. fileId = self.create(treeId, pathName,DELETE | FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE, FILE_NON_DIRECTORY_FILE | FILE_DELETE_ON_CLOSE, FILE_OPEN, 0)
  1220. finally:
  1221. if fileId is not None:
  1222. self.close(treeId, fileId)
  1223. self.disconnectTree(treeId)
  1224. return True
  1225. def retrieveFile(self, shareName, path, callback, mode = FILE_OPEN, offset = 0, password = None, shareAccessMode = FILE_SHARE_READ):
  1226. # ToDo: Handle situations where share is password protected
  1227. path = string.replace(path,'/', '\\')
  1228. path = ntpath.normpath(path)
  1229. if len(path) > 0 and path[0] == '\\':
  1230. path = path[1:]
  1231. treeId = self.connectTree(shareName)
  1232. fileId = None
  1233. from impacket import smb
  1234. try:
  1235. fileId = self.create(treeId, path, FILE_READ_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0)
  1236. res = self.queryInfo(treeId, fileId)
  1237. fileInfo = smb.SMBQueryFileStandardInfo(res)
  1238. fileSize = fileInfo['EndOfFile']
  1239. if (fileSize-offset) < self._Connection['MaxReadSize']:
  1240. # Skip reading 0 bytes files.
  1241. if (fileSize-offset) > 0:
  1242. data = self.read(treeId, fileId, offset, fileSize-offset)
  1243. callback(data)
  1244. else:
  1245. written = 0
  1246. toBeRead = fileSize-offset
  1247. while written < toBeRead:
  1248. data = self.read(treeId, fileId, offset, self._Connection['MaxReadSize'])
  1249. written += len(data)
  1250. offset += len(data)
  1251. callback(data)
  1252. finally:
  1253. if fileId is not None:
  1254. self.close(treeId, fileId)
  1255. self.disconnectTree(treeId)
  1256. def storeFile(self, shareName, path, callback, mode = FILE_OVERWRITE_IF, offset = 0, password = None, shareAccessMode = FILE_SHARE_WRITE):
  1257. # ToDo: Handle situations where share is password protected
  1258. path = string.replace(path,'/', '\\')
  1259. path = ntpath.normpath(path)
  1260. if len(path) > 0 and path[0] == '\\':
  1261. path = path[1:]
  1262. treeId = self.connectTree(shareName)
  1263. fileId = None
  1264. try:
  1265. fileId = self.create(treeId, path, FILE_WRITE_DATA, shareAccessMode, FILE_NON_DIRECTORY_FILE, mode, 0)
  1266. finished = False
  1267. writeOffset = offset
  1268. while not finished:
  1269. data = callback(self._Connection['MaxWriteSize'])
  1270. if len(data) == 0:
  1271. break
  1272. written = self.write(treeId, fileId, data, writeOffset, len(data))
  1273. writeOffset += written
  1274. finally:
  1275. if fileId is not None:
  1276. self.close(treeId, fileId)
  1277. self.disconnectTree(treeId)
  1278. def waitNamedPipe(self, treeId, pipename, timeout = 5):
  1279. pipename = ntpath.basename(pipename)
  1280. if (treeId in self._Session['TreeConnectTable']) is False:
  1281. raise SessionError(STATUS_INVALID_PARAMETER)
  1282. if len(pipename) > 0xffff:
  1283. raise SessionError(STATUS_INVALID_PARAMETER)
  1284. pipeWait = FSCTL_PIPE_WAIT_STRUCTURE()
  1285. pipeWait['Timeout'] = timeout*100000
  1286. pipeWait['NameLength'] = len(pipename)*2
  1287. pipeWait['TimeoutSpecified'] = 1
  1288. pipeWait['Name'] = pipename.encode('utf-16le')
  1289. return self.ioctl(treeId, None, FSCTL_PIPE_WAIT,flags=SMB2_0_IOCTL_IS_FSCTL, inputBlob=pipeWait, maxInputResponse = 0, maxOutputResponse=0)
  1290. def getIOCapabilities(self):
  1291. res = dict()
  1292. res['MaxReadSize'] = self._Connection['MaxReadSize']
  1293. res['MaxWriteSize'] = self._Connection['MaxWriteSize']
  1294. return res
  1295. ######################################################################
  1296. # Backward compatibility functions and alias for SMB1 and DCE Transports
  1297. # NOTE: It is strongly recommended not to use these commands
  1298. # when implementing new client calls.
  1299. get_server_name = getServerName
  1300. get_server_domain = getServerDomain
  1301. get_server_dns_domain_name = getServerDNSDomainName
  1302. get_remote_name = getServerName
  1303. get_remote_host = getServerIP
  1304. get_server_os = getServerOS
  1305. get_server_os_major = getServerOSMajor
  1306. get_server_os_minor = getServerOSMinor
  1307. get_server_os_build = getServerOSBuild
  1308. tree_connect_andx = connectTree
  1309. tree_connect = connectTree
  1310. connect_tree = connectTree
  1311. disconnect_tree = disconnectTree
  1312. set_timeout = setTimeout
  1313. use_timeout = useTimeout
  1314. stor_file = storeFile
  1315. retr_file = retrieveFile
  1316. list_path = listPath
  1317. def __del__(self):
  1318. if self._NetBIOSSession:
  1319. self._NetBIOSSession.close()
  1320. def doesSupportNTLMv2(self):
  1321. # Always true :P
  1322. return True
  1323. def is_login_required(self):
  1324. # Always true :P
  1325. return True
  1326. def is_signing_required(self):
  1327. return self._Session["SigningRequired"]
  1328. def nt_create_andx(self, treeId, fileName, smb_packet=None, cmd = None):
  1329. if len(fileName) > 0 and fileName[0] == '\\':
  1330. fileName = fileName[1:]
  1331. if cmd is not None:
  1332. from impacket import smb
  1333. ntCreate = smb.SMBCommand(data = str(cmd))
  1334. params = smb.SMBNtCreateAndX_Parameters(ntCreate['Parameters'])
  1335. return self.create(treeId, fileName, params['AccessMask'], params['ShareAccess'],
  1336. params['CreateOptions'], params['Disposition'], params['FileAttributes'],
  1337. params['Impersonation'], params['SecurityFlags'])
  1338. else:
  1339. return self.create(treeId, fileName,
  1340. FILE_READ_DATA | FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_READ_EA |
  1341. FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | READ_CONTROL,
  1342. FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE, FILE_OPEN, 0 )
  1343. def get_socket(self):
  1344. return self._NetBIOSSession.get_socket()
  1345. def write_andx(self,tid,fid,data, offset = 0, wait_answer=1, write_pipe_mode = False, smb_packet=None):
  1346. # ToDo: Handle the custom smb_packet situation
  1347. return self.write(tid, fid, data, offset, len(data))
  1348. def TransactNamedPipe(self, tid, fid, data, noAnswer = 0, waitAnswer = 1, offset = 0):
  1349. return self.ioctl(tid, fid, FSCTL_PIPE_TRANSCEIVE, SMB2_0_IOCTL_IS_FSCTL, data, maxOutputResponse = 65535, waitAnswer = noAnswer | waitAnswer)
  1350. def TransactNamedPipeRecv(self):
  1351. ans = self.recvSMB()
  1352. if ans.isValidAnswer(STATUS_SUCCESS):
  1353. smbIoctlResponse = SMB2Ioctl_Response(ans['Data'])
  1354. return smbIoctlResponse['Buffer']
  1355. def read_andx(self, tid, fid, offset=0, max_size = None, wait_answer=1, smb_packet=None):
  1356. # ToDo: Handle the custom smb_packet situation
  1357. if max_size is None:
  1358. max_size = self._Connection['MaxReadSize']
  1359. return self.read(tid, fid, offset, max_size, wait_answer)
  1360. def list_shared(self):
  1361. # In the context of SMB2/3, forget about the old LANMAN, throw NOT IMPLEMENTED
  1362. raise SessionError(STATUS_NOT_IMPLEMENTED)
  1363. def open_andx(self, tid, fileName, open_mode, desired_access):
  1364. # ToDo Return all the attributes of the file
  1365. if len(fileName) > 0 and fileName[0] == '\\':
  1366. fileName = fileName[1:]
  1367. fileId = self.create(tid,fileName,desired_access, open_mode, FILE_NON_DIRECTORY_FILE, open_mode, 0)
  1368. return fileId, 0, 0, 0, 0, 0, 0, 0, 0