smb3.py 70 KB

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