mirror of
https://github.com/fortra/impacket
synced 2026-06-08 14:15:13 +00:00
5825cb8a9d
Co-authored-by: abyss <abyss@abyssdeMBP.lan>
4666 lines
171 KiB
Python
4666 lines
171 KiB
Python
# Impacket - Collection of Python classes for working with network protocols.
|
|
#
|
|
# Copyright Fortra, LLC and its affiliated companies
|
|
#
|
|
# All rights reserved.
|
|
#
|
|
# This software is provided under a slightly modified version
|
|
# of the Apache Software License. See the accompanying LICENSE file
|
|
# for more information.
|
|
#
|
|
# Author:
|
|
# Altered source done by Alberto Solino (@agsolino)
|
|
#
|
|
# Contributors:
|
|
# Raz Kissos (@covertivy)
|
|
#
|
|
# Copyright and license note from Pysmb:
|
|
#
|
|
# Copyright (C) 2001 Michael Teo <michaelteo@bigfoot.com>
|
|
# smb.py - SMB/CIFS library
|
|
#
|
|
# This software is provided 'as-is', without any express or implied warranty.
|
|
# In no event will the author be held liable for any damages arising from the
|
|
# use of this software.
|
|
#
|
|
# Permission is granted to anyone to use this software for any purpose,
|
|
# including commercial applications, and to alter it and redistribute it
|
|
# freely, subject to the following restrictions:
|
|
#
|
|
# 1. The origin of this software must not be misrepresented; you must not
|
|
# claim that you wrote the original software. If you use this software
|
|
# in a product, an acknowledgment in the product documentation would be
|
|
# appreciated but is not required.
|
|
#
|
|
# 2. Altered source versions must be plainly marked as such, and must not be
|
|
# misrepresented as being the original software.
|
|
#
|
|
# 3. This notice cannot be removed or altered from any source distribution.
|
|
#
|
|
# Todo:
|
|
# [ ] Try [SMB]transport fragmentation using Transact requests
|
|
# [ ] Try other methods of doing write (write_raw, transact2, write, write_and_unlock, write_and_close, write_mpx)
|
|
# [-] Try replacements for SMB_COM_NT_CREATE_ANDX (CREATE, T_TRANSACT_CREATE, OPEN_ANDX works
|
|
# [x] Fix forceWriteAndx, which needs to send a RecvRequest, because recv() will not send it
|
|
# [x] Fix Recv() when using RecvAndx and the answer comes splet in several packets
|
|
# [ ] Try [SMB]transport fragmentation with overlapping segments
|
|
# [ ] Try [SMB]transport fragmentation with out of order segments
|
|
# [x] Do chained AndX requests
|
|
# [ ] Transform the rest of the calls to structure
|
|
# [X] Implement TRANS/TRANS2 reassembly for list_path
|
|
#
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
|
|
import os
|
|
import socket
|
|
from binascii import a2b_hex
|
|
import datetime
|
|
from struct import pack, unpack, pack_into
|
|
from ctypes import BigEndianStructure, c_uint32
|
|
from contextlib import contextmanager
|
|
from pyasn1.type.univ import noValue
|
|
|
|
from impacket import nmb, ntlm, nt_errors, LOG
|
|
from impacket.structure import Structure
|
|
from impacket.spnego import SPNEGO_NegTokenInit, TypesMech, SPNEGO_NegTokenResp, ASN1_OID, asn1encode, ASN1_AID
|
|
from impacket.krb5.gssapi import KRB5_AP_REQ
|
|
import six
|
|
|
|
# For signing
|
|
import hashlib
|
|
|
|
unicode_support = 0
|
|
unicode_convert = 1
|
|
|
|
# Dialect for SMB1
|
|
SMB_DIALECT = 'NT LM 0.12'
|
|
|
|
# Shared Device Type
|
|
SHARED_DISK = 0x00
|
|
SHARED_DISK_HIDDEN = 0x80000000
|
|
SHARED_PRINT_QUEUE = 0x01
|
|
SHARED_DEVICE = 0x02
|
|
SHARED_IPC = 0x03
|
|
|
|
# Extended attributes mask
|
|
ATTR_ARCHIVE = 0x020
|
|
ATTR_COMPRESSED = 0x800
|
|
ATTR_NORMAL = 0x080
|
|
ATTR_HIDDEN = 0x002
|
|
ATTR_READONLY = 0x001
|
|
ATTR_TEMPORARY = 0x100
|
|
ATTR_DIRECTORY = 0x010
|
|
ATTR_SYSTEM = 0x004
|
|
|
|
# Service Type
|
|
SERVICE_DISK = 'A:'
|
|
SERVICE_PRINTER = 'LPT1:'
|
|
SERVICE_IPC = 'IPC'
|
|
SERVICE_COMM = 'COMM'
|
|
SERVICE_ANY = '?????'
|
|
|
|
# Server Type (Can be used to mask with SMBMachine.get_type() or SMBDomain.get_type())
|
|
SV_TYPE_WORKSTATION = 0x00000001
|
|
SV_TYPE_SERVER = 0x00000002
|
|
SV_TYPE_SQLSERVER = 0x00000004
|
|
SV_TYPE_DOMAIN_CTRL = 0x00000008
|
|
SV_TYPE_DOMAIN_BAKCTRL = 0x00000010
|
|
SV_TYPE_TIME_SOURCE = 0x00000020
|
|
SV_TYPE_AFP = 0x00000040
|
|
SV_TYPE_NOVELL = 0x00000080
|
|
SV_TYPE_DOMAIN_MEMBER = 0x00000100
|
|
SV_TYPE_PRINTQ_SERVER = 0x00000200
|
|
SV_TYPE_DIALIN_SERVER = 0x00000400
|
|
SV_TYPE_XENIX_SERVER = 0x00000800
|
|
SV_TYPE_NT = 0x00001000
|
|
SV_TYPE_WFW = 0x00002000
|
|
SV_TYPE_SERVER_NT = 0x00004000
|
|
SV_TYPE_POTENTIAL_BROWSER = 0x00010000
|
|
SV_TYPE_BACKUP_BROWSER = 0x00020000
|
|
SV_TYPE_MASTER_BROWSER = 0x00040000
|
|
SV_TYPE_DOMAIN_MASTER = 0x00080000
|
|
SV_TYPE_LOCAL_LIST_ONLY = 0x40000000
|
|
SV_TYPE_DOMAIN_ENUM = 0x80000000
|
|
|
|
# Options values for SMB.stor_file and SMB.retr_file
|
|
SMB_O_CREAT = 0x10 # Create the file if file does not exists. Otherwise, operation fails.
|
|
SMB_O_EXCL = 0x00 # When used with SMB_O_CREAT, operation fails if file exists. Cannot be used with SMB_O_OPEN.
|
|
SMB_O_OPEN = 0x01 # Open the file if the file exists
|
|
SMB_O_TRUNC = 0x02 # Truncate the file if the file exists
|
|
|
|
# Share Access Mode
|
|
SMB_SHARE_COMPAT = 0x00
|
|
SMB_SHARE_DENY_EXCL = 0x10
|
|
SMB_SHARE_DENY_WRITE = 0x20
|
|
SMB_SHARE_DENY_READEXEC = 0x30
|
|
SMB_SHARE_DENY_NONE = 0x40
|
|
SMB_ACCESS_READ = 0x00
|
|
SMB_ACCESS_WRITE = 0x01
|
|
SMB_ACCESS_READWRITE = 0x02
|
|
SMB_ACCESS_EXEC = 0x03
|
|
|
|
TRANS_DISCONNECT_TID = 1
|
|
TRANS_NO_RESPONSE = 2
|
|
|
|
STATUS_SUCCESS = 0x00000000
|
|
STATUS_LOGON_FAILURE = 0xC000006D
|
|
STATUS_LOGON_TYPE_NOT_GRANTED = 0xC000015B
|
|
MAX_TFRAG_SIZE = 5840
|
|
EVASION_NONE = 0
|
|
EVASION_LOW = 1
|
|
EVASION_HIGH = 2
|
|
EVASION_MAX = 3
|
|
RPC_X_BAD_STUB_DATA = 0x6F7
|
|
|
|
# SMB_FILE_ATTRIBUTES
|
|
SMB_FILE_ATTRIBUTE_NORMAL = 0x0000
|
|
SMB_FILE_ATTRIBUTE_READONLY = 0x0001
|
|
SMB_FILE_ATTRIBUTE_HIDDEN = 0x0002
|
|
SMB_FILE_ATTRIBUTE_SYSTEM = 0x0004
|
|
SMB_FILE_ATTRIBUTE_VOLUME = 0x0008
|
|
SMB_FILE_ATTRIBUTE_DIRECTORY = 0x0010
|
|
SMB_FILE_ATTRIBUTE_ARCHIVE = 0x0020
|
|
SMB_SEARCH_ATTRIBUTE_READONLY = 0x0100
|
|
SMB_SEARCH_ATTRIBUTE_HIDDEN = 0x0200
|
|
SMB_SEARCH_ATTRIBUTE_SYSTEM = 0x0400
|
|
SMB_SEARCH_ATTRIBUTE_DIRECTORY = 0x1000
|
|
SMB_SEARCH_ATTRIBUTE_ARCHIVE = 0x2000
|
|
|
|
# Session SetupAndX Action flags
|
|
SMB_SETUP_GUEST = 0x01
|
|
SMB_SETUP_USE_LANMAN_KEY = 0x02
|
|
|
|
# QUERY_INFORMATION levels
|
|
SMB_INFO_ALLOCATION = 0x0001
|
|
SMB_INFO_VOLUME = 0x0002
|
|
FILE_FS_SIZE_INFORMATION = 0x0003
|
|
SMB_QUERY_FS_VOLUME_INFO = 0x0102
|
|
SMB_QUERY_FS_SIZE_INFO = 0x0103
|
|
SMB_QUERY_FILE_EA_INFO = 0x0103
|
|
SMB_QUERY_FS_DEVICE_INFO = 0x0104
|
|
SMB_QUERY_FS_ATTRIBUTE_INFO = 0x0105
|
|
SMB_QUERY_FILE_BASIC_INFO = 0x0101
|
|
SMB_QUERY_FILE_STANDARD_INFO = 0x0102
|
|
SMB_QUERY_FILE_ALL_INFO = 0x0107
|
|
SMB_QUERY_FILE_STREAM_INFO = 0x0109
|
|
FILE_FS_FULL_SIZE_INFORMATION = 0x03EF
|
|
|
|
# SET_INFORMATION levels
|
|
SMB_INFO_STANDARD = 0x0001
|
|
SMB_INFO_SET_EAS = 0x0002
|
|
SMB_SET_FILE_BASIC_INFO = 0x0101
|
|
SMB_SET_FILE_DISPOSITION_INFO = 0x0102
|
|
SMB_SET_FILE_ALLOCATION_INFO = 0x0103
|
|
SMB_SET_FILE_END_OF_FILE_INFO = 0x0104
|
|
|
|
# Device Type [MS-CIFS] 2.2.8.2.5
|
|
FILE_DEVICE_BEEP = 0x0001
|
|
FILE_DEVICE_CD_ROM = 0x0002
|
|
FILE_DEVICE_CD_ROM_FILE_SYSTEM = 0x0003
|
|
FILE_DEVICE_CONTROLLER = 0x0004
|
|
FILE_DEVICE_DATALINK = 0x0005
|
|
FILE_DEVICE_DFS = 0x0006
|
|
FILE_DEVICE_DISK = 0x0007
|
|
FILE_DEVICE_DISK_FILE_SYSTEM = 0x0008
|
|
FILE_DEVICE_FILE_SYSTEM = 0x0009
|
|
FILE_DEVICE_INPORT_PORT = 0x000a
|
|
FILE_DEVICE_KEYBOARD = 0x000b
|
|
FILE_DEVICE_MAILSLOT = 0x000c
|
|
FILE_DEVICE_MIDI_IN = 0x000d
|
|
FILE_DEVICE_MIDI_OUT = 0x000e
|
|
FILE_DEVICE_MOUSE = 0x000f
|
|
FILE_DEVICE_MULTI_UNC_PROVIDER = 0x0010
|
|
FILE_DEVICE_NAMED_PIPE = 0x0011
|
|
FILE_DEVICE_NETWORK = 0x0012
|
|
FILE_DEVICE_NETWORK_BROWSER = 0x0013
|
|
FILE_DEVICE_NETWORK_FILE_SYSTEM = 0x0014
|
|
FILE_DEVICE_NULL = 0x0015
|
|
FILE_DEVICE_PARALLEL_PORT = 0x0016
|
|
FILE_DEVICE_PHYSICAL_NETCARD = 0x0017
|
|
FILE_DEVICE_PRINTER = 0x0018
|
|
FILE_DEVICE_SCANNER = 0x0019
|
|
FILE_DEVICE_SERIAL_MOUSE_PORT = 0x001a
|
|
FILE_DEVICE_SERIAL_PORT = 0x001b
|
|
FILE_DEVICE_SCREEN = 0x001c
|
|
FILE_DEVICE_SOUND = 0x001d
|
|
FILE_DEVICE_STREAMS = 0x001e
|
|
FILE_DEVICE_TAPE = 0x001f
|
|
FILE_DEVICE_TAPE_FILE_SYSTEM = 0x0020
|
|
FILE_DEVICE_TRANSPORT = 0x0021
|
|
FILE_DEVICE_UNKNOWN = 0x0022
|
|
FILE_DEVICE_VIDEO = 0x0023
|
|
FILE_DEVICE_VIRTUAL_DISK = 0x0024
|
|
FILE_DEVICE_WAVE_IN = 0x0025
|
|
FILE_DEVICE_WAVE_OUT = 0x0026
|
|
FILE_DEVICE_8042_PORT = 0x0027
|
|
FILE_DEVICE_NETWORK_REDIRECTOR = 0x0028
|
|
FILE_DEVICE_BATTERY = 0x0029
|
|
FILE_DEVICE_BUS_EXTENDER = 0x002a
|
|
FILE_DEVICE_MODEM = 0x002b
|
|
FILE_DEVICE_VDM = 0x002c
|
|
|
|
# Device Characteristics [MS-CIFS] 2.2.8.2.5
|
|
FILE_REMOVABLE_MEDIA = 0x0001
|
|
FILE_READ_ONLY_DEVICE = 0x0002
|
|
FILE_FLOPPY_DISKETTE = 0x0004
|
|
FILE_WRITE_ONCE_MEDIA = 0x0008
|
|
FILE_REMOTE_DEVICE = 0x0010
|
|
FILE_DEVICE_IS_MOUNTED = 0x0020
|
|
FILE_VIRTUAL_VOLUME = 0x0040
|
|
|
|
# File System Attributes
|
|
FILE_CASE_SENSITIVE_SEARCH = 0x00000001
|
|
FILE_CASE_PRESERVED_NAMES = 0x00000002
|
|
FILE_UNICODE_ON_DISK = 0x00000004
|
|
FILE_PERSISTENT_ACLS = 0x00000008
|
|
FILE_FILE_COMPRESSION = 0x00000010
|
|
FILE_VOLUME_IS_COMPRESSED = 0x00008000
|
|
|
|
# FIND_FIRST2 flags and levels
|
|
SMB_FIND_CLOSE_AFTER_REQUEST = 0x0001
|
|
SMB_FIND_CLOSE_AT_EOS = 0x0002
|
|
SMB_FIND_RETURN_RESUME_KEYS = 0x0004
|
|
SMB_FIND_CONTINUE_FROM_LAST = 0x0008
|
|
SMB_FIND_WITH_BACKUP_INTENT = 0x0010
|
|
|
|
FILE_DIRECTORY_FILE = 0x00000001
|
|
FILE_DELETE_ON_CLOSE = 0x00001000
|
|
FILE_NON_DIRECTORY_FILE = 0x00000040
|
|
|
|
SMB_FIND_INFO_STANDARD = 0x0001
|
|
SMB_FIND_FILE_DIRECTORY_INFO = 0x0101
|
|
SMB_FIND_FILE_FULL_DIRECTORY_INFO= 0x0102
|
|
SMB_FIND_FILE_NAMES_INFO = 0x0103
|
|
SMB_FIND_FILE_BOTH_DIRECTORY_INFO= 0x0104
|
|
SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO = 0x105
|
|
SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO = 0x106
|
|
|
|
|
|
# AccessMask (DesiredAccess) flags
|
|
# A 32-bit field of flags that indicate standard, specific, and generic access rights.
|
|
# These rights are used in access-control entries (ACEs) and are the primary means of specifying the requested or granted access to an object.
|
|
# As Specified in section 2.2.4.64.1 SMB_COM_NT_CREATE_ANDX (0xA2) Request
|
|
FILE_READ_DATA = 0x00000001 # Indicates the right to read data from the file.
|
|
FILE_WRITE_DATA = 0x00000002 # Indicates the right to write data into the file beyond the end of the file.
|
|
FILE_APPEND_DATA = 0x00000004 # Indicates the right to append data to the file beyond the end of the file only.
|
|
FILE_READ_EA = 0x00000008 # Indicates the right to read the extended attributes (EAs) of the file.
|
|
FILE_WRITE_EA = 0x00000010 # Indicates the right to write or change the extended attributes (EAs) of the file.
|
|
FILE_EXECUTE = 0x00000020 # Indicates the right to execute the file.
|
|
FILE_READ_ATTRIBUTES = 0x00000080 # Indicates the right to read the attributes of the file.
|
|
FILE_WRITE_ATTRIBUTES = 0x00000100 # Indicates the right to change the attributes of the file.
|
|
DELETE = 0x00010000 # Indicates the right to delete or to rename the file.
|
|
READ_CONTROL = 0x00020000 # Indicates the right to read the security descriptor of the file.
|
|
WRITE_DAC = 0x00040000 # Indicates the right to change the discretionary access control list (DACL) in the security descriptor of the file.
|
|
WRITE_OWNER = 0x00080000 # Indicates the right to change the owner in the security descriptor of the file.
|
|
SYNCHRONIZE = 0x00100000 # SHOULD NOT be used by the sender and MUST be ignored by the receiver.
|
|
ACCESS_SYSTEM_SECURITY = 0x01000000 # Indicates the right to read or change the system access control list (SACL) in the security descriptor for the file. If the SE_SECURITY_NAME privilege is not set in the access token, the server MUST fail the open request and return STATUS_PRIVILEGE_NOT_HELD.
|
|
MAXIMUM_ALLOWED = 0x02000000 # Indicates that the client requests an open to the file with the highest level of access that the client has on this file. If no access is granted for the client on this file, the server MUST fail the open and return a STATUS_ACCESS_DENIED.
|
|
GENERIC_ALL = 0x10000000 # Indicates a request for all of the access flags that are previously listed except MAXIMUM_ALLOWED and ACCESS_SYSTEM_SECURITY.
|
|
GENERIC_EXECUTE = 0x20000000 # Indicates a request for the following combination of access flags listed previously in this table: FILE_READ_ATTRIBUTES, FILE_EXECUTE, SYNCHRONIZE, and READ_CONTROL.
|
|
GENERIC_WRITE = 0x40000000 # Indicates a request for the following combination of access flags listed previously in this table: FILE_WRITE_DATA, FILE_APPEND_DATA, SYNCHRONIZE, FILE_WRITE_ATTRIBUTES, and FILE_WRITE_EA.
|
|
GENERIC_READ = 0x80000000 # Indicates a request for the following combination of access flags listed previously in this table: FILE_READ_DATA, FILE_READ_ATTRIBUTES, FILE_READ_EA, and SYNCHRONIZE.
|
|
|
|
|
|
# ShareAccess flags
|
|
# A 32-bit field that specifies how the file SHOULD be shared with other processes.
|
|
# As Specified in section 2.2.4.64.1 SMB_COM_NT_CREATE_ANDX (0xA2) Request
|
|
FILE_SHARE_NONE = 0x00000000 # No bits set - Prevents the file from being shared.
|
|
FILE_SHARE_READ = 0x00000001 # Other open operations can be performed on the file for read access.
|
|
FILE_SHARE_WRITE = 0x00000002 # Other open operations can be performed on the file for write access.
|
|
FILE_SHARE_DELETE = 0x00000004 # Other open operations can be performed on the file for delete access.
|
|
|
|
|
|
# CreateDisposition flags
|
|
# A 32-bit value that represents the action to take if the file already exists or if the file is a new file and does not already exist.
|
|
# As Specified in section 2.2.4.64.1 SMB_COM_NT_CREATE_ANDX (0xA2) Request
|
|
FILE_SUPERSEDE = 0x00000000 # No bits set - If the file already exists, it SHOULD be superseded (overwritten). If it does not already exist, then it SHOULD be created.
|
|
FILE_OPEN = 0x00000001 # If the file already exists, it SHOULD be opened rather than created. If the file does not already exist, the operation MUST fail.
|
|
FILE_CREATE = 0x00000002 # If the file already exists, the operation MUST fail. If the file does not already exist, it SHOULD be created.
|
|
FILE_OPEN_IF = 0x00000003 # If the file already exists, it SHOULD be opened. If the file does not already exist, then it SHOULD be created. This value is equivalent to (FILE_OPEN | FILE_CREATE).
|
|
FILE_OVERWRITE = 0x00000004 # If the file already exists, it SHOULD be opened and truncated. If the file does not already exist, the operation MUST fail.
|
|
FILE_OVERWRITE_IF = 0x00000005 # If the file already exists, it SHOULD be opened and truncated. If the file does not already exist, it SHOULD be created.
|
|
|
|
|
|
def strerror(errclass, errcode):
|
|
if errclass == 0x01:
|
|
return 'OS error', ERRDOS.get(errcode, 'Unknown error')
|
|
elif errclass == 0x02:
|
|
return 'Server error', ERRSRV.get(errcode, 'Unknown error')
|
|
elif errclass == 0x03:
|
|
return 'Hardware error', ERRHRD.get(errcode, 'Unknown error')
|
|
# This is not a standard error class for SMB
|
|
#elif errclass == 0x80:
|
|
# return 'Browse error', ERRBROWSE.get(errcode, 'Unknown error')
|
|
elif errclass == 0xff:
|
|
return 'Bad command', 'Bad command. Please file bug report'
|
|
else:
|
|
return 'Unknown error', 'Unknown error'
|
|
|
|
# Raised when an error has occurred during a session
|
|
class SessionError(Exception):
|
|
# SMB X/Open error codes for the ERRDOS error class
|
|
ERRsuccess = 0
|
|
ERRbadfunc = 1
|
|
ERRbadfile = 2
|
|
ERRbadpath = 3
|
|
ERRnofids = 4
|
|
ERRnoaccess = 5
|
|
ERRbadfid = 6
|
|
ERRbadmcb = 7
|
|
ERRnomem = 8
|
|
ERRbadmem = 9
|
|
ERRbadenv = 10
|
|
ERRbadaccess = 12
|
|
ERRbaddata = 13
|
|
ERRres = 14
|
|
ERRbaddrive = 15
|
|
ERRremcd = 16
|
|
ERRdiffdevice = 17
|
|
ERRnofiles = 18
|
|
ERRgeneral = 31
|
|
ERRbadshare = 32
|
|
ERRlock = 33
|
|
ERRunsup = 50
|
|
ERRnetnamedel = 64
|
|
ERRnosuchshare = 67
|
|
ERRfilexists = 80
|
|
ERRinvalidparam = 87
|
|
ERRcannotopen = 110
|
|
ERRinsufficientbuffer = 122
|
|
ERRinvalidname = 123
|
|
ERRunknownlevel = 124
|
|
ERRnotlocked = 158
|
|
ERRrename = 183
|
|
ERRbadpipe = 230
|
|
ERRpipebusy = 231
|
|
ERRpipeclosing = 232
|
|
ERRnotconnected = 233
|
|
ERRmoredata = 234
|
|
ERRnomoreitems = 259
|
|
ERRbaddirectory = 267
|
|
ERReasnotsupported = 282
|
|
ERRlogonfailure = 1326
|
|
ERRbuftoosmall = 2123
|
|
ERRunknownipc = 2142
|
|
ERRnosuchprintjob = 2151
|
|
ERRinvgroup = 2455
|
|
|
|
# here's a special one from observing NT
|
|
ERRnoipc = 66
|
|
|
|
# These errors seem to be only returned by the NT printer driver system
|
|
ERRdriveralreadyinstalled = 1795
|
|
ERRunknownprinterport = 1796
|
|
ERRunknownprinterdriver = 1797
|
|
ERRunknownprintprocessor = 1798
|
|
ERRinvalidseparatorfile = 1799
|
|
ERRinvalidjobpriority = 1800
|
|
ERRinvalidprintername = 1801
|
|
ERRprinteralreadyexists = 1802
|
|
ERRinvalidprintercommand = 1803
|
|
ERRinvaliddatatype = 1804
|
|
ERRinvalidenvironment = 1805
|
|
|
|
ERRunknownprintmonitor = 3000
|
|
ERRprinterdriverinuse = 3001
|
|
ERRspoolfilenotfound = 3002
|
|
ERRnostartdoc = 3003
|
|
ERRnoaddjob = 3004
|
|
ERRprintprocessoralreadyinstalled = 3005
|
|
ERRprintmonitoralreadyinstalled = 3006
|
|
ERRinvalidprintmonitor = 3007
|
|
ERRprintmonitorinuse = 3008
|
|
ERRprinterhasjobsqueued = 3009
|
|
|
|
# Error codes for the ERRSRV class
|
|
|
|
ERRerror = 1
|
|
ERRbadpw = 2
|
|
ERRbadtype = 3
|
|
ERRaccess = 4
|
|
ERRinvnid = 5
|
|
ERRinvnetname = 6
|
|
ERRinvdevice = 7
|
|
ERRqfull = 49
|
|
ERRqtoobig = 50
|
|
ERRinvpfid = 52
|
|
ERRsmbcmd = 64
|
|
ERRsrverror = 65
|
|
ERRfilespecs = 67
|
|
ERRbadlink = 68
|
|
ERRbadpermits = 69
|
|
ERRbadpid = 70
|
|
ERRsetattrmode = 71
|
|
ERRpaused = 81
|
|
ERRmsgoff = 82
|
|
ERRnoroom = 83
|
|
ERRrmuns = 87
|
|
ERRtimeout = 88
|
|
ERRnoresource = 89
|
|
ERRtoomanyuids = 90
|
|
ERRbaduid = 91
|
|
ERRuseMPX = 250
|
|
ERRuseSTD = 251
|
|
ERRcontMPX = 252
|
|
ERRbadPW = None
|
|
ERRnosupport = 0
|
|
ERRunknownsmb = 22
|
|
|
|
# Error codes for the ERRHRD class
|
|
|
|
ERRnowrite = 19
|
|
ERRbadunit = 20
|
|
ERRnotready = 21
|
|
ERRbadcmd = 22
|
|
ERRdata = 23
|
|
ERRbadreq = 24
|
|
ERRseek = 25
|
|
ERRbadmedia = 26
|
|
ERRbadsector = 27
|
|
ERRnopaper = 28
|
|
ERRwrite = 29
|
|
ERRread = 30
|
|
ERRwrongdisk = 34
|
|
ERRFCBunavail = 35
|
|
ERRsharebufexc = 36
|
|
ERRdiskfull = 39
|
|
|
|
|
|
hard_msgs = {
|
|
19: ("ERRnowrite", "Attempt to write on write-protected diskette."),
|
|
20: ("ERRbadunit", "Unknown unit."),
|
|
21: ("ERRnotready", "Drive not ready."),
|
|
22: ("ERRbadcmd", "Unknown command."),
|
|
23: ("ERRdata", "Data error (CRC)."),
|
|
24: ("ERRbadreq", "Bad request structure length."),
|
|
25: ("ERRseek", "Seek error."),
|
|
26: ("ERRbadmedia", "Unknown media type."),
|
|
27: ("ERRbadsector", "Sector not found."),
|
|
28: ("ERRnopaper", "Printer out of paper."),
|
|
29: ("ERRwrite", "Write fault."),
|
|
30: ("ERRread", "Read fault."),
|
|
31: ("ERRgeneral", "General failure."),
|
|
32: ("ERRbadshare", "An open conflicts with an existing open."),
|
|
33: ("ERRlock", "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."),
|
|
34: ("ERRwrongdisk", "The wrong disk was found in a drive."),
|
|
35: ("ERRFCBUnavail", "No FCBs are available to process request."),
|
|
36: ("ERRsharebufexc", "A sharing buffer has been exceeded.")
|
|
}
|
|
|
|
dos_msgs = {
|
|
ERRbadfunc: ("ERRbadfunc", "Invalid function."),
|
|
ERRbadfile: ("ERRbadfile", "File not found."),
|
|
ERRbadpath: ("ERRbadpath", "Directory invalid."),
|
|
ERRnofids: ("ERRnofids", "No file descriptors available"),
|
|
ERRnoaccess: ("ERRnoaccess", "Access denied."),
|
|
ERRbadfid: ("ERRbadfid", "Invalid file handle."),
|
|
ERRbadmcb: ("ERRbadmcb", "Memory control blocks destroyed."),
|
|
ERRnomem: ("ERRnomem", "Insufficient server memory to perform the requested function."),
|
|
ERRbadmem: ("ERRbadmem", "Invalid memory block address."),
|
|
ERRbadenv: ("ERRbadenv", "Invalid environment."),
|
|
11: ("ERRbadformat", "Invalid format."),
|
|
ERRbadaccess: ("ERRbadaccess", "Invalid open mode."),
|
|
ERRbaddata: ("ERRbaddata", "Invalid data."),
|
|
ERRres: ("ERRres", "reserved."),
|
|
ERRbaddrive: ("ERRbaddrive", "Invalid drive specified."),
|
|
ERRremcd: ("ERRremcd", "A Delete Directory request attempted to remove the server's current directory."),
|
|
ERRdiffdevice: ("ERRdiffdevice", "Not same device."),
|
|
ERRnofiles: ("ERRnofiles", "A File Search command can find no more files matching the specified criteria."),
|
|
ERRbadshare: ("ERRbadshare", "The sharing mode specified for an Open conflicts with existing FIDs on the file."),
|
|
ERRlock: ("ERRlock", "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."),
|
|
ERRunsup: ("ERRunsup", "The operation is unsupported"),
|
|
ERRnosuchshare: ("ERRnosuchshare", "You specified an invalid share name"),
|
|
ERRfilexists: ("ERRfilexists", "The file named in a Create Directory, Make New File or Link request already exists."),
|
|
ERRinvalidname: ("ERRinvalidname", "Invalid name"),
|
|
ERRbadpipe: ("ERRbadpipe", "Pipe invalid."),
|
|
ERRpipebusy: ("ERRpipebusy", "All instances of the requested pipe are busy."),
|
|
ERRpipeclosing: ("ERRpipeclosing", "Pipe close in progress."),
|
|
ERRnotconnected: ("ERRnotconnected", "No process on other end of pipe."),
|
|
ERRmoredata: ("ERRmoredata", "There is more data to be returned."),
|
|
ERRinvgroup: ("ERRinvgroup", "Invalid workgroup (try the -W option)"),
|
|
ERRlogonfailure: ("ERRlogonfailure", "Logon failure"),
|
|
ERRdiskfull: ("ERRdiskfull", "Disk full"),
|
|
ERRgeneral: ("ERRgeneral", "General failure"),
|
|
ERRunknownlevel: ("ERRunknownlevel", "Unknown info level")
|
|
}
|
|
|
|
server_msgs = {
|
|
1: ("ERRerror", "Non-specific error code."),
|
|
2: ("ERRbadpw", "Bad password - name/password pair in a Tree Connect or Session Setup are invalid."),
|
|
3: ("ERRbadtype", "reserved."),
|
|
4: ("ERRaccess", "The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID."),
|
|
5: ("ERRinvnid", "The tree ID (TID) specified in a command was invalid."),
|
|
6: ("ERRinvnetname", "Invalid network name in tree connect."),
|
|
7: ("ERRinvdevice", "Invalid device - printer request made to non-printer connection or non-printer request made to printer connection."),
|
|
49: ("ERRqfull", "Print queue full (files) -- returned by open print file."),
|
|
50: ("ERRqtoobig", "Print queue full -- no space."),
|
|
51: ("ERRqeof", "EOF on print queue dump."),
|
|
52: ("ERRinvpfid", "Invalid print file FID."),
|
|
64: ("ERRsmbcmd", "The server did not recognize the command received."),
|
|
65: ("ERRsrverror","The server encountered an internal error, e.g., system file unavailable."),
|
|
67: ("ERRfilespecs", "The file handle (FID) and pathname parameters contained an invalid combination of values."),
|
|
68: ("ERRreserved", "reserved."),
|
|
69: ("ERRbadpermits", "The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute."),
|
|
70: ("ERRreserved", "reserved."),
|
|
71: ("ERRsetattrmode", "The attribute mode in the Set File Attribute request is invalid."),
|
|
81: ("ERRpaused", "Server is paused."),
|
|
82: ("ERRmsgoff", "Not receiving messages."),
|
|
83: ("ERRnoroom", "No room to buffer message."),
|
|
87: ("ERRrmuns", "Too many remote user names."),
|
|
88: ("ERRtimeout", "Operation timed out."),
|
|
89: ("ERRnoresource", "No resources currently available for request."),
|
|
90: ("ERRtoomanyuids", "Too many UIDs active on this session."),
|
|
91: ("ERRbaduid", "The UID is not known as a valid ID on this session."),
|
|
250: ("ERRusempx","Temp unable to support Raw, use MPX mode."),
|
|
251: ("ERRusestd","Temp unable to support Raw, use standard read/write."),
|
|
252: ("ERRcontmpx", "Continue in MPX mode."),
|
|
253: ("ERRreserved", "reserved."),
|
|
254: ("ERRreserved", "reserved."),
|
|
0xFFFF: ("ERRnosupport", "Function not supported.")
|
|
}
|
|
# Error clases
|
|
|
|
ERRDOS = 0x1
|
|
error_classes = { 0: ("SUCCESS", {}),
|
|
ERRDOS: ("ERRDOS", dos_msgs),
|
|
0x02: ("ERRSRV",server_msgs),
|
|
0x03: ("ERRHRD",hard_msgs),
|
|
0x04: ("ERRXOS", {} ),
|
|
0xE1: ("ERRRMX1", {} ),
|
|
0xE2: ("ERRRMX2", {} ),
|
|
0xE3: ("ERRRMX3", {} ),
|
|
0xFF: ("ERRCMD", {} ) }
|
|
|
|
|
|
|
|
def __init__( self, error_string, error_class, error_code, nt_status = 0, packet=0):
|
|
Exception.__init__(self, error_string)
|
|
self.nt_status = nt_status
|
|
self._args = error_string
|
|
if nt_status:
|
|
self.error_class = 0
|
|
self.error_code = (error_code << 16) + error_class
|
|
else:
|
|
self.error_class = error_class
|
|
self.error_code = error_code
|
|
self.packet = packet
|
|
|
|
def get_error_class( self ):
|
|
return self.error_class
|
|
|
|
def get_error_code( self ):
|
|
return self.error_code
|
|
|
|
def get_error_packet(self):
|
|
return self.packet
|
|
|
|
def __str__( self ):
|
|
error_class = SessionError.error_classes.get( self.error_class, None )
|
|
if not error_class:
|
|
error_code_str = self.error_code
|
|
error_class_str = self.error_class
|
|
else:
|
|
error_class_str = error_class[0]
|
|
error_code = error_class[1].get( self.error_code, None )
|
|
if not error_code:
|
|
error_code_str = self.error_code
|
|
else:
|
|
error_code_str = '%s(%s)' % error_code
|
|
|
|
if self.nt_status:
|
|
key = self.error_code
|
|
if key in nt_errors.ERROR_MESSAGES:
|
|
error_msg_short = nt_errors.ERROR_MESSAGES[key][0]
|
|
error_msg_verbose = nt_errors.ERROR_MESSAGES[key][1]
|
|
return 'SMB SessionError: code: 0x%x - %s - %s' % (self.error_code, error_msg_short, error_msg_verbose)
|
|
else:
|
|
return 'SMB SessionError: unknown error code: 0x%x' % self.error_code
|
|
else:
|
|
# Fall back to the old format
|
|
return 'SMB SessionError: class: %s, code: %s' % (error_class_str, error_code_str)
|
|
|
|
|
|
# Raised when an supported feature is present/required in the protocol but is not
|
|
# currently supported by pysmb
|
|
class UnsupportedFeature(Exception):
|
|
pass
|
|
|
|
# Add basic filetime conversion helper methods.
|
|
def POSIXtoFT(t):
|
|
"""
|
|
Helper method that converts POSIX timestamps to FILETIME timestamps.
|
|
|
|
:param int t: POSIX timestamp - can be retrieved from datetime library.
|
|
|
|
:return int: FILETIME timestamp representing the given POSIX timestamp.
|
|
"""
|
|
|
|
t *= 10000000
|
|
t += 116444736000000000
|
|
|
|
return int(t)
|
|
|
|
def FTtoPOSIX(t):
|
|
"""
|
|
Helper method that converts FILETIME timestamps to POSIX timestamps.
|
|
|
|
:param int t: FILETIME timestamp.
|
|
|
|
:return int: POSIX timestamp representing the given FILETIME timestamp.
|
|
"""
|
|
|
|
t -= 116444736000000000
|
|
t //= 10000000
|
|
|
|
return int(t)
|
|
|
|
# Define SMB Standard DateTime Data according to (2.2.1.4 Time)
|
|
class SMBDateStruct(BigEndianStructure):
|
|
_fields_ = [
|
|
("y", c_uint32, 7),
|
|
("m", c_uint32, 4),
|
|
("d", c_uint32, 5),
|
|
]
|
|
|
|
class SMB_DATE:
|
|
"""
|
|
2.2.1.4.1 SMB_DATE
|
|
Class representing an SMB Date value.
|
|
https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/31b65222-4171-49b4-aeed-7d3f38ecf68b
|
|
"""
|
|
def __init__(self, year, month, day):
|
|
"""
|
|
:param int year: An integer representing the valid year part of the date.
|
|
:param int month: An integer representing the valid month part of the date.
|
|
:param int day: An integer representing the valid day part of the date.
|
|
"""
|
|
|
|
self.year = year
|
|
self.month = month
|
|
self.day = day
|
|
|
|
@property
|
|
def year(self):
|
|
return self._year + 1980
|
|
|
|
@year.setter
|
|
def year(self, value):
|
|
value = value - 1980
|
|
if value < 0 or value > 119:
|
|
raise ValueError("Invalid year component.")
|
|
|
|
self._year = value
|
|
|
|
@property
|
|
def month(self):
|
|
return self._month
|
|
|
|
@month.setter
|
|
def month(self, value):
|
|
if value < 0 or value > 12:
|
|
raise ValueError("Invalid month component.")
|
|
|
|
self._month = value
|
|
|
|
@property
|
|
def day(self):
|
|
return self._day
|
|
|
|
@day.setter
|
|
def day(self, value):
|
|
if value < 0 or value > 31:
|
|
raise ValueError("Invalid day component.")
|
|
|
|
self._day = value
|
|
|
|
def pack(self):
|
|
return ((self._year << 9) & 0xFE00) + ((self._month << 5) & 0x01E0) + (self._day & 0x001F)
|
|
|
|
def pack_into(self):
|
|
"""
|
|
Helper method to easily access the data as a struct.
|
|
|
|
:return SMBDateStruct: The structure representation of the object.
|
|
"""
|
|
|
|
res = SMBDateStruct()
|
|
pack_into(">H", res, 0, self.pack())
|
|
return res
|
|
|
|
@classmethod
|
|
def from_int(cls, data):
|
|
"""
|
|
Helper method to easily convert packed bytes value to class object.
|
|
|
|
:param bytes data: Packed bytes to be converted to a class object.
|
|
|
|
:return SMB_DATE: The class representation of the packed data bytes.
|
|
"""
|
|
|
|
s = SMBDateStruct()
|
|
pack_into(">H", s, 0, data)
|
|
return cls.from_struct(s)
|
|
|
|
@classmethod
|
|
def from_struct(cls, s):
|
|
"""
|
|
Helper method to easily convert struct to class object.
|
|
|
|
:param SMBDateStruct s: The struct object to be converted to a class object.
|
|
|
|
:return SMB_DATE: The class representation of the struct object.
|
|
"""
|
|
|
|
return cls(s.y + 1980, s.m, s.d)
|
|
|
|
class SMBTimeStruct(BigEndianStructure):
|
|
_fields_ = [
|
|
("h", c_uint32, 5),
|
|
("m", c_uint32, 6),
|
|
("s", c_uint32, 5),
|
|
]
|
|
|
|
class SMB_TIME:
|
|
"""
|
|
2.2.1.4.2 SMB_TIME
|
|
Class representing an SMB Time value.
|
|
https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/401749d1-ee41-4273-9dcb-698180e68745
|
|
"""
|
|
def __init__(self, hour, minutes, seconds):
|
|
"""
|
|
:param int hour: An integer representing the valid hour part of the time.
|
|
:param int minutes: An integer representing the valid minutes part of the time.
|
|
:param int seconds: An integer representing the valid seconds part of the time.
|
|
"""
|
|
|
|
self.hour = hour
|
|
self.minutes = minutes
|
|
self.seconds = seconds
|
|
|
|
@property
|
|
def hour(self):
|
|
return self._hour
|
|
|
|
@hour.setter
|
|
def hour(self, value):
|
|
if value < 0 or value > 23:
|
|
raise ValueError("Invalid hour component.")
|
|
|
|
self._hour = value
|
|
|
|
@property
|
|
def minutes(self):
|
|
return self._minutes
|
|
|
|
@minutes.setter
|
|
def minutes(self, value):
|
|
if value < 0 or value > 59:
|
|
raise ValueError("Invalid minutes component.")
|
|
|
|
self._minutes = value
|
|
|
|
@property
|
|
def seconds(self):
|
|
return self._seconds
|
|
|
|
@seconds.setter
|
|
def seconds(self, value):
|
|
if value < 0 or value > 59:
|
|
raise ValueError("Invalid seconds component.")
|
|
|
|
self._seconds = value
|
|
|
|
def pack(self):
|
|
return ((self._hour << 11) & 0xF800) + ((self._minutes << 5) & 0x07E0) + (self._seconds & 0x001F)
|
|
|
|
def pack_into(self):
|
|
"""
|
|
Helper method to easily access the data as a struct.
|
|
|
|
:return SMBTimeStruct: The structure representation of the object.
|
|
"""
|
|
|
|
res = SMBTimeStruct()
|
|
pack_into(">H", res, 0, self.pack())
|
|
return res
|
|
|
|
@classmethod
|
|
def from_int(cls, data):
|
|
"""
|
|
Helper method to easily convert packed bytes value to class object.
|
|
|
|
:param bytes data: Packed bytes to be converted to a class object.
|
|
|
|
:return SMB_TIME: The class representation of the packed data bytes.
|
|
"""
|
|
|
|
s = SMBTimeStruct()
|
|
pack_into(">H", s, 0, data)
|
|
return cls.from_struct(s)
|
|
|
|
@classmethod
|
|
def from_struct(cls, s):
|
|
"""
|
|
Helper method to easily convert struct to class object.
|
|
|
|
:param SMBTimeStruct s: The struct object to be converted to a class object.
|
|
|
|
:return SMB_TIME: The class representation of the struct object.
|
|
"""
|
|
|
|
return cls(s.h, s.m, s.s)
|
|
|
|
# Contains information about a SMB shared device/service
|
|
class SharedDevice:
|
|
def __init__(self, name, share_type, comment):
|
|
self.__name = name
|
|
self.__type = share_type
|
|
self.__comment = comment
|
|
|
|
def get_name(self):
|
|
return self.__name
|
|
|
|
def get_type(self):
|
|
return self.__type
|
|
|
|
def get_comment(self):
|
|
return self.__comment
|
|
|
|
def __repr__(self):
|
|
return '<SharedDevice instance: name=' + self.__name + ', type=' + str(self.__type) + ', comment="' + self.__comment + '">'
|
|
|
|
|
|
# Contains information about the shared file/directory
|
|
class SharedFile:
|
|
def __init__(self, ctime, atime, wtime, mtime, filesize, allocsize, attribs, shortname, longname):
|
|
self.__ctime = ctime # CreateTime ([MS-CIFS] 2.2.8.1.4 SMB_FIND_FILE_DIRECTORY_INFO)
|
|
self.__atime = atime # LastAccessTime ([MS-CIFS] 2.2.8.1.4 SMB_FIND_FILE_DIRECTORY_INFO)
|
|
self.__wtime = wtime # LastWriteTime ([MS-CIFS] 2.2.8.1.4 SMB_FIND_FILE_DIRECTORY_INFO)
|
|
self.__mtime = mtime # LastAttrChangeTime ([MS-CIFS] 2.2.8.1.4 SMB_FIND_FILE_DIRECTORY_INFO)
|
|
self.__filesize = filesize
|
|
self.__allocsize = allocsize
|
|
self.__attribs = attribs
|
|
try:
|
|
if isinstance(shortname,bytes):
|
|
self.__shortname = shortname[:shortname.index(b'\0')]
|
|
else:
|
|
self.__shortname = shortname[:shortname.index('\0')]
|
|
except (ValueError, TypeError):
|
|
self.__shortname = shortname
|
|
try:
|
|
if isinstance(shortname,bytes):
|
|
self.__longname = longname[:longname.index(b'\0')]
|
|
else:
|
|
self.__longname = longname[:longname.index('\0')]
|
|
except (ValueError, TypeError):
|
|
self.__longname = longname
|
|
|
|
def get_ctime(self):
|
|
return self.__ctime
|
|
|
|
def get_ctime_epoch(self):
|
|
return self.__convert_smbtime(self.__ctime)
|
|
|
|
def get_wtime(self):
|
|
return self.__wtime
|
|
|
|
def get_wtime_epoch(self):
|
|
return self.__convert_smbtime(self.__wtime)
|
|
|
|
def get_mtime(self):
|
|
return self.__mtime
|
|
|
|
def get_mtime_epoch(self):
|
|
return self.__convert_smbtime(self.__mtime)
|
|
|
|
def get_atime(self):
|
|
return self.__atime
|
|
|
|
def get_atime_epoch(self):
|
|
return self.__convert_smbtime(self.__atime)
|
|
|
|
def get_filesize(self):
|
|
return self.__filesize
|
|
|
|
def get_allocsize(self):
|
|
return self.__allocsize
|
|
|
|
def get_attributes(self):
|
|
return self.__attribs
|
|
|
|
def is_archive(self):
|
|
return self.__attribs & ATTR_ARCHIVE
|
|
|
|
def is_compressed(self):
|
|
return self.__attribs & ATTR_COMPRESSED
|
|
|
|
def is_normal(self):
|
|
return self.__attribs & ATTR_NORMAL
|
|
|
|
def is_hidden(self):
|
|
return self.__attribs & ATTR_HIDDEN
|
|
|
|
def is_readonly(self):
|
|
return self.__attribs & ATTR_READONLY
|
|
|
|
def is_temporary(self):
|
|
return self.__attribs & ATTR_TEMPORARY
|
|
|
|
def is_directory(self):
|
|
return self.__attribs & ATTR_DIRECTORY
|
|
|
|
def is_system(self):
|
|
return self.__attribs & ATTR_SYSTEM
|
|
|
|
def get_shortname(self):
|
|
return self.__shortname
|
|
|
|
def get_longname(self):
|
|
return self.__longname
|
|
|
|
def __repr__(self):
|
|
return '<SharedFile instance: shortname="' + self.__shortname + '", longname="' + self.__longname + '", filesize=' + str(self.__filesize) + '>'
|
|
|
|
@staticmethod
|
|
def __convert_smbtime(t):
|
|
x = t >> 32
|
|
y = t & 0xffffffff
|
|
geo_cal_offset = 11644473600.0 # = 369.0 * 365.25 * 24 * 60 * 60 - (3.0 * 24 * 60 * 60 + 6.0 * 60 * 60)
|
|
return (x * 4.0 * (1 << 30) + (y & 0xfff00000)) * 1.0e-7 - geo_cal_offset
|
|
|
|
|
|
# Contain information about a SMB machine
|
|
class SMBMachine:
|
|
def __init__(self, nbname, nbt_type, comment):
|
|
self.__nbname = nbname
|
|
self.__type = nbt_type
|
|
self.__comment = comment
|
|
|
|
def __repr__(self):
|
|
return '<SMBMachine instance: nbname="' + self.__nbname + '", type=' + hex(self.__type) + ', comment="' + self.__comment + '">'
|
|
|
|
class SMBDomain:
|
|
def __init__(self, nbgroup, domain_type, master_browser):
|
|
self.__nbgroup = nbgroup
|
|
self.__type = domain_type
|
|
self.__master_browser = master_browser
|
|
|
|
def __repr__(self):
|
|
return '<SMBDomain instance: nbgroup="' + self.__nbgroup + '", type=' + hex(self.__type) + ', master browser="' + self.__master_browser + '">'
|
|
|
|
# Represents a SMB Packet
|
|
class NewSMBPacket(Structure):
|
|
structure = (
|
|
('Signature', '"\xffSMB'),
|
|
('Command','B=0'),
|
|
('ErrorClass','B=0'),
|
|
('_reserved','B=0'),
|
|
('ErrorCode','<H=0'),
|
|
('Flags1','B=0'),
|
|
('Flags2','<H=0'),
|
|
('PIDHigh','<H=0'),
|
|
('SecurityFeatures','8s=""'),
|
|
('Reserved','<H=0'),
|
|
('Tid','<H=0xffff'),
|
|
('Pid','<H=0'),
|
|
('Uid','<H=0'),
|
|
('Mid','<H=0'),
|
|
('Data','*:'),
|
|
)
|
|
|
|
def __init__(self, **kargs):
|
|
Structure.__init__(self, **kargs)
|
|
|
|
if ('Flags2' in self.fields) is False:
|
|
self['Flags2'] = 0
|
|
if ('Flags1' in self.fields) is False:
|
|
self['Flags1'] = 0
|
|
|
|
if 'data' not in kargs:
|
|
self['Data'] = []
|
|
|
|
def addCommand(self, command):
|
|
if len(self['Data']) == 0:
|
|
self['Command'] = command.command
|
|
else:
|
|
self['Data'][-1]['Parameters']['AndXCommand'] = command.command
|
|
self['Data'][-1]['Parameters']['AndXOffset'] = len(self)
|
|
self['Data'].append(command)
|
|
|
|
def isMoreData(self):
|
|
return (self['Command'] in [SMB.SMB_COM_TRANSACTION, SMB.SMB_COM_READ_ANDX, SMB.SMB_COM_READ_RAW] and
|
|
self['ErrorClass'] == 1 and self['ErrorCode'] == SessionError.ERRmoredata)
|
|
|
|
def isMoreProcessingRequired(self):
|
|
return self['ErrorClass'] == 0x16 and self['ErrorCode'] == 0xc000
|
|
|
|
def isValidAnswer(self, cmd):
|
|
# this was inside a loop reading more from the net (with recv_packet(None))
|
|
if self['Command'] == cmd:
|
|
if (self['ErrorClass'] == 0x00 and self['ErrorCode'] == 0x00):
|
|
return 1
|
|
elif self.isMoreData():
|
|
return 1
|
|
elif self.isMoreProcessingRequired():
|
|
return 1
|
|
raise SessionError("SMB Library Error", self['ErrorClass'] + (self['_reserved'] << 8), self['ErrorCode'], self['Flags2'] & SMB.FLAGS2_NT_STATUS, self)
|
|
else:
|
|
raise UnsupportedFeature("Unexpected answer from server: Got %d, Expected %d" % (self['Command'], cmd))
|
|
|
|
|
|
class SMBCommand(Structure):
|
|
structure = (
|
|
('WordCount', 'B=len(Parameters)//2'),
|
|
('_ParametersLength','_-Parameters','WordCount*2'),
|
|
('Parameters',':'), # default set by constructor
|
|
('ByteCount','<H-Data'),
|
|
('Data',':'), # default set by constructor
|
|
)
|
|
|
|
def __init__(self, commandOrData = None, data = None, **kargs):
|
|
if type(commandOrData) == type(0):
|
|
self.command = commandOrData
|
|
else:
|
|
data = data or commandOrData
|
|
|
|
Structure.__init__(self, data = data, **kargs)
|
|
|
|
if data is None:
|
|
self['Parameters'] = ''
|
|
self['Data'] = ''
|
|
|
|
class AsciiOrUnicodeStructure(Structure):
|
|
UnicodeStructure = ()
|
|
AsciiStructure = ()
|
|
ENCODING = 'utf-8'
|
|
|
|
def __init__(self, flags = 0, **kargs):
|
|
if flags & SMB.FLAGS2_UNICODE:
|
|
self.structure = self.UnicodeStructure
|
|
else:
|
|
self.structure = self.AsciiStructure
|
|
Structure.__init__(self, **kargs)
|
|
|
|
class SMBCommand_Parameters(Structure):
|
|
pass
|
|
|
|
class SMBAndXCommand_Parameters(Structure):
|
|
commonHdr = (
|
|
('AndXCommand','B=0xff'),
|
|
('_reserved','B=0'),
|
|
('AndXOffset','<H=0'),
|
|
)
|
|
structure = ( # default structure, overridden by subclasses
|
|
('Data',':=""'),
|
|
)
|
|
|
|
############# TRANSACTIONS RELATED
|
|
# TRANS2_QUERY_FS_INFORMATION
|
|
# QUERY_FS Information Levels
|
|
# SMB_QUERY_FS_ATTRIBUTE_INFO
|
|
class SMBQueryFsAttributeInfo(Structure):
|
|
structure = (
|
|
('FileSystemAttributes','<L'),
|
|
('MaxFilenNameLengthInBytes','<L'),
|
|
('LengthOfFileSystemName','<L-FileSystemName'),
|
|
('FileSystemName',':'),
|
|
)
|
|
|
|
class SMBQueryFsInfoVolume(AsciiOrUnicodeStructure):
|
|
commonHdr = (
|
|
('ulVolSerialNbr','<L=0xABCDEFAA'),
|
|
('cCharCount','<B-VolumeLabel'),
|
|
)
|
|
AsciiStructure = (
|
|
('VolumeLabel','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('VolumeLabel','u'),
|
|
)
|
|
|
|
# FILE_FS_SIZE_INFORMATION
|
|
class FileFsSizeInformation(Structure):
|
|
structure = (
|
|
('TotalAllocationUnits','<q=148529400'),
|
|
('AvailableAllocationUnits','<q=14851044'),
|
|
('SectorsPerAllocationUnit','<L=2'),
|
|
('BytesPerSector','<L=512'),
|
|
)
|
|
|
|
# SMB_QUERY_FS_SIZE_INFO
|
|
class SMBQueryFsSizeInfo(Structure):
|
|
structure = (
|
|
('TotalAllocationUnits','<q=148529400'),
|
|
('TotalFreeAllocationUnits','<q=14851044'),
|
|
('SectorsPerAllocationUnit','<L=2'),
|
|
('BytesPerSector','<L=512'),
|
|
)
|
|
# FILE_FS_FULL_SIZE_INFORMATION
|
|
class SMBFileFsFullSizeInformation(Structure):
|
|
structure = (
|
|
('TotalAllocationUnits','<q=148529400'),
|
|
('CallerAvailableAllocationUnits','<q=148529400'),
|
|
('ActualAvailableAllocationUnits','<q=148529400'),
|
|
('SectorsPerAllocationUnit','<L=15'),
|
|
('BytesPerSector','<L=512')
|
|
)
|
|
# SMB_QUERY_FS_VOLUME_INFO
|
|
class SMBQueryFsVolumeInfo(Structure):
|
|
structure = (
|
|
('VolumeCreationTime','<q'),
|
|
('SerialNumber','<L=0xABCDEFAA'),
|
|
('VolumeLabelSize','<L=len(VolumeLabel)'),
|
|
('Reserved','<H=0x10'),
|
|
('VolumeLabel',':')
|
|
)
|
|
|
|
# SMB_QUERY_FS_DEVICE_INFO
|
|
class SMBQueryFsDeviceInfo(Structure):
|
|
structure = (
|
|
('DeviceType', '<L=0'),
|
|
('DeviceCharacteristics', '<L=0')
|
|
)
|
|
|
|
|
|
# SMB_FIND_FILE_BOTH_DIRECTORY_INFO level
|
|
class SMBFindFileBothDirectoryInfo(AsciiOrUnicodeStructure):
|
|
commonHdr = (
|
|
('NextEntryOffset','<L=0'),
|
|
('FileIndex','<L=0'),
|
|
('CreationTime','<q'),
|
|
('LastAccessTime','<q'),
|
|
('LastWriteTime','<q'),
|
|
('LastChangeTime','<q'),
|
|
('EndOfFile','<q=0'),
|
|
('AllocationSize','<q=0'),
|
|
('ExtFileAttributes','<L=0'),
|
|
)
|
|
AsciiStructure = (
|
|
('FileNameLength','<L-FileName'),
|
|
('EaSize','<L=0'),
|
|
('ShortNameLength','<B=0'),
|
|
('Reserved','<B=0'),
|
|
('ShortName','24s'),
|
|
('FileName',':'),
|
|
)
|
|
UnicodeStructure = (
|
|
('FileNameLength','<L-FileName'),
|
|
('EaSize','<L=0'),
|
|
('ShortNameLength','<B=0'),
|
|
('Reserved','<B=0'),
|
|
('ShortName','24s'),
|
|
('FileName',':'),
|
|
)
|
|
|
|
# SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO level
|
|
class SMBFindFileIdFullDirectoryInfo(AsciiOrUnicodeStructure):
|
|
commonHdr = (
|
|
('NextEntryOffset','<L=0'),
|
|
('FileIndex','<L=0'),
|
|
('CreationTime','<q'),
|
|
('LastAccessTime','<q'),
|
|
('LastWriteTime','<q'),
|
|
('LastChangeTime','<q'),
|
|
('EndOfFile','<q=0'),
|
|
('AllocationSize','<q=0'),
|
|
('ExtFileAttributes','<L=0'),
|
|
)
|
|
AsciiStructure = (
|
|
('FileNameLength','<L-FileName'),
|
|
('EaSize','<L=0'),
|
|
('Reserved', '<L=0'),
|
|
('FileID','<q=0'),
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('FileNameLength','<L-FileName'),
|
|
('EaSize','<L=0'),
|
|
('Reserved','<L=0'),
|
|
('FileID','<q=0'),
|
|
('FileName',':'),
|
|
)
|
|
|
|
# SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO level
|
|
class SMBFindFileIdBothDirectoryInfo(AsciiOrUnicodeStructure):
|
|
commonHdr = (
|
|
('NextEntryOffset','<L=0'),
|
|
('FileIndex','<L=0'),
|
|
('CreationTime','<q'),
|
|
('LastAccessTime','<q'),
|
|
('LastWriteTime','<q'),
|
|
('LastChangeTime','<q'),
|
|
('EndOfFile','<q=0'),
|
|
('AllocationSize','<q=0'),
|
|
('ExtFileAttributes','<L=0'),
|
|
)
|
|
AsciiStructure = (
|
|
('FileNameLength','<L-FileName'),
|
|
('EaSize','<L=0'),
|
|
('ShortNameLength','<B=0'),
|
|
('Reserved','<B=0'),
|
|
('ShortName','24s'),
|
|
('Reserved','<H=0'),
|
|
('FileID','<q=0'),
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('FileNameLength','<L-FileName'),
|
|
('EaSize','<L=0'),
|
|
('ShortNameLength','<B=0'),
|
|
('Reserved','<B=0'),
|
|
('ShortName','24s'),
|
|
('Reserved','<H=0'),
|
|
('FileID','<q=0'),
|
|
('FileName',':'),
|
|
)
|
|
|
|
# SMB_FIND_FILE_DIRECTORY_INFO level
|
|
class SMBFindFileDirectoryInfo(AsciiOrUnicodeStructure):
|
|
commonHdr = (
|
|
('NextEntryOffset','<L=0'),
|
|
('FileIndex','<L=0'),
|
|
('CreationTime','<q'),
|
|
('LastAccessTime','<q'),
|
|
('LastWriteTime','<q'),
|
|
('LastChangeTime','<q'),
|
|
('EndOfFile','<q=0'),
|
|
('AllocationSize','<q=1'),
|
|
('ExtFileAttributes','<L=0'),
|
|
)
|
|
AsciiStructure = (
|
|
('FileNameLength','<L-FileName'),
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('FileNameLength','<L-FileName'),
|
|
('FileName',':'),
|
|
)
|
|
|
|
# SMB_FIND_FILE_NAMES_INFO level
|
|
class SMBFindFileNamesInfo(AsciiOrUnicodeStructure):
|
|
commonHdr = (
|
|
('NextEntryOffset','<L=0'),
|
|
('FileIndex','<L=0'),
|
|
)
|
|
AsciiStructure = (
|
|
('FileNameLength','<L-FileName'),
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('FileNameLength','<L-FileName'),
|
|
('FileName',':'),
|
|
)
|
|
|
|
# SMB_FIND_FILE_FULL_DIRECTORY_INFO level
|
|
class SMBFindFileFullDirectoryInfo(AsciiOrUnicodeStructure):
|
|
commonHdr = (
|
|
('NextEntryOffset','<L=0'),
|
|
('FileIndex','<L=0'),
|
|
('CreationTime','<q'),
|
|
('LastAccessTime','<q'),
|
|
('LastWriteTime','<q'),
|
|
('LastChangeTime','<q'),
|
|
('EndOfFile','<q=0'),
|
|
('AllocationSize','<q=1'),
|
|
('ExtFileAttributes','<L=0'),
|
|
)
|
|
AsciiStructure = (
|
|
('FileNameLength','<L-FileName'),
|
|
('EaSize','<L'),
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('FileNameLength','<L-FileName'),
|
|
('EaSize','<L'),
|
|
('FileName',':'),
|
|
)
|
|
|
|
# SMB_FIND_INFO_STANDARD level
|
|
class SMBFindInfoStandard(AsciiOrUnicodeStructure):
|
|
commonHdr = (
|
|
('ResumeKey','<L=0xff'),
|
|
('CreationDate','<H=0'),
|
|
('CreationTime','<H=0'),
|
|
('LastAccessDate','<H=0'),
|
|
('LastAccessTime','<H=0'),
|
|
('LastWriteDate','<H=0'),
|
|
('LastWriteTime','<H=0'),
|
|
('EaSize','<L'),
|
|
('AllocationSize','<L=1'),
|
|
('ExtFileAttributes','<H=0'),
|
|
)
|
|
AsciiStructure = (
|
|
('FileNameLength','<B-FileName'),
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('FileNameLength','<B-FileName'),
|
|
('FileName',':'),
|
|
)
|
|
|
|
# SET_FILE_INFORMATION structures
|
|
# 2.2.8.4.1 SMB_INFO_STANDARD
|
|
class SMBSetStandardInfo(Structure):
|
|
structure = (
|
|
('CreateDate','<H'),
|
|
('CreationTime','<H'),
|
|
('LastAccessDate','<H'),
|
|
('LastAccessTime','<H'),
|
|
('LastWriteDate','<H'),
|
|
('LastWriteTime','<H'),
|
|
('Reserved','<B=10'),
|
|
)
|
|
|
|
# 2.2.8.4.4 SMB_SET_FILE_DISPOSITION_INFO
|
|
class SMBSetFileDispositionInfo(Structure):
|
|
structure = (
|
|
('DeletePending','<B'),
|
|
)
|
|
|
|
# 2.2.8.4.3 SMB_SET_FILE_BASIC_INFO
|
|
class SMBSetFileBasicInfo(Structure):
|
|
structure = (
|
|
('CreationTime','<q'),
|
|
('LastAccessTime','<q'),
|
|
('LastWriteTime','<q'),
|
|
('ChangeTime','<q'),
|
|
('ExtFileAttributes','<L'),
|
|
('Reserved','<L=0'),
|
|
)
|
|
|
|
# FILE_STREAM_INFORMATION
|
|
class SMBFileStreamInformation(Structure):
|
|
commonHdr = (
|
|
('NextEntryOffset','<L=0'),
|
|
('StreamNameLength','<L=0'),
|
|
('StreamSize','<q=0'),
|
|
('StreamAllocationSize','<q=0'),
|
|
('StreamName',':=""'),
|
|
)
|
|
|
|
# FILE_NETWORK_OPEN_INFORMATION
|
|
class SMBFileNetworkOpenInfo(Structure):
|
|
structure = (
|
|
('CreationTime','<q=0'),
|
|
('LastAccessTime','<q=0'),
|
|
('LastWriteTime','<q=0'),
|
|
('ChangeTime','<q=0'),
|
|
('AllocationSize','<q=0'),
|
|
('EndOfFile','<q=0'),
|
|
('FileAttributes','<L=0'),
|
|
('Reserved','<L=0'),
|
|
)
|
|
|
|
# SMB_SET_FILE_END_OF_FILE_INFO
|
|
class SMBSetFileEndOfFileInfo(Structure):
|
|
structure = (
|
|
('EndOfFile','<q'),
|
|
)
|
|
|
|
# TRANS2_FIND_NEXT2
|
|
class SMBFindNext2_Parameters(AsciiOrUnicodeStructure):
|
|
commonHdr = (
|
|
('SID','<H'),
|
|
('SearchCount','<H'),
|
|
('InformationLevel','<H'),
|
|
('ResumeKey','<L'),
|
|
('Flags','<H'),
|
|
)
|
|
AsciiStructure = (
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('FileName','u'),
|
|
)
|
|
|
|
class SMBFindNext2Response_Parameters(Structure):
|
|
structure = (
|
|
('SearchCount','<H'),
|
|
('EndOfSearch','<H=1'),
|
|
('EaErrorOffset','<H=0'),
|
|
('LastNameOffset','<H=0'),
|
|
)
|
|
|
|
class SMBFindNext2_Data(Structure):
|
|
structure = (
|
|
('GetExtendedAttributesListLength','_-GetExtendedAttributesList', 'self["GetExtendedAttributesListLength"]'),
|
|
('GetExtendedAttributesList',':'),
|
|
)
|
|
|
|
|
|
# TRANS2_FIND_FIRST2
|
|
class SMBFindFirst2Response_Parameters(Structure):
|
|
structure = (
|
|
('SID','<H'),
|
|
('SearchCount','<H'),
|
|
('EndOfSearch','<H=1'),
|
|
('EaErrorOffset','<H=0'),
|
|
('LastNameOffset','<H=0'),
|
|
)
|
|
|
|
class SMBFindFirst2_Parameters(AsciiOrUnicodeStructure):
|
|
commonHdr = (
|
|
('SearchAttributes','<H'),
|
|
('SearchCount','<H'),
|
|
('Flags','<H'),
|
|
('InformationLevel','<H'),
|
|
('SearchStorageType','<L'),
|
|
)
|
|
AsciiStructure = (
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('FileName','u'),
|
|
)
|
|
|
|
class SMBFindFirst2_Data(Structure):
|
|
structure = (
|
|
('GetExtendedAttributesListLength','_-GetExtendedAttributesList', 'self["GetExtendedAttributesListLength"]'),
|
|
('GetExtendedAttributesList',':'),
|
|
)
|
|
|
|
# TRANS2_SET_PATH_INFORMATION
|
|
class SMBSetPathInformation_Parameters(AsciiOrUnicodeStructure):
|
|
commonHdr = (
|
|
('InformationLevel','<H'),
|
|
('Reserved','<L'),
|
|
)
|
|
AsciiStructure = (
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('FileName','u'),
|
|
)
|
|
|
|
class SMBSetPathInformationResponse_Parameters(Structure):
|
|
structure = (
|
|
('EaErrorOffset','<H=0'),
|
|
)
|
|
|
|
# TRANS2_SET_FILE_INFORMATION
|
|
class SMBSetFileInformation_Parameters(Structure):
|
|
structure = (
|
|
('FID','<H'),
|
|
('InformationLevel','<H'),
|
|
('Reserved','<H'),
|
|
)
|
|
|
|
class SMBSetFileInformationResponse_Parameters(Structure):
|
|
structure = (
|
|
('EaErrorOffset','<H=0'),
|
|
)
|
|
|
|
# TRANS2_QUERY_FILE_INFORMATION
|
|
class SMBQueryFileInformation_Parameters(Structure):
|
|
structure = (
|
|
('FID','<H'),
|
|
('InformationLevel','<H'),
|
|
)
|
|
|
|
class SMBQueryFileInformationResponse_Parameters(Structure):
|
|
structure = (
|
|
('EaErrorOffset','<H=0'),
|
|
)
|
|
|
|
class SMBQueryFileInformation_Data(Structure):
|
|
structure = (
|
|
('GetExtendedAttributeList',':'),
|
|
)
|
|
|
|
# TRANS2_QUERY_PATH_INFORMATION
|
|
class SMBQueryPathInformationResponse_Parameters(Structure):
|
|
structure = (
|
|
('EaErrorOffset','<H=0'),
|
|
)
|
|
|
|
class SMBQueryPathInformation_Parameters(AsciiOrUnicodeStructure):
|
|
commonHdr = (
|
|
('InformationLevel','<H'),
|
|
('Reserved','<L=0'),
|
|
)
|
|
AsciiStructure = (
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('FileName','u'),
|
|
)
|
|
|
|
class SMBQueryPathInformation_Data(Structure):
|
|
structure = (
|
|
('GetExtendedAttributeList',':'),
|
|
)
|
|
|
|
|
|
# SMB_QUERY_FILE_EA_INFO
|
|
class SMBQueryFileEaInfo(Structure):
|
|
structure = (
|
|
('EaSize','<L=0'),
|
|
)
|
|
|
|
# SMB_QUERY_FILE_BASIC_INFO
|
|
class SMBQueryFileBasicInfo(Structure):
|
|
structure = (
|
|
('CreationTime','<q'),
|
|
('LastAccessTime','<q'),
|
|
('LastWriteTime','<q'),
|
|
('LastChangeTime','<q'),
|
|
('ExtFileAttributes','<L'),
|
|
#('Reserved','<L=0'),
|
|
)
|
|
|
|
# SMB_QUERY_FILE_STANDARD_INFO
|
|
class SMBQueryFileStandardInfo(Structure):
|
|
structure = (
|
|
('AllocationSize','<q'),
|
|
('EndOfFile','<q'),
|
|
('NumberOfLinks','<L=0'),
|
|
('DeletePending','<B=0'),
|
|
('Directory','<B'),
|
|
)
|
|
|
|
# SMB_QUERY_FILE_ALL_INFO
|
|
class SMBQueryFileAllInfo(Structure):
|
|
structure = (
|
|
('CreationTime','<q'),
|
|
('LastAccessTime','<q'),
|
|
('LastWriteTime','<q'),
|
|
('LastChangeTime','<q'),
|
|
('ExtFileAttributes','<L'),
|
|
('Reserved','<L=0'),
|
|
('AllocationSize','<q'),
|
|
('EndOfFile','<q'),
|
|
('NumberOfLinks','<L=0'),
|
|
('DeletePending','<B=0'),
|
|
('Directory','<B'),
|
|
('Reserved','<H=0'),
|
|
('EaSize','<L=0'),
|
|
('FileNameLength','<L-FileName'),
|
|
('FileName',':'),
|
|
)
|
|
|
|
# \PIPE\LANMAN NetShareEnum
|
|
class SMBNetShareEnum(Structure):
|
|
structure = (
|
|
('RAPOpcode','<H=0'),
|
|
('ParamDesc','z'),
|
|
('DataDesc','z'),
|
|
('InfoLevel','<H'),
|
|
('ReceiveBufferSize','<H'),
|
|
)
|
|
|
|
class SMBNetShareEnumResponse(Structure):
|
|
structure = (
|
|
('Status','<H=0'),
|
|
('Convert','<H=0'),
|
|
('EntriesReturned','<H'),
|
|
('EntriesAvailable','<H'),
|
|
)
|
|
|
|
class NetShareInfo1(Structure):
|
|
structure = (
|
|
('NetworkName','13s'),
|
|
('Pad','<B=0'),
|
|
('Type','<H=0'),
|
|
('RemarkOffsetLow','<H=0'),
|
|
('RemarkOffsetHigh','<H=0'),
|
|
)
|
|
|
|
# \PIPE\LANMAN NetServerGetInfo
|
|
class SMBNetServerGetInfoResponse(Structure):
|
|
structure = (
|
|
('Status','<H=0'),
|
|
('Convert','<H=0'),
|
|
('TotalBytesAvailable','<H'),
|
|
)
|
|
|
|
class SMBNetServerInfo1(Structure):
|
|
# Level 1 Response
|
|
structure = (
|
|
('ServerName','16s'),
|
|
('MajorVersion','B=5'),
|
|
('MinorVersion','B=0'),
|
|
('ServerType','<L=3'),
|
|
('ServerCommentLow','<H=0'),
|
|
('ServerCommentHigh','<H=0'),
|
|
)
|
|
|
|
# \PIPE\LANMAN NetShareGetInfo
|
|
class SMBNetShareGetInfo(Structure):
|
|
structure = (
|
|
('RAPOpcode','<H=0'),
|
|
('ParamDesc','z'),
|
|
('DataDesc','z'),
|
|
('ShareName','z'),
|
|
('InfoLevel','<H'),
|
|
('ReceiveBufferSize','<H'),
|
|
)
|
|
|
|
class SMBNetShareGetInfoResponse(Structure):
|
|
structure = (
|
|
('Status','<H=0'),
|
|
('Convert','<H=0'),
|
|
('TotalBytesAvailable','<H'),
|
|
)
|
|
|
|
############# Security Features
|
|
class SecurityFeatures(Structure):
|
|
structure = (
|
|
('Key','<L=0'),
|
|
('CID','<H=0'),
|
|
('SequenceNumber','<H=0'),
|
|
)
|
|
|
|
############# SMB_COM_QUERY_INFORMATION2 (0x23)
|
|
class SMBQueryInformation2_Parameters(Structure):
|
|
structure = (
|
|
('Fid','<H'),
|
|
)
|
|
|
|
class SMBQueryInformation2Response_Parameters(Structure):
|
|
structure = (
|
|
('CreateDate','<H'),
|
|
('CreationTime','<H'),
|
|
('LastAccessDate','<H'),
|
|
('LastAccessTime','<H'),
|
|
('LastWriteDate','<H'),
|
|
('LastWriteTime','<H'),
|
|
('FileDataSize','<L'),
|
|
('FileAllocationSize','<L'),
|
|
('FileAttributes','<L'),
|
|
)
|
|
|
|
|
|
|
|
############# SMB_COM_SESSION_SETUP_ANDX (0x73)
|
|
class SMBSessionSetupAndX_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('MaxBuffer','<H'),
|
|
('MaxMpxCount','<H'),
|
|
('VCNumber','<H'),
|
|
('SessionKey','<L'),
|
|
('AnsiPwdLength','<H'),
|
|
('UnicodePwdLength','<H'),
|
|
('_reserved','<L=0'),
|
|
('Capabilities','<L'),
|
|
)
|
|
|
|
class SMBSessionSetupAndX_Extended_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('MaxBufferSize','<H'),
|
|
('MaxMpxCount','<H'),
|
|
('VcNumber','<H'),
|
|
('SessionKey','<L'),
|
|
('SecurityBlobLength','<H'),
|
|
('Reserved','<L=0'),
|
|
('Capabilities','<L'),
|
|
)
|
|
|
|
class SMBSessionSetupAndX_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('AnsiPwdLength','_-AnsiPwd','self["AnsiPwdLength"]'),
|
|
('UnicodePwdLength','_-UnicodePwd','self["UnicodePwdLength"]'),
|
|
('AnsiPwd',':=""'),
|
|
('UnicodePwd',':=""'),
|
|
('Account','z=""'),
|
|
('PrimaryDomain','z=""'),
|
|
('NativeOS','z=""'),
|
|
('NativeLanMan','z=""'),
|
|
)
|
|
|
|
UnicodeStructure = (
|
|
('AnsiPwdLength','_-AnsiPwd','self["AnsiPwdLength"]'),
|
|
('UnicodePwdLength','_-UnicodePwd','self["UnicodePwdLength"]'),
|
|
('AnsiPwd',':=""'),
|
|
('UnicodePwd',':=""'),
|
|
('Account','u=""'),
|
|
('PrimaryDomain','u=""'),
|
|
('NativeOS','u=""'),
|
|
('NativeLanMan','u=""'),
|
|
)
|
|
|
|
class SMBSessionSetupAndX_Extended_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('SecurityBlobLength','_-SecurityBlob','self["SecurityBlobLength"]'),
|
|
('SecurityBlob',':'),
|
|
('NativeOS','z=""'),
|
|
('NativeLanMan','z=""'),
|
|
)
|
|
|
|
UnicodeStructure = (
|
|
('SecurityBlobLength','_-SecurityBlob','self["SecurityBlobLength"]'),
|
|
('SecurityBlob',':'),
|
|
('NativeOS','u=""'),
|
|
('NativeLanMan','u=""'),
|
|
)
|
|
|
|
class SMBSessionSetupAndXResponse_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('Action','<H'),
|
|
)
|
|
|
|
class SMBSessionSetupAndX_Extended_Response_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('Action','<H=0'),
|
|
('SecurityBlobLength','<H'),
|
|
)
|
|
|
|
class SMBSessionSetupAndXResponse_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('NativeOS','z=""'),
|
|
('NativeLanMan','z=""'),
|
|
('PrimaryDomain','z=""'),
|
|
)
|
|
|
|
UnicodeStructure = (
|
|
('NativeOS','u=""'),
|
|
('NativeLanMan','u=""'),
|
|
('PrimaryDomain','u=""'),
|
|
)
|
|
|
|
class SMBSessionSetupAndX_Extended_Response_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('SecurityBlobLength','_-SecurityBlob','self["SecurityBlobLength"]'),
|
|
('SecurityBlob',':'),
|
|
('NativeOS','z=""'),
|
|
('NativeLanMan','z=""'),
|
|
)
|
|
|
|
UnicodeStructure = (
|
|
('SecurityBlobLength','_-SecurityBlob','self["SecurityBlobLength"]'),
|
|
('SecurityBlob',':'),
|
|
('PadLen','_-Pad','1 if (len(self["SecurityBlob"]) % 2 == 0) else 0'),
|
|
('Pad',':=""'),
|
|
('NativeOS','u=""'),
|
|
('NativeLanMan','u=""'),
|
|
)
|
|
def getData(self):
|
|
if self.structure == self.UnicodeStructure:
|
|
if len(self['SecurityBlob']) % 2 == 0:
|
|
self['Pad'] = '\x00'
|
|
return AsciiOrUnicodeStructure.getData(self)
|
|
|
|
############# SMB_COM_TREE_CONNECT (0x70)
|
|
class SMBTreeConnect_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
)
|
|
|
|
class SMBTreeConnect_Data(SMBCommand_Parameters):
|
|
structure = (
|
|
('PathFormat','"\x04'),
|
|
('Path','z'),
|
|
('PasswordFormat','"\x04'),
|
|
('Password','z'),
|
|
('ServiceFormat','"\x04'),
|
|
('Service','z'),
|
|
)
|
|
|
|
############# SMB_COM_TREE_CONNECT_ANDX (0x75)
|
|
class SMBTreeConnectAndX_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('Flags','<H=0'),
|
|
('PasswordLength','<H'),
|
|
)
|
|
|
|
class SMBTreeConnectAndXResponse_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('OptionalSupport','<H=0'),
|
|
)
|
|
|
|
class SMBTreeConnectAndXExtendedResponse_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('OptionalSupport','<H=1'),
|
|
('MaximalShareAccessRights','<L=0x1fffff'),
|
|
('GuestMaximalShareAccessRights','<L=0x1fffff'),
|
|
)
|
|
|
|
class SMBTreeConnectAndX_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('_PasswordLength','_-Password','self["_PasswordLength"]'),
|
|
('Password',':'),
|
|
('Path','z'),
|
|
('Service','z'),
|
|
)
|
|
|
|
UnicodeStructure = (
|
|
('_PasswordLength','_-Password','self["_PasswordLength"] if self["_PasswordLength"] > 0 else 1'),
|
|
('Password',':'),
|
|
('Path','u'),
|
|
('Service','z'),
|
|
)
|
|
|
|
class SMBTreeConnectAndXResponse_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('Service','z'),
|
|
('PadLen','_-Pad','self["PadLen"]'),
|
|
('Pad',':=""'),
|
|
('NativeFileSystem','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('Service','z'),
|
|
('PadLen','_-Pad','self["PadLen"]'),
|
|
('Pad',':=""'),
|
|
('NativeFileSystem','u'),
|
|
)
|
|
|
|
############# SMB_COM_NT_CREATE_ANDX (0xA2)
|
|
class SMBNtCreateAndX_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('_reserved', 'B=0'),
|
|
('FileNameLength','<H'), # NameLength
|
|
('CreateFlags','<L'), # Flags
|
|
('RootFid','<L=0'), # RootDirectoryFID
|
|
('AccessMask','<L'), # DesiredAccess
|
|
('AllocationSizeLo','<L=0'), # AllocationSize
|
|
('AllocationSizeHi','<L=0'),
|
|
('FileAttributes','<L=0'), # ExtFileAttributes
|
|
('ShareAccess','<L=3'), #
|
|
('Disposition','<L=1'), # CreateDisposition
|
|
('CreateOptions','<L'), # CreateOptions
|
|
('Impersonation','<L=2'),
|
|
('SecurityFlags','B=3'),
|
|
)
|
|
|
|
class SMBNtCreateAndXResponse_Parameters(SMBAndXCommand_Parameters):
|
|
# XXX Is there a memory leak in the response for NTCreate (where the Data section would be) in Win 2000, Win XP, and Win 2003?
|
|
structure = (
|
|
('OplockLevel', 'B=0'),
|
|
('Fid','<H'),
|
|
('CreateAction','<L'),
|
|
('CreateTime','<q=0'),
|
|
('LastAccessTime','<q=0'),
|
|
('LastWriteTime','<q=0'),
|
|
('LastChangeTime','<q=0'),
|
|
('FileAttributes','<L=0x80'),
|
|
('AllocationSize','<q=0'),
|
|
('EndOfFile','<q=0'),
|
|
('FileType','<H=0'),
|
|
('IPCState','<H=0'),
|
|
('IsDirectory','B'),
|
|
)
|
|
|
|
class SMBNtCreateAndXExtendedResponse_Parameters(SMBAndXCommand_Parameters):
|
|
# [MS-SMB] Extended response description
|
|
structure = (
|
|
('OplockLevel', 'B=0'),
|
|
('Fid','<H'),
|
|
('CreateAction','<L'),
|
|
('CreateTime','<q=0'),
|
|
('LastAccessTime','<q=0'),
|
|
('LastWriteTime','<q=0'),
|
|
('LastChangeTime','<q=0'),
|
|
('FileAttributes','<L=0x80'),
|
|
('AllocationSize','<q=0'),
|
|
('EndOfFile','<q=0'),
|
|
('FileType','<H=0'),
|
|
('IPCState','<H=0'),
|
|
('IsDirectory','B'),
|
|
('VolumeGUID','16s'),
|
|
('FileIdLow','<L=0'),
|
|
('FileIdHigh','<L=0'),
|
|
('MaximalAccessRights','<L=0x12019b'),
|
|
('GuestMaximalAccessRights','<L=0x120089'),
|
|
)
|
|
|
|
class SMBNtCreateAndX_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('Pad','B'),
|
|
('FileName','u'),
|
|
)
|
|
|
|
############# SMB_COM_OPEN_ANDX (0xD2)
|
|
class SMBOpenAndX_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('Flags','<H=0'),
|
|
('DesiredAccess','<H=0'),
|
|
('SearchAttributes','<H=0'),
|
|
('FileAttributes','<H=0'),
|
|
('CreationTime','<L=0'),
|
|
('OpenMode','<H=1'), # SMB_O_OPEN = 1
|
|
('AllocationSize','<L=0'),
|
|
('Reserved','8s=""'),
|
|
)
|
|
|
|
class SMBOpenAndX_Data(SMBNtCreateAndX_Data):
|
|
pass
|
|
|
|
class SMBOpenAndXResponse_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('Fid','<H=0'),
|
|
('FileAttributes','<H=0'),
|
|
('LastWriten','<L=0'),
|
|
('FileSize','<L=0'),
|
|
('GrantedAccess','<H=0'),
|
|
('FileType','<H=0'),
|
|
('IPCState','<H=0'),
|
|
('Action','<H=0'),
|
|
('ServerFid','<L=0'),
|
|
('_reserved','<H=0'),
|
|
)
|
|
|
|
############# SMB_COM_WRITE (0x0B)
|
|
class SMBWrite_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('Fid','<H'),
|
|
('Count','<H'),
|
|
('Offset','<L'),
|
|
('Remaining','<H'),
|
|
)
|
|
|
|
class SMBWriteResponse_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('Count','<H'),
|
|
)
|
|
|
|
class SMBWrite_Data(Structure):
|
|
structure = (
|
|
('BufferFormat','<B=1'),
|
|
('DataLength','<H-Data'),
|
|
('Data',':'),
|
|
)
|
|
|
|
|
|
############# SMB_COM_WRITE_ANDX (0x2F)
|
|
class SMBWriteAndX_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('Fid','<H=0'),
|
|
('Offset','<L=0'),
|
|
('_reserved','<L=0xff'),
|
|
('WriteMode','<H=8'),
|
|
('Remaining','<H=0'),
|
|
('DataLength_Hi','<H=0'),
|
|
('DataLength','<H=0'),
|
|
('DataOffset','<H=0'),
|
|
('HighOffset','<L=0'),
|
|
)
|
|
|
|
class SMBWriteAndX_Data_Short(Structure):
|
|
structure = (
|
|
('_PadLen','_-Pad','self["DataOffset"] - 59'),
|
|
('Pad',':'),
|
|
#('Pad','<B=0'),
|
|
('DataLength','_-Data','self["DataLength"]'),
|
|
('Data',':'),
|
|
)
|
|
|
|
class SMBWriteAndX_Data(Structure):
|
|
structure = (
|
|
('_PadLen','_-Pad','self["DataOffset"] - 63'),
|
|
('Pad',':'),
|
|
#('Pad','<B=0'),
|
|
('DataLength','_-Data','self["DataLength"]'),
|
|
('Data',':'),
|
|
)
|
|
|
|
|
|
class SMBWriteAndX_Parameters_Short(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('Fid','<H'),
|
|
('Offset','<L'),
|
|
('_reserved','<L=0xff'),
|
|
('WriteMode','<H=8'),
|
|
('Remaining','<H'),
|
|
('DataLength_Hi','<H=0'),
|
|
('DataLength','<H'),
|
|
('DataOffset','<H=0'),
|
|
)
|
|
|
|
class SMBWriteAndXResponse_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('Count','<H'),
|
|
('Available','<H'),
|
|
('Reserved','<L=0'),
|
|
)
|
|
|
|
############# SMB_COM_WRITE_RAW (0x1D)
|
|
class SMBWriteRaw_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('Fid','<H'),
|
|
('Count','<H'),
|
|
('_reserved','<H=0'),
|
|
('Offset','<L'),
|
|
('Timeout','<L=0'),
|
|
('WriteMode','<H=0'),
|
|
('_reserved2','<L=0'),
|
|
('DataLength','<H'),
|
|
('DataOffset','<H=0'),
|
|
)
|
|
|
|
############# SMB_COM_READ (0x0A)
|
|
class SMBRead_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('Fid','<H'),
|
|
('Count','<H'),
|
|
('Offset','<L'),
|
|
('Remaining','<H=Count'),
|
|
)
|
|
|
|
class SMBReadResponse_Parameters(Structure):
|
|
structure = (
|
|
('Count','<H=0'),
|
|
('_reserved','8s=""'),
|
|
)
|
|
|
|
class SMBReadResponse_Data(Structure):
|
|
structure = (
|
|
('BufferFormat','<B=0x1'),
|
|
('DataLength','<H-Data'),
|
|
('Data',':'),
|
|
)
|
|
|
|
############# SMB_COM_READ_RAW (0x1A)
|
|
class SMBReadRaw_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('Fid','<H'),
|
|
('Offset','<L'),
|
|
('MaxCount','<H'),
|
|
('MinCount','<H=MaxCount'),
|
|
('Timeout','<L=0'),
|
|
('_reserved','<H=0'),
|
|
)
|
|
|
|
############# SMB_COM_NT_TRANSACT (0xA0)
|
|
class SMBNTTransaction_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('MaxSetupCount','<B=0'),
|
|
('Reserved1','<H=0'),
|
|
('TotalParameterCount','<L'),
|
|
('TotalDataCount','<L'),
|
|
('MaxParameterCount','<L=1024'),
|
|
('MaxDataCount','<L=65504'),
|
|
('ParameterCount','<L'),
|
|
('ParameterOffset','<L'),
|
|
('DataCount','<L'),
|
|
('DataOffset','<L'),
|
|
('SetupCount','<B=len(Setup)//2'),
|
|
('Function','<H=0'),
|
|
('SetupLength','_-Setup','SetupCount*2'),
|
|
('Setup',':'),
|
|
)
|
|
|
|
class SMBNTTransactionResponse_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('Reserved1','3s=""'),
|
|
('TotalParameterCount','<L'),
|
|
('TotalDataCount','<L'),
|
|
('ParameterCount','<L'),
|
|
('ParameterOffset','<L'),
|
|
('ParameterDisplacement','<L=0'),
|
|
('DataCount','<L'),
|
|
('DataOffset','<L'),
|
|
('DataDisplacement','<L=0'),
|
|
('SetupCount','<B=0'),
|
|
('SetupLength','_-Setup','SetupCount*2'),
|
|
('Setup',':'),
|
|
)
|
|
|
|
class SMBNTTransaction_Data(Structure):
|
|
structure = (
|
|
('Pad1Length','_-Pad1','self["Pad1Length"]'),
|
|
('Pad1',':'),
|
|
('NT_Trans_ParametersLength','_-NT_Trans_Parameters','self["NT_Trans_ParametersLength"]'),
|
|
('NT_Trans_Parameters',':'),
|
|
('Pad2Length','_-Pad2','self["Pad2Length"]'),
|
|
('Pad2',':'),
|
|
('NT_Trans_DataLength','_-NT_Trans_Data','self["NT_Trans_DataLength"]'),
|
|
('NT_Trans_Data',':'),
|
|
)
|
|
|
|
class SMBNTTransactionResponse_Data(Structure):
|
|
structure = (
|
|
('Pad1Length','_-Pad1','self["Pad1Length"]'),
|
|
('Pad1',':'),
|
|
('Trans_ParametersLength','_-Trans_Parameters','self["Trans_ParametersLength"]'),
|
|
('Trans_Parameters',':'),
|
|
('Pad2Length','_-Pad2','self["Pad2Length"]'),
|
|
('Pad2',':'),
|
|
('Trans_DataLength','_-Trans_Data','self["Trans_DataLength"]'),
|
|
('Trans_Data',':'),
|
|
)
|
|
|
|
|
|
############# SMB_COM_TRANSACTION2_SECONDARY (0x33)
|
|
class SMBTransaction2Secondary_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('TotalParameterCount','<H'),
|
|
('TotalDataCount','<H'),
|
|
('ParameterCount','<H'),
|
|
('ParameterOffset','<H'),
|
|
('ParameterDisplacement','<H'),
|
|
('DataCount','<H'),
|
|
('DataOffset','<H'),
|
|
('DataDisplacement','<H=0'),
|
|
('FID','<H'),
|
|
)
|
|
|
|
class SMBTransaction2Secondary_Data(Structure):
|
|
structure = (
|
|
('Pad1Length','_-Pad1','self["Pad1Length"]'),
|
|
('Pad1',':'),
|
|
('Trans_ParametersLength','_-Trans_Parameters','self["Trans_ParametersLength"]'),
|
|
('Trans_Parameters',':'),
|
|
('Pad2Length','_-Pad2','self["Pad2Length"]'),
|
|
('Pad2',':'),
|
|
('Trans_DataLength','_-Trans_Data','self["Trans_DataLength"]'),
|
|
('Trans_Data',':'),
|
|
)
|
|
|
|
|
|
############# SMB_COM_TRANSACTION2 (0x32)
|
|
|
|
class SMBTransaction2_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('TotalParameterCount','<H'),
|
|
('TotalDataCount','<H'),
|
|
('MaxParameterCount','<H=1024'),
|
|
('MaxDataCount','<H=65504'),
|
|
('MaxSetupCount','<B=0'),
|
|
('Reserved1','<B=0'),
|
|
('Flags','<H=0'),
|
|
('Timeout','<L=0'),
|
|
('Reserved2','<H=0'),
|
|
('ParameterCount','<H'),
|
|
('ParameterOffset','<H'),
|
|
('DataCount','<H'),
|
|
('DataOffset','<H'),
|
|
('SetupCount','<B=len(Setup)//2'),
|
|
('Reserved3','<B=0'),
|
|
('SetupLength','_-Setup','SetupCount*2'),
|
|
('Setup',':'),
|
|
)
|
|
|
|
class SMBTransaction2Response_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('TotalParameterCount','<H'),
|
|
('TotalDataCount','<H'),
|
|
('Reserved1','<H=0'),
|
|
('ParameterCount','<H'),
|
|
('ParameterOffset','<H'),
|
|
('ParameterDisplacement','<H=0'),
|
|
('DataCount','<H'),
|
|
('DataOffset','<H'),
|
|
('DataDisplacement','<H=0'),
|
|
('SetupCount','<B=0'),
|
|
('Reserved2','<B=0'),
|
|
('SetupLength','_-Setup','SetupCount*2'),
|
|
('Setup',':'),
|
|
)
|
|
|
|
class SMBTransaction2_Data(Structure):
|
|
structure = (
|
|
# ('NameLength','_-Name','1'),
|
|
# ('Name',':'),
|
|
('Pad1Length','_-Pad1','self["Pad1Length"]'),
|
|
('Pad1',':'),
|
|
('Trans_ParametersLength','_-Trans_Parameters','self["Trans_ParametersLength"]'),
|
|
('Trans_Parameters',':'),
|
|
('Pad2Length','_-Pad2','self["Pad2Length"]'),
|
|
('Pad2',':'),
|
|
('Trans_DataLength','_-Trans_Data','self["Trans_DataLength"]'),
|
|
('Trans_Data',':'),
|
|
)
|
|
|
|
class SMBTransaction2Response_Data(Structure):
|
|
structure = (
|
|
('Pad1Length','_-Pad1','self["Pad1Length"]'),
|
|
('Pad1',':'),
|
|
('Trans_ParametersLength','_-Trans_Parameters','self["Trans_ParametersLength"]'),
|
|
('Trans_Parameters',':'),
|
|
('Pad2Length','_-Pad2','self["Pad2Length"]'),
|
|
('Pad2',':'),
|
|
('Trans_DataLength','_-Trans_Data','self["Trans_DataLength"]'),
|
|
('Trans_Data',':'),
|
|
)
|
|
|
|
############# SMB_COM_QUERY_INFORMATION (0x08)
|
|
|
|
class SMBQueryInformation_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('BufferFormat','B=4'),
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('BufferFormat','B=4'),
|
|
('FileName','u'),
|
|
)
|
|
|
|
|
|
class SMBQueryInformationResponse_Parameters(Structure):
|
|
structure = (
|
|
('FileAttributes','<H'),
|
|
('LastWriteTime','<L'),
|
|
('FileSize','<L'),
|
|
('Reserved','"0123456789'),
|
|
)
|
|
|
|
############# SMB_COM_TRANSACTION (0x25)
|
|
class SMBTransaction_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('TotalParameterCount','<H'),
|
|
('TotalDataCount','<H'),
|
|
('MaxParameterCount','<H=1024'),
|
|
('MaxDataCount','<H=65504'),
|
|
('MaxSetupCount','<B=0'),
|
|
('Reserved1','<B=0'),
|
|
('Flags','<H=0'),
|
|
('Timeout','<L=0'),
|
|
('Reserved2','<H=0'),
|
|
('ParameterCount','<H'),
|
|
('ParameterOffset','<H'),
|
|
('DataCount','<H'),
|
|
('DataOffset','<H'),
|
|
('SetupCount','<B=len(Setup)//2'),
|
|
('Reserved3','<B=0'),
|
|
('SetupLength','_-Setup','SetupCount*2'),
|
|
('Setup',':'),
|
|
)
|
|
|
|
class SMBTransactionResponse_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('TotalParameterCount','<H'),
|
|
('TotalDataCount','<H'),
|
|
('Reserved1','<H=0'),
|
|
('ParameterCount','<H'),
|
|
('ParameterOffset','<H'),
|
|
('ParameterDisplacement','<H=0'),
|
|
('DataCount','<H'),
|
|
('DataOffset','<H'),
|
|
('DataDisplacement','<H=0'),
|
|
('SetupCount','<B'),
|
|
('Reserved2','<B=0'),
|
|
('SetupLength','_-Setup','SetupCount*2'),
|
|
('Setup',':'),
|
|
)
|
|
|
|
# TODO: We should merge these both. But this will require fixing
|
|
# the instances where this structure is used on the client side
|
|
class SMBTransaction_SData(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('Name','z'),
|
|
('Trans_ParametersLength','_-Trans_Parameters'),
|
|
('Trans_Parameters',':'),
|
|
('Trans_DataLength','_-Trans_Data'),
|
|
('Trans_Data',':'),
|
|
)
|
|
UnicodeStructure = (
|
|
('Pad','B'),
|
|
('Name','u'),
|
|
('Trans_ParametersLength','_-Trans_Parameters'),
|
|
('Trans_Parameters',':'),
|
|
('Trans_DataLength','_-Trans_Data'),
|
|
('Trans_Data',':'),
|
|
)
|
|
|
|
class SMBTransaction_Data(Structure):
|
|
structure = (
|
|
('NameLength','_-Name'),
|
|
('Name',':'),
|
|
('Trans_ParametersLength','_-Trans_Parameters'),
|
|
('Trans_Parameters',':'),
|
|
('Trans_DataLength','_-Trans_Data'),
|
|
('Trans_Data',':'),
|
|
)
|
|
|
|
class SMBTransactionResponse_Data(Structure):
|
|
structure = (
|
|
('Trans_ParametersLength','_-Trans_Parameters'),
|
|
('Trans_Parameters',':'),
|
|
('Trans_DataLength','_-Trans_Data'),
|
|
('Trans_Data',':'),
|
|
)
|
|
|
|
############# SMB_COM_READ_ANDX (0x2E)
|
|
class SMBReadAndX_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('Fid','<H'),
|
|
('Offset','<L'),
|
|
('MaxCount','<H'),
|
|
('MinCount','<H=MaxCount'),
|
|
('_reserved','<L=0x0'),
|
|
('Remaining','<H=MaxCount'),
|
|
('HighOffset','<L=0'),
|
|
)
|
|
|
|
class SMBReadAndX_Parameters2(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('Fid','<H'),
|
|
('Offset','<L'),
|
|
('MaxCount','<H'),
|
|
('MinCount','<H=MaxCount'),
|
|
('_reserved','<L=0xffffffff'),
|
|
('Remaining','<H=MaxCount'),
|
|
)
|
|
|
|
class SMBReadAndXResponse_Parameters(SMBAndXCommand_Parameters):
|
|
structure = (
|
|
('Remaining','<H=0'),
|
|
('DataMode','<H=0'),
|
|
('_reserved','<H=0'),
|
|
('DataCount','<H'),
|
|
('DataOffset','<H'),
|
|
('DataCount_Hi','<L'),
|
|
('_reserved2','6s=""'),
|
|
)
|
|
|
|
############# SMB_COM_ECHO (0x2B)
|
|
class SMBEcho_Data(Structure):
|
|
structure = (
|
|
('Data',':'),
|
|
)
|
|
|
|
class SMBEcho_Parameters(Structure):
|
|
structure = (
|
|
('EchoCount','<H'),
|
|
)
|
|
|
|
class SMBEchoResponse_Data(Structure):
|
|
structure = (
|
|
('Data',':'),
|
|
)
|
|
|
|
class SMBEchoResponse_Parameters(Structure):
|
|
structure = (
|
|
('SequenceNumber','<H=1'),
|
|
)
|
|
|
|
############# SMB_COM_QUERY_INFORMATION_DISK (0x80)
|
|
class SMBQueryInformationDiskResponse_Parameters(Structure):
|
|
structure = (
|
|
('TotalUnits','<H'),
|
|
('BlocksPerUnit','<H'),
|
|
('BlockSize','<H'),
|
|
('FreeUnits','<H'),
|
|
('Reserved','<H=0'),
|
|
)
|
|
|
|
|
|
############# SMB_COM_LOGOFF_ANDX (0x74)
|
|
class SMBLogOffAndX(SMBAndXCommand_Parameters):
|
|
structure = ()
|
|
|
|
############# SMB_COM_CLOSE (0x04)
|
|
class SMBClose_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('FID','<H'),
|
|
('Time','<L=0'),
|
|
)
|
|
|
|
############# SMB_COM_FLUSH (0x05)
|
|
class SMBFlush_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('FID','<H'),
|
|
)
|
|
|
|
############# SMB_COM_CREATE_DIRECTORY (0x00)
|
|
class SMBCreateDirectory_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('BufferFormat','<B=4'),
|
|
('DirectoryName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('BufferFormat','<B=4'),
|
|
('DirectoryName','u'),
|
|
)
|
|
|
|
############# SMB_COM_DELETE (0x06)
|
|
class SMBDelete_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('BufferFormat','<B=4'),
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('BufferFormat','<B=4'),
|
|
('FileName','u'),
|
|
)
|
|
|
|
class SMBDelete_Parameters(Structure):
|
|
structure = (
|
|
('SearchAttributes','<H'),
|
|
)
|
|
|
|
############# SMB_COM_DELETE_DIRECTORY (0x01)
|
|
class SMBDeleteDirectory_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('BufferFormat','<B=4'),
|
|
('DirectoryName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('BufferFormat','<B=4'),
|
|
('DirectoryName','u'),
|
|
)
|
|
|
|
############# SMB_COM_CHECK_DIRECTORY (0x10)
|
|
class SMBCheckDirectory_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('BufferFormat','<B=4'),
|
|
('DirectoryName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('BufferFormat','<B=4'),
|
|
('DirectoryName','u'),
|
|
)
|
|
|
|
############# SMB_COM_RENAME (0x07)
|
|
class SMBRename_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('SearchAttributes','<H'),
|
|
)
|
|
|
|
class SMBRename_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('BufferFormat1','<B=4'),
|
|
('OldFileName','z'),
|
|
('BufferFormat2','<B=4'),
|
|
('NewFileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('BufferFormat1','<B=4'),
|
|
('OldFileName','u'),
|
|
('BufferFormat2','<B=4'),
|
|
('Pad','B=0'),
|
|
('NewFileName','u'),
|
|
)
|
|
|
|
|
|
############# SMB_COM_OPEN (0x02)
|
|
class SMBOpen_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('DesiredAccess','<H=0'),
|
|
('SearchAttributes','<H=0'),
|
|
)
|
|
|
|
class SMBOpen_Data(AsciiOrUnicodeStructure):
|
|
AsciiStructure = (
|
|
('FileNameFormat','"\x04'),
|
|
('FileName','z'),
|
|
)
|
|
UnicodeStructure = (
|
|
('FileNameFormat','"\x04'),
|
|
('FileName','u'),
|
|
)
|
|
|
|
class SMBOpenResponse_Parameters(SMBCommand_Parameters):
|
|
structure = (
|
|
('Fid','<H=0'),
|
|
('FileAttributes','<H=0'),
|
|
('LastWriten','<L=0'),
|
|
('FileSize','<L=0'),
|
|
('GrantedAccess','<H=0'),
|
|
)
|
|
|
|
############# EXTENDED SECURITY CLASSES
|
|
class SMBExtended_Security_Parameters(Structure):
|
|
structure = (
|
|
('DialectIndex','<H'),
|
|
('SecurityMode','<B'),
|
|
('MaxMpxCount','<H'),
|
|
('MaxNumberVcs','<H'),
|
|
('MaxBufferSize','<L'),
|
|
('MaxRawSize','<L'),
|
|
('SessionKey','<L'),
|
|
('Capabilities','<L'),
|
|
('LowDateTime','<L'),
|
|
('HighDateTime','<L'),
|
|
('ServerTimeZone','<H'),
|
|
('ChallengeLength','<B'),
|
|
)
|
|
|
|
class SMBExtended_Security_Data(Structure):
|
|
structure = (
|
|
('ServerGUID','16s'),
|
|
('SecurityBlob',':'),
|
|
)
|
|
|
|
class SMBNTLMDialect_Parameters(Structure):
|
|
structure = (
|
|
('DialectIndex','<H'),
|
|
('SecurityMode','<B'),
|
|
('MaxMpxCount','<H'),
|
|
('MaxNumberVcs','<H'),
|
|
('MaxBufferSize','<L'),
|
|
('MaxRawSize','<L'),
|
|
('SessionKey','<L'),
|
|
('Capabilities','<L'),
|
|
('LowDateTime','<L'),
|
|
('HighDateTime','<L'),
|
|
('ServerTimeZone','<H'),
|
|
('ChallengeLength','<B'),
|
|
)
|
|
|
|
class SMBNTLMDialect_Data(Structure):
|
|
structure = (
|
|
('ChallengeLength','_-Challenge','self["ChallengeLength"]'),
|
|
('Challenge',':'),
|
|
('Payload',':'),
|
|
# For some reason on an old Linux this field is not present, we have to check this out. There must be a flag stating this.
|
|
('DomainName','_'),
|
|
('ServerName','_'),
|
|
)
|
|
def __init__(self,data = None, alignment = 0):
|
|
Structure.__init__(self,data,alignment)
|
|
#self['ChallengeLength']=8
|
|
|
|
def fromString(self,data):
|
|
Structure.fromString(self,data)
|
|
self['DomainName'] = ''
|
|
self['ServerName'] = ''
|
|
|
|
class SMB(object):
|
|
|
|
class HostnameValidationException(Exception):
|
|
pass
|
|
|
|
# SMB Command Codes
|
|
SMB_COM_CREATE_DIRECTORY = 0x00
|
|
SMB_COM_DELETE_DIRECTORY = 0x01
|
|
SMB_COM_OPEN = 0x02
|
|
SMB_COM_CREATE = 0x03
|
|
SMB_COM_CLOSE = 0x04
|
|
SMB_COM_FLUSH = 0x05
|
|
SMB_COM_DELETE = 0x06
|
|
SMB_COM_RENAME = 0x07
|
|
SMB_COM_QUERY_INFORMATION = 0x08
|
|
SMB_COM_SET_INFORMATION = 0x09
|
|
SMB_COM_READ = 0x0A
|
|
SMB_COM_WRITE = 0x0B
|
|
SMB_COM_LOCK_BYTE_RANGE = 0x0C
|
|
SMB_COM_UNLOCK_BYTE_RANGE = 0x0D
|
|
SMB_COM_CREATE_TEMPORARY = 0x0E
|
|
SMB_COM_CREATE_NEW = 0x0F
|
|
SMB_COM_CHECK_DIRECTORY = 0x10
|
|
SMB_COM_PROCESS_EXIT = 0x11
|
|
SMB_COM_SEEK = 0x12
|
|
SMB_COM_LOCK_AND_READ = 0x13
|
|
SMB_COM_WRITE_AND_UNLOCK = 0x14
|
|
SMB_COM_READ_RAW = 0x1A
|
|
SMB_COM_READ_MPX = 0x1B
|
|
SMB_COM_READ_MPX_SECONDARY = 0x1C
|
|
SMB_COM_WRITE_RAW = 0x1D
|
|
SMB_COM_WRITE_MPX = 0x1E
|
|
SMB_COM_WRITE_MPX_SECONDARY = 0x1F
|
|
SMB_COM_WRITE_COMPLETE = 0x20
|
|
SMB_COM_QUERY_SERVER = 0x21
|
|
SMB_COM_SET_INFORMATION2 = 0x22
|
|
SMB_COM_QUERY_INFORMATION2 = 0x23
|
|
SMB_COM_LOCKING_ANDX = 0x24
|
|
SMB_COM_TRANSACTION = 0x25
|
|
SMB_COM_TRANSACTION_SECONDARY = 0x26
|
|
SMB_COM_IOCTL = 0x27
|
|
SMB_COM_IOCTL_SECONDARY = 0x28
|
|
SMB_COM_COPY = 0x29
|
|
SMB_COM_MOVE = 0x2A
|
|
SMB_COM_ECHO = 0x2B
|
|
SMB_COM_WRITE_AND_CLOSE = 0x2C
|
|
SMB_COM_OPEN_ANDX = 0x2D
|
|
SMB_COM_READ_ANDX = 0x2E
|
|
SMB_COM_WRITE_ANDX = 0x2F
|
|
SMB_COM_NEW_FILE_SIZE = 0x30
|
|
SMB_COM_CLOSE_AND_TREE_DISC = 0x31
|
|
SMB_COM_TRANSACTION2 = 0x32
|
|
SMB_COM_TRANSACTION2_SECONDARY = 0x33
|
|
SMB_COM_FIND_CLOSE2 = 0x34
|
|
SMB_COM_FIND_NOTIFY_CLOSE = 0x35
|
|
# Used by Xenix/Unix 0x60 - 0x6E
|
|
SMB_COM_TREE_CONNECT = 0x70
|
|
SMB_COM_TREE_DISCONNECT = 0x71
|
|
SMB_COM_NEGOTIATE = 0x72
|
|
SMB_COM_SESSION_SETUP_ANDX = 0x73
|
|
SMB_COM_LOGOFF_ANDX = 0x74
|
|
SMB_COM_TREE_CONNECT_ANDX = 0x75
|
|
SMB_COM_QUERY_INFORMATION_DISK = 0x80
|
|
SMB_COM_SEARCH = 0x81
|
|
SMB_COM_FIND = 0x82
|
|
SMB_COM_FIND_UNIQUE = 0x83
|
|
SMB_COM_FIND_CLOSE = 0x84
|
|
SMB_COM_NT_TRANSACT = 0xA0
|
|
SMB_COM_NT_TRANSACT_SECONDARY = 0xA1
|
|
SMB_COM_NT_CREATE_ANDX = 0xA2
|
|
SMB_COM_NT_CANCEL = 0xA4
|
|
SMB_COM_NT_RENAME = 0xA5
|
|
SMB_COM_OPEN_PRINT_FILE = 0xC0
|
|
SMB_COM_WRITE_PRINT_FILE = 0xC1
|
|
SMB_COM_CLOSE_PRINT_FILE = 0xC2
|
|
SMB_COM_GET_PRINT_QUEUE = 0xC3
|
|
SMB_COM_READ_BULK = 0xD8
|
|
SMB_COM_WRITE_BULK = 0xD9
|
|
SMB_COM_WRITE_BULK_DATA = 0xDA
|
|
|
|
# TRANSACT codes
|
|
TRANS_TRANSACT_NMPIPE = 0x26
|
|
|
|
# TRANSACT2 codes
|
|
TRANS2_FIND_FIRST2 = 0x0001
|
|
TRANS2_FIND_NEXT2 = 0x0002
|
|
TRANS2_QUERY_FS_INFORMATION = 0x0003
|
|
TRANS2_QUERY_PATH_INFORMATION = 0x0005
|
|
TRANS2_QUERY_FILE_INFORMATION = 0x0007
|
|
TRANS2_SET_FILE_INFORMATION = 0x0008
|
|
TRANS2_SET_PATH_INFORMATION = 0x0006
|
|
|
|
# Security Share Mode (Used internally by SMB class)
|
|
SECURITY_SHARE_MASK = 0x01
|
|
SECURITY_SHARE_SHARE = 0x00
|
|
SECURITY_SHARE_USER = 0x01
|
|
SECURITY_SIGNATURES_ENABLED = 0X04
|
|
SECURITY_SIGNATURES_REQUIRED = 0X08
|
|
|
|
# Security Auth Mode (Used internally by SMB class)
|
|
SECURITY_AUTH_MASK = 0x02
|
|
SECURITY_AUTH_ENCRYPTED = 0x02
|
|
SECURITY_AUTH_PLAINTEXT = 0x00
|
|
|
|
# Raw Mode Mask (Used internally by SMB class. Good for dialect up to and including LANMAN2.1)
|
|
RAW_READ_MASK = 0x01
|
|
RAW_WRITE_MASK = 0x02
|
|
|
|
# Capabilities Mask (Used internally by SMB class. Good for dialect NT LM 0.12)
|
|
CAP_RAW_MODE = 0x00000001
|
|
CAP_MPX_MODE = 0x0002
|
|
CAP_UNICODE = 0x0004
|
|
CAP_LARGE_FILES = 0x0008
|
|
CAP_EXTENDED_SECURITY = 0x80000000
|
|
CAP_USE_NT_ERRORS = 0x40
|
|
CAP_NT_SMBS = 0x10
|
|
CAP_LARGE_READX = 0x00004000
|
|
CAP_LARGE_WRITEX = 0x00008000
|
|
CAP_RPC_REMOTE_APIS = 0x20
|
|
|
|
# Flags1 Mask
|
|
FLAGS1_LOCK_AND_READ_OK = 0x01
|
|
FLAGS1_PATHCASELESS = 0x08
|
|
FLAGS1_CANONICALIZED_PATHS = 0x10
|
|
FLAGS1_REPLY = 0x80
|
|
|
|
# Flags2 Mask
|
|
FLAGS2_LONG_NAMES = 0x0001
|
|
FLAGS2_EAS = 0x0002
|
|
FLAGS2_SMB_SECURITY_SIGNATURE = 0x0004
|
|
FLAGS2_IS_LONG_NAME = 0x0040
|
|
FLAGS2_DFS = 0x1000
|
|
FLAGS2_PAGING_IO = 0x2000
|
|
FLAGS2_NT_STATUS = 0x4000
|
|
FLAGS2_UNICODE = 0x8000
|
|
FLAGS2_COMPRESSED = 0x0008
|
|
FLAGS2_SMB_SECURITY_SIGNATURE_REQUIRED = 0x0010
|
|
FLAGS2_EXTENDED_SECURITY = 0x0800
|
|
|
|
# Dialect's Security Mode flags
|
|
NEGOTIATE_USER_SECURITY = 0x01
|
|
NEGOTIATE_ENCRYPT_PASSWORDS = 0x02
|
|
NEGOTIATE_SECURITY_SIGNATURE_ENABLE = 0x04
|
|
NEGOTIATE_SECURITY_SIGNATURE_REQUIRED = 0x08
|
|
|
|
# Tree Connect AndX Response optionalSuppor flags
|
|
SMB_SUPPORT_SEARCH_BITS = 0x01
|
|
SMB_SHARE_IS_IN_DFS = 0x02
|
|
|
|
def __init__(self, remote_name, remote_host, my_name=None, host_type=nmb.TYPE_SERVER, sess_port=445, timeout=None,
|
|
UDP=0, session=None, negPacket=None):
|
|
# The uid attribute will be set when the client calls the login() method
|
|
self._uid = 0
|
|
self.__server_name = ''
|
|
self.__client_name = ''
|
|
self.__server_os = ''
|
|
self.__server_os_major = None
|
|
self.__server_os_minor = None
|
|
self.__server_os_build = None
|
|
self.__server_lanman = ''
|
|
self.__server_domain = ''
|
|
self.__server_dns_domain_name = ''
|
|
self.__server_dns_host_name = ''
|
|
self.__remote_name = remote_name.upper()
|
|
self.__remote_host = remote_host
|
|
self.__isNTLMv2 = True
|
|
self._dialects_parameters = None
|
|
self._dialects_data = None
|
|
self._doKerberos = False
|
|
|
|
# Credentials
|
|
self.__userName = b''
|
|
self.__password = b''
|
|
self.__domain = b''
|
|
self.__lmhash = b''
|
|
self.__nthash = b''
|
|
self.__aesKey = b''
|
|
self.__kdc = b''
|
|
self.__TGT = None
|
|
self.__TGS = None
|
|
|
|
# Negotiate Protocol Result, used everywhere
|
|
# Could be extended or not, flags should be checked before
|
|
self._dialect_data = 0
|
|
self._dialect_parameters = 0
|
|
self._action = 0
|
|
self._sess = None
|
|
self.encrypt_passwords = True
|
|
self.tid = 0
|
|
self.fid = 0
|
|
|
|
# Strict host validation - off by default
|
|
self._strict_hostname_validation = False
|
|
self._validation_allow_absent = True
|
|
self._accepted_hostname = ''
|
|
|
|
# Signing stuff
|
|
self._SignSequenceNumber = 0
|
|
self._SigningSessionKey = b''
|
|
self._SigningChallengeResponse = b''
|
|
self._SignatureEnabled = False
|
|
self._SignatureVerificationEnabled = False
|
|
self._SignatureRequired = False
|
|
|
|
# Base flags (default flags, can be overridden using set_flags())
|
|
self.__flags1 = SMB.FLAGS1_PATHCASELESS | SMB.FLAGS1_CANONICALIZED_PATHS
|
|
self.__flags2 = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES
|
|
|
|
if timeout is None:
|
|
self.__timeout = 60
|
|
else:
|
|
self.__timeout = timeout
|
|
|
|
# If port 445 and the name sent is *SMBSERVER we're setting the name to the IP.
|
|
# This is to help some old applications still believing
|
|
# *SMSBSERVER will work against modern OSes. If port is NETBIOS_SESSION_PORT the user better
|
|
# know about *SMBSERVER's limitations
|
|
if sess_port == 445 and remote_name == '*SMBSERVER':
|
|
self.__remote_name = remote_host
|
|
|
|
# This is on purpose. I'm still not convinced to do a socket.gethostname() if not specified
|
|
if my_name is None:
|
|
self.__client_name = b''
|
|
else:
|
|
self.__client_name = my_name
|
|
|
|
if session is None:
|
|
if not my_name:
|
|
# If destination port is 139 yes, there's some client disclosure
|
|
my_name = socket.gethostname()
|
|
i = my_name.find('.')
|
|
if i > -1:
|
|
my_name = my_name[:i]
|
|
|
|
if UDP:
|
|
self._sess = nmb.NetBIOSUDPSession(my_name, remote_name, remote_host, host_type, sess_port, self.__timeout)
|
|
else:
|
|
self._sess = nmb.NetBIOSTCPSession(my_name, remote_name, remote_host, host_type, sess_port, self.__timeout)
|
|
|
|
# Initialize session values (_dialect_data and _dialect_parameters)
|
|
self.neg_session()
|
|
|
|
# Call login() without any authentication information to
|
|
# setup a session if the remote server
|
|
# is in share mode.
|
|
if (self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SHARE_MASK) == SMB.SECURITY_SHARE_SHARE:
|
|
self.login('', '')
|
|
else:
|
|
self._sess = session
|
|
self.neg_session(negPacket = negPacket)
|
|
# Call login() without any authentication information to
|
|
# setup a session if the remote server
|
|
# is in share mode.
|
|
if (self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SHARE_MASK) == SMB.SECURITY_SHARE_SHARE:
|
|
self.login('', '')
|
|
|
|
@staticmethod
|
|
def ntlm_supported():
|
|
return False
|
|
|
|
def getKerberos(self):
|
|
return self._doKerberos
|
|
|
|
def get_remote_name(self):
|
|
return self.__remote_name
|
|
|
|
def set_remote_name(self, name):
|
|
self.__remote_name = name
|
|
return True
|
|
|
|
def set_hostname_validation(self, validate, accept_empty, hostname):
|
|
self._strict_hostname_validation = validate
|
|
self._validation_allow_absent = accept_empty
|
|
self._accepted_hostname = hostname
|
|
|
|
def get_remote_host(self):
|
|
return self.__remote_host
|
|
|
|
def get_flags(self):
|
|
return self.__flags1, self.__flags2
|
|
|
|
def set_flags(self, flags1=None, flags2=None):
|
|
if flags1 is not None:
|
|
self.__flags1 = flags1
|
|
if flags2 is not None:
|
|
self.__flags2 = flags2
|
|
|
|
def set_timeout(self, timeout):
|
|
prev_timeout = self.__timeout
|
|
self.__timeout = timeout
|
|
return prev_timeout
|
|
|
|
def get_timeout(self):
|
|
return self.__timeout
|
|
|
|
@contextmanager
|
|
def use_timeout(self, timeout):
|
|
prev_timeout = self.set_timeout(timeout)
|
|
try:
|
|
yield
|
|
finally:
|
|
self.set_timeout(prev_timeout)
|
|
|
|
def get_session(self):
|
|
return self._sess
|
|
|
|
def get_tid(self):
|
|
return self.tid
|
|
|
|
def get_fid(self):
|
|
return self.fid
|
|
|
|
def isGuestSession(self):
|
|
return self._action & SMB_SETUP_GUEST
|
|
|
|
def doesSupportNTLMv2(self):
|
|
return self.__isNTLMv2
|
|
|
|
def close_session(self):
|
|
if self._sess:
|
|
self._sess.close()
|
|
self._sess = None
|
|
|
|
def recvSMB(self):
|
|
r = self._sess.recv_packet(self.__timeout)
|
|
return NewSMBPacket(data = r.get_trailer())
|
|
|
|
@staticmethod
|
|
def __decode_trans(params, data):
|
|
totparamcnt, totdatacnt, _, paramcnt, paramoffset, paramds, datacnt, dataoffset, datads, setupcnt = unpack('<HHHHHHHHHB', params[:19])
|
|
if paramcnt + paramds < totparamcnt or datacnt + datads < totdatacnt:
|
|
has_more = 1
|
|
else:
|
|
has_more = 0
|
|
paramoffset = paramoffset - 55 - setupcnt * 2
|
|
dataoffset = dataoffset - 55 - setupcnt * 2
|
|
return has_more, params[20:20 + setupcnt * 2], data[paramoffset:paramoffset + paramcnt], data[dataoffset:dataoffset + datacnt]
|
|
|
|
# TODO: Move this to NewSMBPacket, it belongs there
|
|
def signSMB(self, packet, signingSessionKey, signingChallengeResponse):
|
|
# This logic MUST be applied for messages sent in response to any of the higher-layer actions and in
|
|
# compliance with the message sequencing rules.
|
|
# * The client or server that sends the message MUST provide the 32-bit sequence number for this
|
|
# message, as specified in sections 3.2.4.1 and 3.3.4.1.
|
|
# * The SMB_FLAGS2_SMB_SECURITY_SIGNATURE flag in the header MUST be set.
|
|
# * To generate the signature, a 32-bit sequence number is copied into the
|
|
# least significant 32 bits of the SecuritySignature field and the remaining
|
|
# 4 bytes are set to 0x00.
|
|
# * The MD5 algorithm, as specified in [RFC1321], MUST be used to generate a hash of the SMB
|
|
# message from the start of the SMB Header, which is defined as follows.
|
|
# CALL MD5Init( md5context )
|
|
# CALL MD5Update( md5context, Connection.SigningSessionKey )
|
|
# CALL MD5Update( md5context, Connection.SigningChallengeResponse )
|
|
# CALL MD5Update( md5context, SMB message )
|
|
# CALL MD5Final( digest, md5context )
|
|
# SET signature TO the first 8 bytes of the digest
|
|
# The resulting 8-byte signature MUST be copied into the SecuritySignature field of the SMB Header,
|
|
# after which the message can be transmitted.
|
|
|
|
#print "seq(%d) signingSessionKey %r, signingChallengeResponse %r" % (self._SignSequenceNumber, signingSessionKey, signingChallengeResponse)
|
|
packet['SecurityFeatures'] = pack('<q',self._SignSequenceNumber)
|
|
# Sign with the sequence
|
|
m = hashlib.md5()
|
|
m.update( signingSessionKey )
|
|
m.update( signingChallengeResponse )
|
|
m.update( packet.getData() )
|
|
# Replace sequence with acual hash
|
|
packet['SecurityFeatures'] = m.digest()[:8]
|
|
if self._SignatureVerificationEnabled:
|
|
self._SignSequenceNumber +=1
|
|
else:
|
|
self._SignSequenceNumber +=2
|
|
|
|
def checkSignSMB(self, packet, signingSessionKey, signingChallengeResponse):
|
|
# Let's check
|
|
signature = packet['SecurityFeatures']
|
|
#print "Signature received: %r " % signature
|
|
self.signSMB(packet, signingSessionKey, signingChallengeResponse)
|
|
#print "Signature calculated: %r" % packet['SecurityFeatures']
|
|
if self._SignatureVerificationEnabled is not True:
|
|
self._SignSequenceNumber -= 1
|
|
return packet['SecurityFeatures'] == signature
|
|
|
|
def sendSMB(self,smb):
|
|
smb['Uid'] = self._uid
|
|
#At least on AIX, PIDs can exceed 16 bits, so we mask them out
|
|
smb['Pid'] = (os.getpid() & 0xFFFF)
|
|
# set flags
|
|
smb['Flags1'] |= self.__flags1
|
|
smb['Flags2'] |= self.__flags2
|
|
if self._SignatureEnabled:
|
|
smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE
|
|
self.signSMB(smb, self._SigningSessionKey, self._SigningChallengeResponse)
|
|
|
|
self._sess.send_packet(smb.getData())
|
|
|
|
@staticmethod
|
|
def isValidAnswer(s, cmd):
|
|
while 1:
|
|
if s.rawData():
|
|
if s.get_command() == cmd:
|
|
if s.get_error_class() == 0x00 and s.get_error_code() == 0x00:
|
|
return 1
|
|
else:
|
|
raise SessionError( "SMB Library Error", s.get_error_class()+ (s.get_reserved() << 8), s.get_error_code() , s.get_flags2() & SMB.FLAGS2_NT_STATUS)
|
|
else:
|
|
break
|
|
return 0
|
|
|
|
def neg_session(self, extended_security = True, negPacket = None):
|
|
def parsePacket(smb):
|
|
# If server speaks Unicode, let's set that flag from now on
|
|
if smb['Flags2'] & SMB.FLAGS2_UNICODE:
|
|
self.__flags2 |= SMB.FLAGS2_UNICODE
|
|
|
|
if smb.isValidAnswer(SMB.SMB_COM_NEGOTIATE):
|
|
sessionResponse = SMBCommand(smb['Data'][0])
|
|
self._dialects_parameters = SMBNTLMDialect_Parameters(sessionResponse['Parameters'])
|
|
self._dialects_data = SMBNTLMDialect_Data()
|
|
self._dialects_data['ChallengeLength'] = self._dialects_parameters['ChallengeLength']
|
|
self._dialects_data.fromString(sessionResponse['Data'])
|
|
if self._dialects_parameters['Capabilities'] & SMB.CAP_EXTENDED_SECURITY:
|
|
# Whether we choose it or it is enforced by the server, we go for extended security
|
|
self._dialects_parameters = SMBExtended_Security_Parameters(sessionResponse['Parameters'])
|
|
self._dialects_data = SMBExtended_Security_Data(sessionResponse['Data'])
|
|
# Let's setup some variable for later use
|
|
if self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SIGNATURES_REQUIRED:
|
|
self._SignatureRequired = True
|
|
|
|
# Interestingly, the security Blob might be missing sometimes.
|
|
#spnego = SPNEGO_NegTokenInit(self._dialects_data['SecurityBlob'])
|
|
#for i in spnego['MechTypes']:
|
|
# print "Mech Found: %s" % MechTypes[i]
|
|
return 1
|
|
|
|
# If not, let's try the old way
|
|
else:
|
|
if self._dialects_data['ServerName'] is not None:
|
|
self.__server_name = self._dialects_data['ServerName']
|
|
|
|
if self._dialects_parameters['DialectIndex'] == 0xffff:
|
|
raise UnsupportedFeature("Remote server does not know NT LM 0.12")
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
if negPacket is None:
|
|
smb = NewSMBPacket()
|
|
negSession = SMBCommand(SMB.SMB_COM_NEGOTIATE)
|
|
flags2 = self.get_flags()[1]
|
|
if extended_security is True:
|
|
self.set_flags(flags2=flags2|SMB.FLAGS2_EXTENDED_SECURITY)
|
|
else:
|
|
self.set_flags(flags2=flags2 & (~SMB.FLAGS2_EXTENDED_SECURITY))
|
|
|
|
negSession['Data'] = b'\x02NT LM 0.12\x00'
|
|
smb.addCommand(negSession)
|
|
self.sendSMB(smb)
|
|
|
|
while 1:
|
|
smb = self.recvSMB()
|
|
return parsePacket(smb)
|
|
else:
|
|
|
|
return parsePacket( NewSMBPacket( data = negPacket))
|
|
|
|
def tree_connect(self, path, password = '', service = SERVICE_ANY):
|
|
LOG.warning("[MS-CIFS] This is an original Core Protocol command.This command has been deprecated.Client Implementations SHOULD use SMB_COM_TREE_CONNECT_ANDX")
|
|
|
|
# return 0x800
|
|
if password:
|
|
# Password is only encrypted if the server passed us an "encryption" during protocol dialect
|
|
if self._dialects_parameters['ChallengeLength'] > 0:
|
|
# this code is untested
|
|
password = self.get_ntlmv1_response(ntlm.compute_lmhash(password))
|
|
|
|
if not unicode_support:
|
|
if unicode_convert:
|
|
path = str(path)
|
|
else:
|
|
raise Exception('SMB: Can\t conver path from unicode!')
|
|
|
|
smb = NewSMBPacket()
|
|
treeConnect = SMBCommand(SMB.SMB_COM_TREE_CONNECT)
|
|
treeConnect['Parameters'] = SMBTreeConnect_Parameters()
|
|
treeConnect['Data'] = SMBTreeConnect_Data()
|
|
treeConnect['Data']['Path'] = path.upper()
|
|
treeConnect['Data']['Password'] = password
|
|
treeConnect['Data']['Service'] = service
|
|
smb.addCommand(treeConnect)
|
|
self.sendSMB(smb)
|
|
|
|
while 1:
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_TREE_CONNECT):
|
|
# XXX Here we are ignoring the rest of the response
|
|
return smb['Tid']
|
|
return smb['Tid']
|
|
|
|
def get_uid(self):
|
|
return self._uid
|
|
|
|
def set_uid(self, uid):
|
|
self._uid = uid
|
|
|
|
def tree_connect_andx(self, path, password = None, service = SERVICE_ANY, smb_packet=None):
|
|
if password:
|
|
# Password is only encrypted if the server passed us an "encryption" during protocol dialect
|
|
if self._dialects_parameters['ChallengeLength'] > 0:
|
|
# this code is untested
|
|
password = self.get_ntlmv1_response(ntlm.compute_lmhash(password))
|
|
else:
|
|
password = '\x00'
|
|
|
|
if not unicode_support:
|
|
if unicode_convert:
|
|
path = str(path)
|
|
else:
|
|
raise Exception('SMB: Can\t convert path from unicode!')
|
|
|
|
if smb_packet is None:
|
|
smb = NewSMBPacket()
|
|
else:
|
|
smb = smb_packet
|
|
|
|
# Just in case this came with the full path ,let's just leave
|
|
# the sharename, we'll take care of the rest
|
|
|
|
share = path.split('\\')[-1]
|
|
try:
|
|
_, _, _, _, sockaddr = socket.getaddrinfo(self.get_remote_host(), 80, 0, 0, socket.IPPROTO_TCP)[0]
|
|
remote_host = sockaddr[0]
|
|
except Exception:
|
|
remote_host = self.get_remote_host()
|
|
|
|
path = '\\\\' + remote_host + '\\' +share
|
|
path = path.upper().encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else path
|
|
|
|
treeConnect = SMBCommand(SMB.SMB_COM_TREE_CONNECT_ANDX)
|
|
treeConnect['Parameters'] = SMBTreeConnectAndX_Parameters()
|
|
treeConnect['Data'] = SMBTreeConnectAndX_Data(flags=self.__flags2)
|
|
treeConnect['Parameters']['PasswordLength'] = len(password)
|
|
treeConnect['Data']['Password'] = password
|
|
treeConnect['Data']['Path'] = path
|
|
treeConnect['Data']['Service'] = service
|
|
|
|
if self.__flags2 & SMB.FLAGS2_UNICODE:
|
|
treeConnect['Data']['Pad'] = 0x0
|
|
|
|
smb.addCommand(treeConnect)
|
|
|
|
# filename = "\PIPE\epmapper"
|
|
|
|
# ntCreate = SMBCommand(SMB.SMB_COM_NT_CREATE_ANDX)
|
|
# ntCreate['Parameters'] = SMBNtCreateAndX_Parameters()
|
|
# ntCreate['Data'] = SMBNtCreateAndX_Data()
|
|
# ntCreate['Parameters']['FileNameLength'] = len(filename)
|
|
# ntCreate['Parameters']['CreateFlags'] = 0
|
|
# ntCreate['Parameters']['AccessMask'] = 0x3
|
|
# ntCreate['Parameters']['CreateOptions'] = 0x0
|
|
# ntCreate['Data']['FileName'] = filename
|
|
|
|
# smb.addCommand(ntCreate)
|
|
self.sendSMB(smb)
|
|
|
|
while 1:
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_TREE_CONNECT_ANDX):
|
|
# XXX Here we are ignoring the rest of the response
|
|
self.tid = smb['Tid']
|
|
return self.tid
|
|
self.tid = smb['Tid']
|
|
return self.tid
|
|
|
|
# backwars compatibility
|
|
connect_tree = tree_connect_andx
|
|
|
|
@staticmethod
|
|
def getDialect():
|
|
return SMB_DIALECT
|
|
|
|
def get_server_name(self):
|
|
#return self._dialects_data['ServerName']
|
|
return self.__server_name
|
|
|
|
def get_client_name(self):
|
|
return self.__client_name
|
|
|
|
def get_session_key(self):
|
|
return self._SigningSessionKey
|
|
|
|
def set_session_key(self, key):
|
|
self._SignatureEnabled = True
|
|
self._SignSequenceNumber = 2
|
|
self._SigningSessionKey = key
|
|
|
|
def get_encryption_key(self):
|
|
if 'Challenge' in self._dialects_data.fields:
|
|
return self._dialects_data['Challenge']
|
|
else:
|
|
return None
|
|
|
|
def get_server_time(self):
|
|
timestamp = self._dialects_parameters['HighDateTime']
|
|
timestamp <<= 32
|
|
timestamp |= self._dialects_parameters['LowDateTime']
|
|
timestamp -= 116444736000000000
|
|
timestamp //= 10000000
|
|
d = datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc)
|
|
return d.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
|
|
|
def disconnect_tree(self, tid):
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
smb.addCommand(SMBCommand(SMB.SMB_COM_TREE_DISCONNECT))
|
|
|
|
self.sendSMB(smb)
|
|
self.recvSMB()
|
|
|
|
def open(self, tid, filename, open_mode, desired_access):
|
|
filename = filename.replace('/', '\\')
|
|
filename = filename.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else filename
|
|
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
openFile = SMBCommand(SMB.SMB_COM_OPEN)
|
|
openFile['Parameters'] = SMBOpen_Parameters()
|
|
openFile['Parameters']['DesiredAccess'] = desired_access
|
|
openFile['Parameters']['OpenMode'] = open_mode
|
|
openFile['Parameters']['SearchAttributes'] = ATTR_READONLY | ATTR_HIDDEN | ATTR_ARCHIVE
|
|
openFile['Data'] = SMBOpen_Data(flags=self.__flags2)
|
|
openFile['Data']['FileName'] = filename
|
|
|
|
smb.addCommand(openFile)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_OPEN):
|
|
# XXX Here we are ignoring the rest of the response
|
|
openFileResponse = SMBCommand(smb['Data'][0])
|
|
openFileParameters = SMBOpenResponse_Parameters(openFileResponse['Parameters'])
|
|
|
|
return (
|
|
openFileParameters['Fid'],
|
|
openFileParameters['FileAttributes'],
|
|
openFileParameters['LastWriten'],
|
|
openFileParameters['FileSize'],
|
|
openFileParameters['GrantedAccess'],
|
|
)
|
|
|
|
def open_andx(self, tid, filename, open_mode, desired_access):
|
|
filename = filename.replace('/', '\\')
|
|
filename = filename.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else filename
|
|
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
openFile = SMBCommand(SMB.SMB_COM_OPEN_ANDX)
|
|
openFile['Parameters'] = SMBOpenAndX_Parameters()
|
|
openFile['Parameters']['DesiredAccess'] = desired_access
|
|
openFile['Parameters']['OpenMode'] = open_mode
|
|
openFile['Parameters']['SearchAttributes'] = ATTR_READONLY | ATTR_HIDDEN | ATTR_ARCHIVE
|
|
openFile['Data'] = SMBOpenAndX_Data(flags=self.__flags2)
|
|
openFile['Data']['FileName'] = filename
|
|
|
|
if self.__flags2 & SMB.FLAGS2_UNICODE:
|
|
openFile['Data']['Pad'] = 0x0
|
|
|
|
smb.addCommand(openFile)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_OPEN_ANDX):
|
|
# XXX Here we are ignoring the rest of the response
|
|
openFileResponse = SMBCommand(smb['Data'][0])
|
|
openFileParameters = SMBOpenAndXResponse_Parameters(openFileResponse['Parameters'])
|
|
|
|
return (
|
|
openFileParameters['Fid'],
|
|
openFileParameters['FileAttributes'],
|
|
openFileParameters['LastWriten'],
|
|
openFileParameters['FileSize'],
|
|
openFileParameters['GrantedAccess'],
|
|
openFileParameters['FileType'],
|
|
openFileParameters['IPCState'],
|
|
openFileParameters['Action'],
|
|
openFileParameters['ServerFid'],
|
|
)
|
|
|
|
def close(self, tid, fid):
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
closeFile = SMBCommand(SMB.SMB_COM_CLOSE)
|
|
closeFile['Parameters'] = SMBClose_Parameters()
|
|
closeFile['Parameters']['FID'] = fid
|
|
smb.addCommand(closeFile)
|
|
|
|
self.sendSMB(smb)
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_CLOSE):
|
|
return 1
|
|
return 0
|
|
|
|
def send_trans(self, tid, setup, name, param, data, noAnswer = 0):
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
transCommand = SMBCommand(SMB.SMB_COM_TRANSACTION)
|
|
transCommand['Parameters'] = SMBTransaction_Parameters()
|
|
transCommand['Data'] = SMBTransaction_Data()
|
|
|
|
transCommand['Parameters']['Setup'] = setup
|
|
transCommand['Parameters']['TotalParameterCount'] = len(param)
|
|
transCommand['Parameters']['TotalDataCount'] = len(data)
|
|
|
|
transCommand['Parameters']['ParameterCount'] = len(param)
|
|
transCommand['Parameters']['ParameterOffset'] = 32+3+28+len(setup)+len(name)
|
|
|
|
transCommand['Parameters']['DataCount'] = len(data)
|
|
transCommand['Parameters']['DataOffset'] = transCommand['Parameters']['ParameterOffset'] + len(param)
|
|
|
|
transCommand['Data']['Name'] = name
|
|
transCommand['Data']['Trans_Parameters'] = param
|
|
transCommand['Data']['Trans_Data'] = data
|
|
|
|
if noAnswer:
|
|
transCommand['Parameters']['Flags'] = TRANS_NO_RESPONSE
|
|
|
|
smb.addCommand(transCommand)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
def send_trans2(self, tid, setup, name, param, data):
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
command = pack('<H', setup)
|
|
|
|
transCommand = SMBCommand(SMB.SMB_COM_TRANSACTION2)
|
|
transCommand['Parameters'] = SMBTransaction2_Parameters()
|
|
transCommand['Parameters']['MaxDataCount'] = self._dialects_parameters['MaxBufferSize']
|
|
transCommand['Data'] = SMBTransaction2_Data()
|
|
|
|
transCommand['Parameters']['Setup'] = command
|
|
transCommand['Parameters']['TotalParameterCount'] = len(param)
|
|
transCommand['Parameters']['TotalDataCount'] = len(data)
|
|
|
|
if len(param) > 0:
|
|
padLen = (4 - (32+2+28 + len(command)) % 4 ) % 4
|
|
padBytes = '\xFF' * padLen
|
|
transCommand['Data']['Pad1'] = padBytes
|
|
else:
|
|
transCommand['Data']['Pad1'] = ''
|
|
padLen = 0
|
|
|
|
transCommand['Parameters']['ParameterCount'] = len(param)
|
|
transCommand['Parameters']['ParameterOffset'] = 32+2+28+len(command)+len(name) + padLen
|
|
|
|
if len(data) > 0:
|
|
pad2Len = (4 - (32+2+28 + len(command) + padLen + len(param)) % 4) % 4
|
|
transCommand['Data']['Pad2'] = '\xFF' * pad2Len
|
|
else:
|
|
transCommand['Data']['Pad2'] = ''
|
|
pad2Len = 0
|
|
|
|
transCommand['Parameters']['DataCount'] = len(data)
|
|
transCommand['Parameters']['DataOffset'] = transCommand['Parameters']['ParameterOffset'] + len(param) + pad2Len
|
|
|
|
transCommand['Data']['Name'] = name
|
|
transCommand['Data']['Trans_Parameters'] = param
|
|
transCommand['Data']['Trans_Data'] = data
|
|
smb.addCommand(transCommand)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
def query_file_info(self, tid, fid, fileInfoClass = SMB_QUERY_FILE_STANDARD_INFO):
|
|
self.send_trans2(tid, SMB.TRANS2_QUERY_FILE_INFORMATION, '\x00', pack('<HH', fid, fileInfoClass), '')
|
|
|
|
resp = self.recvSMB()
|
|
if resp.isValidAnswer(SMB.SMB_COM_TRANSACTION2):
|
|
trans2Response = SMBCommand(resp['Data'][0])
|
|
trans2Parameters = SMBTransaction2Response_Parameters(trans2Response['Parameters'])
|
|
# Remove Potential Prefix Padding
|
|
return trans2Response['Data'][-trans2Parameters['TotalDataCount']:]
|
|
|
|
def set_file_info(self, tid, fid, fileInfoClass, file_info_data):
|
|
SMBTrans2SetFileInfo_Params = SMBSetFileInformation_Parameters()
|
|
SMBTrans2SetFileInfo_Params["FID"] = fid
|
|
SMBTrans2SetFileInfo_Params["InformationLevel"] = fileInfoClass
|
|
SMBTrans2SetFileInfo_Params["Reserved"] = 0
|
|
|
|
self.send_trans2(tid, SMB.TRANS2_SET_FILE_INFORMATION, '\x00', SMBTrans2SetFileInfo_Params.getData(), file_info_data.getData())
|
|
|
|
resp = self.recvSMB()
|
|
if resp.isValidAnswer(SMB.SMB_COM_TRANSACTION2):
|
|
trans2Response = SMBCommand(resp['Data'][0])
|
|
trans2Parameters = SMBTransaction2Response_Parameters(trans2Response['Parameters'])
|
|
# Remove Potential Prefix Padding
|
|
return trans2Response['Data'][-trans2Parameters['TotalDataCount']:]
|
|
|
|
def __nonraw_retr_file(self, tid, fid, offset, datasize, callback):
|
|
if (self._dialects_parameters['Capabilities'] & SMB.CAP_LARGE_READX) and self._SignatureEnabled is False:
|
|
max_buf_size = 65000
|
|
else:
|
|
max_buf_size = self._dialects_parameters['MaxBufferSize'] & ~0x3ff # Read in multiple KB blocks
|
|
|
|
read_offset = offset
|
|
while read_offset < datasize:
|
|
data = self.read_andx(tid, fid, read_offset, max_buf_size)
|
|
|
|
callback(data)
|
|
read_offset += len(data)
|
|
|
|
def __nonraw_stor_file(self, tid, fid, offset, datasize, callback):
|
|
if (self._dialects_parameters['Capabilities'] & SMB.CAP_LARGE_WRITEX) and self._SignatureEnabled is False:
|
|
max_buf_size = 65000
|
|
else:
|
|
max_buf_size = self._dialects_parameters['MaxBufferSize'] & ~0x3ff # Write in multiple KB blocks
|
|
|
|
write_offset = offset
|
|
while 1:
|
|
data = callback(max_buf_size)
|
|
if not data:
|
|
break
|
|
|
|
smb = self.write_andx(tid,fid,data, write_offset)
|
|
writeResponse = SMBCommand(smb['Data'][0])
|
|
writeResponseParameters = SMBWriteAndXResponse_Parameters(writeResponse['Parameters'])
|
|
write_offset += writeResponseParameters['Count']
|
|
|
|
def get_server_domain(self):
|
|
return self.__server_domain
|
|
|
|
def get_server_dns_domain_name(self):
|
|
return self.__server_dns_domain_name
|
|
|
|
def get_server_dns_host_name(self):
|
|
return self.__server_dns_host_name
|
|
|
|
def get_server_os(self):
|
|
return self.__server_os
|
|
|
|
def get_server_os_major(self):
|
|
return self.__server_os_major
|
|
|
|
def get_server_os_minor(self):
|
|
return self.__server_os_minor
|
|
|
|
def get_server_os_build(self):
|
|
return self.__server_os_build
|
|
|
|
def set_server_os(self, os):
|
|
self.__server_os = os
|
|
|
|
def get_server_lanman(self):
|
|
return self.__server_lanman
|
|
|
|
def is_login_required(self):
|
|
# Login is required if share mode is user.
|
|
# Otherwise only public services or services in share mode
|
|
# are allowed.
|
|
return (self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SHARE_MASK) == SMB.SECURITY_SHARE_USER
|
|
|
|
def is_signing_required(self):
|
|
return self._SignatureRequired
|
|
|
|
def get_ntlmv1_response(self, key):
|
|
challenge = self._dialects_data['Challenge']
|
|
return ntlm.get_ntlmv1_response(key, challenge)
|
|
|
|
def perform_hostname_validation(self):
|
|
if self.__server_name == '':
|
|
if not self._validation_allow_absent:
|
|
raise self.HostnameValidationException('Hostname was not supplied by target host and absent validation is disallowed')
|
|
return
|
|
if self.__server_name.lower() != self._accepted_hostname.lower() and self.__server_dns_host_name.lower() != self._accepted_hostname.lower():
|
|
raise self.HostnameValidationException('Supplied hostname %s does not match reported hostnames %s or %s' %
|
|
(self._accepted_hostname.lower(), self.__server_name.lower(), self.__server_dns_host_name.lower()))
|
|
|
|
|
|
def kerberos_login(self, user, password, domain = '', lmhash = '', nthash = '', aesKey = '', kdcHost = '', TGT=None, TGS=None):
|
|
# Importing down here so pyasn1 is not required if kerberos is not used.
|
|
from impacket.krb5.asn1 import AP_REQ, Authenticator, TGS_REP, seq_set
|
|
from impacket.krb5.kerberosv5 import getKerberosTGT, getKerberosTGS
|
|
from impacket.krb5 import constants
|
|
from impacket.krb5.types import Principal, KerberosTime, Ticket
|
|
from pyasn1.codec.der import decoder, encoder
|
|
import datetime
|
|
|
|
# login feature does not support unicode
|
|
# disable it if enabled
|
|
flags2 = self.__flags2
|
|
if flags2 & SMB.FLAGS2_UNICODE:
|
|
self.__flags2 = flags2 & (flags2 ^ SMB.FLAGS2_UNICODE)
|
|
|
|
# If TGT or TGS are specified, they are in the form of:
|
|
# TGS['KDC_REP'] = the response from the server
|
|
# TGS['cipher'] = the cipher used
|
|
# TGS['sessionKey'] = the sessionKey
|
|
# If we have hashes, normalize them
|
|
if lmhash != '' or nthash != '':
|
|
if len(lmhash) % 2:
|
|
lmhash = '0%s' % lmhash
|
|
if len(nthash) % 2:
|
|
nthash = '0%s' % nthash
|
|
try: # just in case they were converted already
|
|
lmhash = a2b_hex(lmhash)
|
|
nthash = a2b_hex(nthash)
|
|
except:
|
|
pass
|
|
|
|
self.__userName = user
|
|
self.__password = password
|
|
self.__domain = domain
|
|
self.__lmhash = lmhash
|
|
self.__nthash = nthash
|
|
self.__aesKey = aesKey
|
|
self.__kdc = kdcHost
|
|
self.__TGT = TGT
|
|
self.__TGS = TGS
|
|
self._doKerberos= True
|
|
|
|
# First of all, we need to get a TGT for the user
|
|
userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
|
|
if TGT is None:
|
|
if TGS is None:
|
|
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost)
|
|
else:
|
|
tgt = TGT['KDC_REP']
|
|
cipher = TGT['cipher']
|
|
sessionKey = TGT['sessionKey']
|
|
|
|
# Now that we have the TGT, we should ask for a TGS for cifs
|
|
|
|
if TGS is None:
|
|
serverName = Principal('cifs/%s' % self.__remote_name, type=constants.PrincipalNameType.NT_SRV_INST.value)
|
|
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey)
|
|
else:
|
|
tgs = TGS['KDC_REP']
|
|
cipher = TGS['cipher']
|
|
sessionKey = TGS['sessionKey']
|
|
|
|
smb = NewSMBPacket()
|
|
|
|
# Are we required to sign SMB? If so we do it, if not we skip it
|
|
if self._SignatureRequired:
|
|
smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE
|
|
|
|
|
|
sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
|
|
sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
|
|
sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()
|
|
|
|
sessionSetup['Parameters']['MaxBufferSize'] = 61440
|
|
sessionSetup['Parameters']['MaxMpxCount'] = 2
|
|
sessionSetup['Parameters']['VcNumber'] = 1
|
|
sessionSetup['Parameters']['SessionKey'] = 0
|
|
sessionSetup['Parameters']['Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE | SMB.CAP_LARGE_READX | SMB.CAP_LARGE_WRITEX
|
|
|
|
|
|
# Let's build a NegTokenInit with the NTLMSSP
|
|
# TODO: In the future we should be able to choose different providers
|
|
|
|
blob = SPNEGO_NegTokenInit()
|
|
|
|
# Kerberos v5 mech
|
|
blob['MechTypes'] = [TypesMech['MS KRB5 - Microsoft Kerberos 5']]
|
|
|
|
# Let's extract the ticket from the TGS
|
|
tgs = decoder.decode(tgs, asn1Spec = TGS_REP())[0]
|
|
ticket = Ticket()
|
|
ticket.from_asn1(tgs['ticket'])
|
|
|
|
# Now let's build the AP_REQ
|
|
apReq = AP_REQ()
|
|
apReq['pvno'] = 5
|
|
apReq['msg-type'] = int(constants.ApplicationTagNumbers.AP_REQ.value)
|
|
|
|
opts = list()
|
|
apReq['ap-options'] = constants.encodeFlags(opts)
|
|
seq_set(apReq,'ticket', ticket.to_asn1)
|
|
|
|
authenticator = Authenticator()
|
|
authenticator['authenticator-vno'] = 5
|
|
authenticator['crealm'] = domain
|
|
seq_set(authenticator, 'cname', userName.components_to_asn1)
|
|
now = datetime.datetime.now(datetime.timezone.utc)
|
|
|
|
authenticator['cusec'] = now.microsecond
|
|
authenticator['ctime'] = KerberosTime.to_asn1(now)
|
|
|
|
encodedAuthenticator = encoder.encode(authenticator)
|
|
|
|
# Key Usage 11
|
|
# AP-REQ Authenticator (includes application authenticator
|
|
# subkey), encrypted with the application session key
|
|
# (Section 5.5.1)
|
|
encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None)
|
|
|
|
apReq['authenticator'] = noValue
|
|
apReq['authenticator']['etype'] = cipher.enctype
|
|
apReq['authenticator']['cipher'] = encryptedEncodedAuthenticator
|
|
|
|
blob['MechToken'] = pack('B', ASN1_AID) + asn1encode(pack('B', ASN1_OID) + asn1encode(
|
|
TypesMech['KRB5 - Kerberos 5']) + KRB5_AP_REQ + encoder.encode(apReq))
|
|
|
|
sessionSetup['Parameters']['SecurityBlobLength'] = len(blob)
|
|
sessionSetup['Parameters'].getData()
|
|
sessionSetup['Data']['SecurityBlob'] = blob.getData()
|
|
|
|
# Fake Data here, don't want to get us fingerprinted
|
|
sessionSetup['Data']['NativeOS'] = 'Unix'
|
|
sessionSetup['Data']['NativeLanMan'] = 'Samba'
|
|
|
|
smb.addCommand(sessionSetup)
|
|
self.sendSMB(smb)
|
|
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX):
|
|
# We will need to use this uid field for all future requests/responses
|
|
self._uid = smb['Uid']
|
|
|
|
# Now we have to extract the blob to continue the auth process
|
|
sessionResponse = SMBCommand(smb['Data'][0])
|
|
sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(sessionResponse['Parameters'])
|
|
sessionData = SMBSessionSetupAndX_Extended_Response_Data(flags = smb['Flags2'])
|
|
sessionData['SecurityBlobLength'] = sessionParameters['SecurityBlobLength']
|
|
sessionData.fromString(sessionResponse['Data'])
|
|
|
|
self._action = sessionParameters['Action']
|
|
# If smb sign required, let's enable it for the rest of the connection
|
|
if self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SIGNATURES_REQUIRED:
|
|
self._SigningSessionKey = sessionKey.contents
|
|
self._SignSequenceNumber = 2
|
|
self._SignatureEnabled = True
|
|
|
|
# restore unicode flag if needed
|
|
if flags2 & SMB.FLAGS2_UNICODE:
|
|
self.__flags2 |= SMB.FLAGS2_UNICODE
|
|
|
|
return 1
|
|
else:
|
|
raise Exception('Error: Could not login successfully')
|
|
|
|
def login_extended(self, user, password, domain = '', lmhash = '', nthash = '', use_ntlmv2 = True ):
|
|
|
|
# login feature does not support unicode
|
|
# disable it if enabled
|
|
flags2 = self.__flags2
|
|
if flags2 & SMB.FLAGS2_UNICODE:
|
|
self.__flags2 = flags2 & (flags2 ^ SMB.FLAGS2_UNICODE)
|
|
|
|
# Once everything's working we should join login methods into a single one
|
|
smb = NewSMBPacket()
|
|
# Are we required to sign SMB? If so we do it, if not we skip it
|
|
if self._SignatureRequired:
|
|
smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE
|
|
|
|
sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
|
|
sessionSetup['Parameters'] = SMBSessionSetupAndX_Extended_Parameters()
|
|
sessionSetup['Data'] = SMBSessionSetupAndX_Extended_Data()
|
|
|
|
sessionSetup['Parameters']['MaxBufferSize'] = 61440
|
|
sessionSetup['Parameters']['MaxMpxCount'] = 2
|
|
sessionSetup['Parameters']['VcNumber'] = 1
|
|
sessionSetup['Parameters']['SessionKey'] = 0
|
|
sessionSetup['Parameters']['Capabilities'] = SMB.CAP_EXTENDED_SECURITY | SMB.CAP_USE_NT_ERRORS | SMB.CAP_UNICODE | SMB.CAP_LARGE_READX | SMB.CAP_LARGE_WRITEX
|
|
|
|
|
|
# Let's build a NegTokenInit with the NTLMSSP
|
|
# TODO: In the future we should be able to choose different providers
|
|
|
|
blob = SPNEGO_NegTokenInit()
|
|
|
|
# NTLMSSP
|
|
blob['MechTypes'] = [TypesMech['NTLMSSP - Microsoft NTLM Security Support Provider']]
|
|
auth = ntlm.getNTLMSSPType1(self.get_client_name(),domain,self._SignatureRequired, use_ntlmv2 = use_ntlmv2)
|
|
blob['MechToken'] = auth.getData()
|
|
|
|
sessionSetup['Parameters']['SecurityBlobLength'] = len(blob)
|
|
sessionSetup['Parameters'].getData()
|
|
sessionSetup['Data']['SecurityBlob'] = blob.getData()
|
|
|
|
# Fake Data here, don't want to get us fingerprinted
|
|
sessionSetup['Data']['NativeOS'] = 'Unix'
|
|
sessionSetup['Data']['NativeLanMan'] = 'Samba'
|
|
|
|
smb.addCommand(sessionSetup)
|
|
self.sendSMB(smb)
|
|
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX):
|
|
# We will need to use this uid field for all future requests/responses
|
|
self._uid = smb['Uid']
|
|
|
|
# Now we have to extract the blob to continue the auth process
|
|
sessionResponse = SMBCommand(smb['Data'][0])
|
|
sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(sessionResponse['Parameters'])
|
|
sessionData = SMBSessionSetupAndX_Extended_Response_Data(flags = smb['Flags2'])
|
|
sessionData['SecurityBlobLength'] = sessionParameters['SecurityBlobLength']
|
|
sessionData.fromString(sessionResponse['Data'])
|
|
respToken = SPNEGO_NegTokenResp(sessionData['SecurityBlob'])
|
|
|
|
# Let's parse some data and keep it to ourselves in case it is asked
|
|
ntlmChallenge = ntlm.NTLMAuthChallenge(respToken['ResponseToken'])
|
|
if ntlmChallenge['TargetInfoFields_len'] > 0:
|
|
av_pairs = ntlm.AV_PAIRS(ntlmChallenge['TargetInfoFields'][:ntlmChallenge['TargetInfoFields_len']])
|
|
if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None:
|
|
try:
|
|
self.__server_name = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode('utf-16le')
|
|
except UnicodeDecodeError:
|
|
# For some reason, we couldn't decode Unicode here.. silently discard the operation
|
|
pass
|
|
if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None:
|
|
try:
|
|
if self.__server_name != av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le'):
|
|
self.__server_domain = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode('utf-16le')
|
|
except UnicodeDecodeError:
|
|
# For some reason, we couldn't decode Unicode here.. silently discard the operation
|
|
pass
|
|
if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None:
|
|
try:
|
|
self.__server_dns_domain_name = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode('utf-16le')
|
|
except UnicodeDecodeError:
|
|
# For some reason, we couldn't decode Unicode here.. silently discard the operation
|
|
pass
|
|
|
|
if av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] is not None:
|
|
try:
|
|
self.__server_dns_host_name = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME][1].decode('utf-16le')
|
|
except UnicodeDecodeError:
|
|
# For some reason, we couldn't decode Unicode here.. silently discard the operation
|
|
pass
|
|
|
|
if self._strict_hostname_validation:
|
|
self.perform_hostname_validation()
|
|
|
|
# Parse Version to know the target Operating system name. Not provided elsewhere anymore
|
|
if 'Version' in ntlmChallenge.fields:
|
|
version = ntlmChallenge['Version']
|
|
|
|
if len(version) >= 4:
|
|
self.__server_os_major, self.__server_os_minor, self.__server_os_build = unpack('<BBH',version[:4])
|
|
|
|
type3, exportedSessionKey = ntlm.getNTLMSSPType3(auth, respToken['ResponseToken'], user, password, domain, lmhash, nthash, use_ntlmv2 = use_ntlmv2)
|
|
|
|
if exportedSessionKey is not None:
|
|
self._SigningSessionKey = exportedSessionKey
|
|
|
|
smb = NewSMBPacket()
|
|
|
|
# Are we required to sign SMB? If so we do it, if not we skip it
|
|
if self._SignatureRequired:
|
|
smb['Flags2'] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE
|
|
|
|
respToken2 = SPNEGO_NegTokenResp()
|
|
respToken2['ResponseToken'] = type3.getData()
|
|
|
|
# Reusing the previous structure
|
|
sessionSetup['Parameters']['SecurityBlobLength'] = len(respToken2)
|
|
sessionSetup['Data']['SecurityBlob'] = respToken2.getData()
|
|
|
|
# Storing some info for later use
|
|
self.__server_os = sessionData['NativeOS']
|
|
self.__server_lanman = sessionData['NativeLanMan']
|
|
|
|
smb.addCommand(sessionSetup)
|
|
self.sendSMB(smb)
|
|
|
|
smb = self.recvSMB()
|
|
self._uid = 0
|
|
if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX):
|
|
self._uid = smb['Uid']
|
|
sessionResponse = SMBCommand(smb['Data'][0])
|
|
sessionParameters = SMBSessionSetupAndXResponse_Parameters(sessionResponse['Parameters'])
|
|
|
|
self._action = sessionParameters['Action']
|
|
# If smb sign required, let's enable it for the rest of the connection
|
|
if self._dialects_parameters['SecurityMode'] & SMB.SECURITY_SIGNATURES_REQUIRED:
|
|
self._SignSequenceNumber = 2
|
|
self._SignatureEnabled = True
|
|
|
|
# restore unicode flag if needed
|
|
if flags2 & SMB.FLAGS2_UNICODE:
|
|
self.__flags2 |= SMB.FLAGS2_UNICODE
|
|
|
|
return 1
|
|
else:
|
|
raise Exception('Error: Could not login successfully')
|
|
|
|
def getCredentials(self):
|
|
return (
|
|
self.__userName,
|
|
self.__password,
|
|
self.__domain,
|
|
self.__lmhash,
|
|
self.__nthash,
|
|
self.__aesKey,
|
|
self.__TGT,
|
|
self.__TGS)
|
|
|
|
def getIOCapabilities(self):
|
|
res = dict()
|
|
if (self._dialects_parameters['Capabilities'] & SMB.CAP_LARGE_READX) and self._SignatureEnabled is False:
|
|
max_size = 65000
|
|
else:
|
|
max_size = self._dialects_parameters['MaxBufferSize'] # Read in multiple KB blocks
|
|
res['MaxReadSize'] = max_size
|
|
res['MaxWriteSize'] = max_size
|
|
return res
|
|
|
|
def login(self, user, password, domain = '', lmhash = '', nthash = '', ntlm_fallback = True):
|
|
|
|
# If we have hashes, normalize them
|
|
if lmhash != '' or nthash != '':
|
|
if len(lmhash) % 2:
|
|
lmhash = '0%s' % lmhash
|
|
if len(nthash) % 2:
|
|
nthash = '0%s' % nthash
|
|
try: # just in case they were converted already
|
|
lmhash = a2b_hex(lmhash)
|
|
nthash = a2b_hex(nthash)
|
|
except:
|
|
pass
|
|
|
|
self.__userName = user
|
|
self.__password = password
|
|
self.__domain = domain
|
|
self.__lmhash = lmhash
|
|
self.__nthash = nthash
|
|
self.__aesKey = ''
|
|
self.__TGT = None
|
|
self.__TGS = None
|
|
|
|
if self._dialects_parameters['Capabilities'] & SMB.CAP_EXTENDED_SECURITY:
|
|
try:
|
|
self.login_extended(user, password, domain, lmhash, nthash, use_ntlmv2 = True)
|
|
except:
|
|
# If the target OS is Windows 5.0 or Samba, let's try using NTLMv1
|
|
if ntlm_fallback and ((six.ensure_binary(self.get_server_lanman()).find(b'Windows 2000') != -1) or
|
|
(six.ensure_binary(self.get_server_lanman()).find(b'Samba') != -1)):
|
|
self.login_extended(user, password, domain, lmhash, nthash, use_ntlmv2 = False)
|
|
self.__isNTLMv2 = False
|
|
else:
|
|
raise
|
|
elif ntlm_fallback:
|
|
self.login_standard(user, password, domain, lmhash, nthash)
|
|
self.__isNTLMv2 = False
|
|
else:
|
|
raise SessionError('Cannot authenticate against target, enable ntlm_fallback')
|
|
|
|
def login_standard(self, user, password, domain = '', lmhash = '', nthash = ''):
|
|
|
|
# login feature does not support unicode
|
|
# disable it if enabled
|
|
flags2 = self.__flags2
|
|
if flags2 & SMB.FLAGS2_UNICODE:
|
|
self.__flags2 = flags2 & (flags2 ^ SMB.FLAGS2_UNICODE)
|
|
|
|
# Only supports NTLMv1
|
|
# Password is only encrypted if the server passed us an "encryption key" during protocol dialect negotiation
|
|
if self._dialects_parameters['ChallengeLength'] > 0:
|
|
if lmhash != '' or nthash != '':
|
|
pwd_ansi = self.get_ntlmv1_response(lmhash)
|
|
pwd_unicode = self.get_ntlmv1_response(nthash)
|
|
elif password:
|
|
lmhash = ntlm.compute_lmhash(password)
|
|
nthash = ntlm.compute_nthash(password)
|
|
pwd_ansi = self.get_ntlmv1_response(lmhash)
|
|
pwd_unicode = self.get_ntlmv1_response(nthash)
|
|
else: # NULL SESSION
|
|
pwd_ansi = ''
|
|
pwd_unicode = ''
|
|
else:
|
|
pwd_ansi = password
|
|
pwd_unicode = ''
|
|
|
|
smb = NewSMBPacket()
|
|
|
|
sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
|
|
sessionSetup['Parameters'] = SMBSessionSetupAndX_Parameters()
|
|
sessionSetup['Data'] = SMBSessionSetupAndX_Data()
|
|
|
|
sessionSetup['Parameters']['MaxBuffer'] = 61440
|
|
sessionSetup['Parameters']['MaxMpxCount'] = 2
|
|
sessionSetup['Parameters']['VCNumber'] = os.getpid() & 0xFFFF # Value has to be expressed in 2 bytes
|
|
sessionSetup['Parameters']['SessionKey'] = self._dialects_parameters['SessionKey']
|
|
sessionSetup['Parameters']['AnsiPwdLength'] = len(pwd_ansi)
|
|
sessionSetup['Parameters']['UnicodePwdLength'] = len(pwd_unicode)
|
|
sessionSetup['Parameters']['Capabilities'] = SMB.CAP_RAW_MODE | SMB.CAP_USE_NT_ERRORS | SMB.CAP_LARGE_READX | SMB.CAP_LARGE_WRITEX
|
|
|
|
sessionSetup['Data']['AnsiPwd'] = pwd_ansi
|
|
sessionSetup['Data']['UnicodePwd'] = pwd_unicode
|
|
sessionSetup['Data']['Account'] = str(user)
|
|
sessionSetup['Data']['PrimaryDomain'] = str(domain)
|
|
sessionSetup['Data']['NativeOS'] = str(os.name)
|
|
sessionSetup['Data']['NativeLanMan'] = 'pysmb'
|
|
smb.addCommand(sessionSetup)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX):
|
|
# We will need to use this uid field for all future requests/responses
|
|
self._uid = smb['Uid']
|
|
sessionResponse = SMBCommand(smb['Data'][0])
|
|
sessionParameters = SMBSessionSetupAndXResponse_Parameters(sessionResponse['Parameters'])
|
|
sessionData = SMBSessionSetupAndXResponse_Data(flags = smb['Flags2'], data = sessionResponse['Data'])
|
|
|
|
self._action = sessionParameters['Action']
|
|
|
|
# Still gotta figure out how to do this with no EXTENDED_SECURITY
|
|
if sessionParameters['Action'] & SMB_SETUP_USE_LANMAN_KEY == 0:
|
|
self._SigningChallengeResponse = sessionSetup['Data']['UnicodePwd']
|
|
self._SigningSessionKey = nthash
|
|
else:
|
|
self._SigningChallengeResponse = sessionSetup['Data']['AnsiPwd']
|
|
self._SigningSessionKey = lmhash
|
|
|
|
#self._SignSequenceNumber = 1
|
|
#self.checkSignSMB(smb, self._SigningSessionKey ,self._SigningChallengeResponse)
|
|
#self._SignatureEnabled = True
|
|
self.__server_os = sessionData['NativeOS']
|
|
self.__server_lanman = sessionData['NativeLanMan']
|
|
self.__server_domain = sessionData['PrimaryDomain']
|
|
|
|
# restore unicode flag if needed
|
|
if flags2 & SMB.FLAGS2_UNICODE:
|
|
self.__flags2 |= SMB.FLAGS2_UNICODE
|
|
|
|
return 1
|
|
else:
|
|
raise Exception('Error: Could not login successfully')
|
|
|
|
def waitNamedPipe(self, tid, pipe, timeout = 5, noAnswer = 0):
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
transCommand = SMBCommand(SMB.SMB_COM_TRANSACTION)
|
|
transCommand['Parameters'] = SMBTransaction_Parameters()
|
|
transCommand['Data'] = SMBTransaction_Data()
|
|
|
|
setup = '\x53\x00\x00\x00'
|
|
name = '\\PIPE%s\x00' % pipe
|
|
if self.__flags2 & SMB.FLAGS2_UNICODE:
|
|
start_of_name = 32+3+28+len(setup)#32 is smb_header,28 is parameter,3 is wordcount and bytecount
|
|
start_pad = 2-(start_of_name%2)
|
|
name = start_pad*b'\x00' + name.encode('utf-16le')
|
|
end_of_name = start_of_name+len(name)
|
|
pad_len = 4-(end_of_name%4)
|
|
name += pad_len*b'\x00'
|
|
else:
|
|
name = name.encode('utf-8')
|
|
transCommand['Parameters']['Setup'] = setup
|
|
transCommand['Parameters']['TotalParameterCount'] = 0
|
|
transCommand['Parameters']['TotalDataCount'] = 0
|
|
transCommand['Parameters']['MaxParameterCount'] = 0
|
|
transCommand['Parameters']['MaxDataCount'] = 0
|
|
transCommand['Parameters']['Timeout'] = timeout * 1000
|
|
|
|
transCommand['Parameters']['ParameterCount'] = 0
|
|
transCommand['Parameters']['ParameterOffset'] = 32+3+28+len(setup)+len(name)
|
|
|
|
transCommand['Parameters']['DataCount'] = 0
|
|
transCommand['Parameters']['DataOffset'] = 0
|
|
|
|
transCommand['Data']['Name'] = name
|
|
transCommand['Data']['Trans_Parameters'] = ''
|
|
transCommand['Data']['Trans_Data'] = ''
|
|
|
|
if noAnswer:
|
|
transCommand['Parameters']['Flags'] = TRANS_NO_RESPONSE
|
|
|
|
smb.addCommand(transCommand)
|
|
self.sendSMB(smb)
|
|
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_TRANSACTION):
|
|
return 1
|
|
return 0
|
|
|
|
def read(self, tid, fid, offset=0, max_size = None, wait_answer=1):
|
|
if not max_size:
|
|
max_size = self._dialects_parameters['MaxBufferSize'] # Read in multiple KB blocks
|
|
|
|
# max_size is not working, because although it would, the server returns an error (More data avail)
|
|
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
read = SMBCommand(SMB.SMB_COM_READ)
|
|
read['Parameters'] = SMBRead_Parameters()
|
|
read['Parameters']['Fid'] = fid
|
|
read['Parameters']['Offset'] = offset
|
|
read['Parameters']['Count'] = max_size
|
|
smb.addCommand(read)
|
|
|
|
if wait_answer:
|
|
while 1:
|
|
self.sendSMB(smb)
|
|
ans = self.recvSMB()
|
|
|
|
if ans.isValidAnswer(SMB.SMB_COM_READ):
|
|
readResponse = SMBCommand(ans['Data'][0])
|
|
readData = SMBReadResponse_Data(readResponse['Data'])
|
|
|
|
return readData['Data']
|
|
|
|
return None
|
|
|
|
def read_andx(self, tid, fid, offset=0, max_size = None, wait_answer=1, smb_packet=None):
|
|
if not max_size:
|
|
if (self._dialects_parameters['Capabilities'] & SMB.CAP_LARGE_READX) and self._SignatureEnabled is False:
|
|
max_size = 65000
|
|
else:
|
|
max_size = self._dialects_parameters['MaxBufferSize'] # Read in multiple KB blocks
|
|
|
|
# max_size is not working, because although it would, the server returns an error (More data avail)
|
|
|
|
if smb_packet is None:
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
readAndX = SMBCommand(SMB.SMB_COM_READ_ANDX)
|
|
readAndX['Parameters'] = SMBReadAndX_Parameters()
|
|
readAndX['Parameters']['Fid'] = fid
|
|
readAndX['Parameters']['Offset'] = offset
|
|
readAndX['Parameters']['MaxCount'] = max_size
|
|
smb.addCommand(readAndX)
|
|
else:
|
|
smb = smb_packet
|
|
|
|
if wait_answer:
|
|
answer = b''
|
|
while 1:
|
|
self.sendSMB(smb)
|
|
ans = self.recvSMB()
|
|
|
|
if ans.isValidAnswer(SMB.SMB_COM_READ_ANDX):
|
|
# XXX Here we are only using a few fields from the response
|
|
readAndXResponse = SMBCommand(ans['Data'][0])
|
|
readAndXParameters = SMBReadAndXResponse_Parameters(readAndXResponse['Parameters'])
|
|
|
|
offset = readAndXParameters['DataOffset']
|
|
count = readAndXParameters['DataCount']+0x10000*readAndXParameters['DataCount_Hi']
|
|
answer += ans.getData()[offset:offset+count]
|
|
if not ans.isMoreData():
|
|
return answer
|
|
max_size = min(max_size, readAndXParameters['Remaining'])
|
|
readAndX['Parameters']['Offset'] += count # XXX Offset is not important (apparently)
|
|
else:
|
|
self.sendSMB(smb)
|
|
ans = self.recvSMB()
|
|
|
|
try:
|
|
if ans.isValidAnswer(SMB.SMB_COM_READ_ANDX):
|
|
return ans
|
|
else:
|
|
return None
|
|
except:
|
|
return ans
|
|
|
|
return None
|
|
|
|
def read_raw(self, tid, fid, offset=0, max_size = None, wait_answer=1):
|
|
if not max_size:
|
|
max_size = self._dialects_parameters['MaxBufferSize'] # Read in multiple KB blocks
|
|
|
|
# max_size is not working, because although it would, the server returns an error (More data avail)
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
readRaw = SMBCommand(SMB.SMB_COM_READ_RAW)
|
|
readRaw['Parameters'] = SMBReadRaw_Parameters()
|
|
readRaw['Parameters']['Fid'] = fid
|
|
readRaw['Parameters']['Offset'] = offset
|
|
readRaw['Parameters']['MaxCount'] = max_size
|
|
smb.addCommand(readRaw)
|
|
|
|
self.sendSMB(smb)
|
|
if wait_answer:
|
|
data = self._sess.recv_packet(self.__timeout).get_trailer()
|
|
if not data:
|
|
# If there is no data it means there was an error
|
|
data = self.read_andx(tid, fid, offset, max_size)
|
|
return data
|
|
|
|
return None
|
|
|
|
def write(self,tid,fid,data, offset = 0, wait_answer=1):
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
write = SMBCommand(SMB.SMB_COM_WRITE)
|
|
write['Parameters'] = SMBWrite_Parameters()
|
|
write['Data'] = SMBWrite_Data()
|
|
write['Parameters']['Fid'] = fid
|
|
write['Parameters']['Count'] = len(data)
|
|
write['Parameters']['Offset'] = offset
|
|
write['Parameters']['Remaining'] = len(data)
|
|
write['Data']['Data'] = data
|
|
smb.addCommand(write)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
if wait_answer:
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_WRITE):
|
|
return smb
|
|
return None
|
|
|
|
def write_andx(self,tid,fid,data, offset = 0, wait_answer=1, write_pipe_mode = False, smb_packet=None):
|
|
if smb_packet is None:
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
writeAndX = SMBCommand(SMB.SMB_COM_WRITE_ANDX)
|
|
smb.addCommand(writeAndX)
|
|
|
|
writeAndX['Parameters'] = SMBWriteAndX_Parameters()
|
|
writeAndX['Parameters']['Fid'] = fid
|
|
writeAndX['Parameters']['Offset'] = offset
|
|
writeAndX['Parameters']['WriteMode'] = 8
|
|
writeAndX['Parameters']['Remaining'] = len(data)
|
|
writeAndX['Parameters']['DataLength'] = len(data)
|
|
writeAndX['Parameters']['DataOffset'] = len(smb) # this length already includes the parameter
|
|
writeAndX['Data'] = data
|
|
|
|
if write_pipe_mode is True:
|
|
# First of all we gotta know what the MaxBuffSize is
|
|
maxBuffSize = self._dialects_parameters['MaxBufferSize']
|
|
if len(data) > maxBuffSize:
|
|
chunks_size = maxBuffSize - 60
|
|
writeAndX['Parameters']['WriteMode'] = 0x0c
|
|
sendData = b'\xff\xff' + data
|
|
totalLen = len(sendData)
|
|
writeAndX['Parameters']['DataLength'] = chunks_size
|
|
writeAndX['Parameters']['Remaining'] = totalLen-2
|
|
writeAndX['Data'] = sendData[:chunks_size]
|
|
|
|
self.sendSMB(smb)
|
|
if wait_answer:
|
|
smbResp = self.recvSMB()
|
|
smbResp.isValidAnswer(SMB.SMB_COM_WRITE_ANDX)
|
|
|
|
alreadySent = chunks_size
|
|
sendData = sendData[chunks_size:]
|
|
|
|
while alreadySent < totalLen:
|
|
writeAndX['Parameters']['WriteMode'] = 0x04
|
|
writeAndX['Parameters']['DataLength'] = len(sendData[:chunks_size])
|
|
writeAndX['Data'] = sendData[:chunks_size]
|
|
self.sendSMB(smb)
|
|
if wait_answer:
|
|
smbResp = self.recvSMB()
|
|
smbResp.isValidAnswer(SMB.SMB_COM_WRITE_ANDX)
|
|
alreadySent += writeAndX['Parameters']['DataLength']
|
|
sendData = sendData[chunks_size:]
|
|
|
|
return smbResp
|
|
|
|
else:
|
|
smb = smb_packet
|
|
|
|
self.sendSMB(smb)
|
|
|
|
if wait_answer:
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_WRITE_ANDX):
|
|
return smb
|
|
return None
|
|
|
|
def write_raw(self,tid,fid,data, offset = 0, wait_answer=1):
|
|
LOG.warning("[MS-CIFS] This command was introduced in the CorePlus dialect, but is often listed as part of the LAN Manager 1.0 dialect.This command has been deprecated.Clients SHOULD use SMB_COM_WRITE_ANDX")
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
|
|
writeRaw = SMBCommand(SMB.SMB_COM_WRITE_RAW)
|
|
writeRaw['Parameters'] = SMBWriteRaw_Parameters()
|
|
writeRaw['Parameters']['Fid'] = fid
|
|
writeRaw['Parameters']['Offset'] = offset
|
|
writeRaw['Parameters']['Count'] = len(data)
|
|
writeRaw['Parameters']['DataLength'] = 0
|
|
writeRaw['Parameters']['DataOffset'] = 0
|
|
smb.addCommand(writeRaw)
|
|
|
|
self.sendSMB(smb)
|
|
self._sess.send_packet(data)
|
|
|
|
if wait_answer:
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_WRITE_RAW):
|
|
return smb
|
|
return None
|
|
|
|
def TransactNamedPipe(self, tid, fid, data = '', noAnswer = 0, waitAnswer = 1, offset = 0):
|
|
self.send_trans(tid,pack('<HH', 0x26, fid),'\\PIPE\\\x00','',data, noAnswer = noAnswer)
|
|
|
|
if noAnswer or not waitAnswer:
|
|
return
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_TRANSACTION):
|
|
transResponse = SMBCommand(smb['Data'][0])
|
|
transParameters = SMBTransactionResponse_Parameters(transResponse['Parameters'])
|
|
return transResponse['Data'][-transParameters['TotalDataCount']:] # Remove Potential Prefix Padding
|
|
return None
|
|
|
|
def TransactNamedPipeRecv(self):
|
|
s = self.recvSMB()
|
|
if s.isValidAnswer(SMB.SMB_COM_TRANSACTION):
|
|
transResponse = SMBCommand(s['Data'][0])
|
|
transParameters = SMBTransactionResponse_Parameters(transResponse['Parameters'])
|
|
return transResponse['Data'][-transParameters['TotalDataCount']:] # Remove Potential Prefix Padding
|
|
return None
|
|
|
|
def nt_create_andx(self, tid, filename, smb_packet = None, cmd = None, shareAccessMode = FILE_SHARE_READ | FILE_SHARE_WRITE, disposition = FILE_OPEN, accessMask = READ_CONTROL | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_WRITE_EA | FILE_READ_EA | FILE_APPEND_DATA | FILE_WRITE_DATA):
|
|
filename = filename.replace('/', '\\')
|
|
filename = filename.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else filename
|
|
|
|
if smb_packet is None:
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
else:
|
|
smb = smb_packet
|
|
|
|
if cmd is None:
|
|
ntCreate = SMBCommand(SMB.SMB_COM_NT_CREATE_ANDX)
|
|
ntCreate['Parameters'] = SMBNtCreateAndX_Parameters()
|
|
ntCreate['Data'] = SMBNtCreateAndX_Data(flags=self.__flags2)
|
|
ntCreate['Parameters']['FileNameLength'] = len(filename)
|
|
ntCreate['Parameters']['CreateFlags'] = 0x16
|
|
ntCreate['Parameters']['AccessMask'] = accessMask
|
|
ntCreate['Parameters']['CreateOptions'] = 0x40
|
|
ntCreate['Parameters']['ShareAccess'] = shareAccessMode
|
|
ntCreate['Parameters']['Disposition'] = disposition
|
|
ntCreate['Data']['FileName'] = filename
|
|
|
|
if self.__flags2 & SMB.FLAGS2_UNICODE:
|
|
ntCreate['Data']['Pad'] = 0x0
|
|
else:
|
|
ntCreate = cmd
|
|
|
|
smb.addCommand(ntCreate)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
while 1:
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_NT_CREATE_ANDX):
|
|
# XXX Here we are ignoring the rest of the response
|
|
ntCreateResponse = SMBCommand(smb['Data'][0])
|
|
ntCreateParameters = SMBNtCreateAndXResponse_Parameters(ntCreateResponse['Parameters'])
|
|
|
|
self.fid = ntCreateParameters['Fid']
|
|
return ntCreateParameters['Fid']
|
|
|
|
def logoff(self):
|
|
smb = NewSMBPacket()
|
|
|
|
logOff = SMBCommand(SMB.SMB_COM_LOGOFF_ANDX)
|
|
logOff['Parameters'] = SMBLogOffAndX()
|
|
smb.addCommand(logOff)
|
|
|
|
self.sendSMB(smb)
|
|
self.recvSMB()
|
|
# Let's clear some fields so you can login again under the same session
|
|
self._uid = 0
|
|
|
|
def list_path(self, service, path = '*', password = None):
|
|
path = path.replace('/', '\\')
|
|
path = path.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else path
|
|
|
|
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
|
|
try:
|
|
findFirstParameter = SMBFindFirst2_Parameters(self.__flags2)
|
|
findFirstParameter['SearchAttributes'] = SMB_FILE_ATTRIBUTE_DIRECTORY | SMB_FILE_ATTRIBUTE_HIDDEN | \
|
|
SMB_FILE_ATTRIBUTE_SYSTEM | SMB_FILE_ATTRIBUTE_READONLY | \
|
|
SMB_FILE_ATTRIBUTE_ARCHIVE
|
|
findFirstParameter['SearchCount'] = 512
|
|
findFirstParameter['Flags'] = SMB_FIND_RETURN_RESUME_KEYS | SMB_FIND_CLOSE_AT_EOS
|
|
findFirstParameter['InformationLevel'] = SMB_FIND_FILE_BOTH_DIRECTORY_INFO
|
|
findFirstParameter['SearchStorageType'] = 0
|
|
if self.__flags2 & SMB.FLAGS2_UNICODE:
|
|
findFirstParameter['FileName'] = path + b'\x00\x00'
|
|
else:
|
|
findFirstParameter['FileName'] = path + '\x00'
|
|
self.send_trans2(tid, SMB.TRANS2_FIND_FIRST2, '\x00', findFirstParameter, '')
|
|
files = [ ]
|
|
|
|
totalDataCount = 1
|
|
findData = b''
|
|
findFirst2ParameterBlock = b''
|
|
while len(findData) < totalDataCount:
|
|
resp = self.recvSMB()
|
|
|
|
if resp.isValidAnswer(SMB.SMB_COM_TRANSACTION2):
|
|
trans2Response = SMBCommand(resp['Data'][0])
|
|
trans2Parameters = SMBTransaction2Response_Parameters(trans2Response['Parameters'])
|
|
totalDataCount = trans2Parameters['TotalDataCount']
|
|
findFirst2ParameterBlock += trans2Response['Data'][trans2Parameters['ParameterOffset']-55:][:trans2Parameters['ParameterCount']]
|
|
findData += trans2Response['Data'][trans2Parameters['DataOffset']-55:]
|
|
|
|
findParameterBlock = SMBFindFirst2Response_Parameters(findFirst2ParameterBlock)
|
|
# Save the SID for resume operations
|
|
sid = findParameterBlock['SID']
|
|
|
|
while findParameterBlock['SearchCount'] > 0:
|
|
record = SMBFindFileBothDirectoryInfo(data = findData)
|
|
|
|
shortname = record['ShortName'].decode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else \
|
|
record['ShortName'].decode('cp437')
|
|
filename = record['FileName'].decode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else \
|
|
record['FileName'].decode('cp437')
|
|
|
|
fileRecord = SharedFile(record['CreationTime'], record['LastAccessTime'], record['LastWriteTime'], record['LastChangeTime'],
|
|
record['EndOfFile'], record['AllocationSize'], record['ExtFileAttributes'],
|
|
shortname, filename)
|
|
files.append(fileRecord)
|
|
if record['NextEntryOffset'] > 0 and len(findData[record['NextEntryOffset']:]) > 0:
|
|
findData = findData[record['NextEntryOffset']:]
|
|
else:
|
|
# More data to search?
|
|
if findParameterBlock['EndOfSearch'] == 0:
|
|
resume_filename = record['FileName']
|
|
findNextParameter = SMBFindNext2_Parameters()
|
|
findNextParameter['SID'] = sid
|
|
findNextParameter['SearchCount'] = 1024
|
|
findNextParameter['InformationLevel'] = SMB_FIND_FILE_BOTH_DIRECTORY_INFO
|
|
findNextParameter['ResumeKey'] = record["FileIndex"]
|
|
findNextParameter['Flags'] = SMB_FIND_RETURN_RESUME_KEYS | SMB_FIND_CLOSE_AT_EOS
|
|
if self.__flags2 & SMB.FLAGS2_UNICODE:
|
|
findNextParameter['FileName'] = resume_filename + b'\x00\x00'
|
|
else:
|
|
findNextParameter['FileName'] = resume_filename + b'\x00'
|
|
self.send_trans2(tid, SMB.TRANS2_FIND_NEXT2, '\x00', findNextParameter, '')
|
|
findData = b''
|
|
findNext2ParameterBlock = b''
|
|
totalDataCount = 1
|
|
while len(findData) < totalDataCount:
|
|
resp = self.recvSMB()
|
|
|
|
if resp.isValidAnswer(SMB.SMB_COM_TRANSACTION2):
|
|
trans2Response = SMBCommand(resp['Data'][0])
|
|
trans2Parameters = SMBTransaction2Response_Parameters(trans2Response['Parameters'])
|
|
totalDataCount = trans2Parameters['TotalDataCount']
|
|
findNext2ParameterBlock += trans2Response['Data'][trans2Parameters['ParameterOffset']-55:][:trans2Parameters['ParameterCount']]
|
|
findData += trans2Response['Data'][trans2Parameters['DataOffset']-55:]
|
|
findParameterBlock = SMBFindNext2Response_Parameters(findNext2ParameterBlock)
|
|
else:
|
|
break
|
|
finally:
|
|
self.disconnect_tree(tid)
|
|
|
|
return files
|
|
|
|
def retr_file(self, service, filename, callback, mode = FILE_OPEN, offset = 0, password = None, shareAccessMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE):
|
|
filename = filename.replace('/', '\\')
|
|
|
|
fid = -1
|
|
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
|
|
try:
|
|
fid = self.nt_create_andx(tid, filename, shareAccessMode = shareAccessMode, accessMask = READ_CONTROL | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_READ_DATA)
|
|
|
|
res = self.query_file_info(tid, fid)
|
|
datasize = SMBQueryFileStandardInfo(res)['EndOfFile']
|
|
|
|
self.__nonraw_retr_file(tid, fid, offset, datasize, callback)
|
|
finally:
|
|
if fid >= 0:
|
|
self.close(tid, fid)
|
|
self.disconnect_tree(tid)
|
|
|
|
def stor_file(self, service, filename, callback, mode = FILE_OVERWRITE_IF, offset = 0, password = None, shareAccessMode = FILE_SHARE_READ):
|
|
filename = filename.replace('/', '\\')
|
|
|
|
fid = -1
|
|
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
|
|
try:
|
|
fid = self.nt_create_andx(tid, filename, shareAccessMode = shareAccessMode, disposition = mode )
|
|
|
|
self.__nonraw_stor_file(tid, fid, offset, 0, callback)
|
|
finally:
|
|
if fid >= 0:
|
|
self.close(tid, fid)
|
|
self.disconnect_tree(tid)
|
|
|
|
def stor_file_nonraw(self, service, filename, callback, mode = FILE_OVERWRITE_IF, offset = 0, password = None, shareAccessMode = FILE_SHARE_READ):
|
|
filename = filename.replace('/', '\\')
|
|
|
|
fid = -1
|
|
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
|
|
try:
|
|
fid = self.nt_create_andx(tid, filename, shareAccessMode = shareAccessMode, disposition = mode)
|
|
self.__nonraw_stor_file(tid, fid, offset, 0, callback)
|
|
finally:
|
|
if fid >= 0:
|
|
self.close(tid, fid)
|
|
self.disconnect_tree(tid)
|
|
|
|
def check_dir(self, service, path, password = None):
|
|
path = path.replace('/', '\\')
|
|
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
|
|
try:
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
smb['Mid'] = 0
|
|
|
|
cmd = SMBCommand(SMB.SMB_COM_CHECK_DIRECTORY)
|
|
cmd['Parameters'] = ''
|
|
cmd['Data'] = SMBCheckDirectory_Data(flags = self.__flags2)
|
|
cmd['Data']['DirectoryName'] = path.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else path
|
|
smb.addCommand(cmd)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
while 1:
|
|
s = self.recvSMB()
|
|
if s.isValidAnswer(SMB.SMB_COM_CHECK_DIRECTORY):
|
|
return
|
|
finally:
|
|
self.disconnect_tree(tid)
|
|
|
|
def remove(self, service, path, password = None):
|
|
path = path.replace('/', '\\')
|
|
# Perform a list to ensure the path exists
|
|
self.list_path(service, path, password)
|
|
|
|
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
|
|
try:
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
smb['Mid'] = 0
|
|
|
|
cmd = SMBCommand(SMB.SMB_COM_DELETE)
|
|
cmd['Parameters'] = SMBDelete_Parameters()
|
|
cmd['Parameters']['SearchAttributes'] = ATTR_HIDDEN | ATTR_SYSTEM | ATTR_ARCHIVE
|
|
cmd['Data'] = SMBDelete_Data(flags = self.__flags2)
|
|
cmd['Data']['FileName'] = (path + '\x00').encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else (path + '\x00')
|
|
smb.addCommand(cmd)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
while 1:
|
|
s = self.recvSMB()
|
|
if s.isValidAnswer(SMB.SMB_COM_DELETE):
|
|
return
|
|
finally:
|
|
self.disconnect_tree(tid)
|
|
|
|
def rmdir(self, service, path, password = None):
|
|
path = path.replace('/', '\\')
|
|
# Check that the directory exists
|
|
self.check_dir(service, path, password)
|
|
|
|
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
|
|
try:
|
|
path = path.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else path
|
|
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
createDir = SMBCommand(SMB.SMB_COM_DELETE_DIRECTORY)
|
|
createDir['Data'] = SMBDeleteDirectory_Data(flags=self.__flags2)
|
|
createDir['Data']['DirectoryName'] = path
|
|
smb.addCommand(createDir)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
while 1:
|
|
s = self.recvSMB()
|
|
if s.isValidAnswer(SMB.SMB_COM_DELETE_DIRECTORY):
|
|
return
|
|
finally:
|
|
self.disconnect_tree(tid)
|
|
|
|
def mkdir(self, service, path, password = None):
|
|
path = path.replace('/', '\\')
|
|
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
|
|
try:
|
|
path = path.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else path
|
|
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
smb['Mid'] = 0
|
|
|
|
createDir = SMBCommand(SMB.SMB_COM_CREATE_DIRECTORY)
|
|
createDir['Data'] = SMBCreateDirectory_Data(flags=self.__flags2)
|
|
createDir['Data']['DirectoryName'] = path
|
|
smb.addCommand(createDir)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_CREATE_DIRECTORY):
|
|
return 1
|
|
return 0
|
|
finally:
|
|
self.disconnect_tree(tid)
|
|
|
|
def rename(self, service, old_path, new_path, password = None):
|
|
old_path = old_path.replace('/', '\\')
|
|
new_path = new_path.replace('/', '\\')
|
|
tid = self.tree_connect_andx('\\\\' + self.__remote_name + '\\' + service, password)
|
|
try:
|
|
smb = NewSMBPacket()
|
|
smb['Tid'] = tid
|
|
smb['Mid'] = 0
|
|
|
|
renameCmd = SMBCommand(SMB.SMB_COM_RENAME)
|
|
renameCmd['Parameters'] = SMBRename_Parameters()
|
|
renameCmd['Parameters']['SearchAttributes'] = ATTR_SYSTEM | ATTR_HIDDEN | ATTR_DIRECTORY
|
|
renameCmd['Data'] = SMBRename_Data(flags = self.__flags2)
|
|
renameCmd['Data']['OldFileName'] = old_path.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else old_path
|
|
renameCmd['Data']['NewFileName'] = new_path.encode('utf-16le') if self.__flags2 & SMB.FLAGS2_UNICODE else new_path
|
|
smb.addCommand(renameCmd)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
smb = self.recvSMB()
|
|
if smb.isValidAnswer(SMB.SMB_COM_RENAME):
|
|
return 1
|
|
return 0
|
|
finally:
|
|
self.disconnect_tree(tid)
|
|
|
|
def writeFile(self, treeId, fileId, data, offset = 0):
|
|
if (self._dialects_parameters['Capabilities'] & SMB.CAP_LARGE_WRITEX) and self._SignatureEnabled is False:
|
|
max_buf_size = 65000
|
|
else:
|
|
max_buf_size = self._dialects_parameters['MaxBufferSize'] & ~0x3ff # Write in multiple KB blocks
|
|
|
|
write_offset = offset
|
|
while 1:
|
|
if len(data) == 0:
|
|
break
|
|
writeData = data[:max_buf_size]
|
|
data = data[max_buf_size:]
|
|
|
|
smb = self.write_andx(treeId,fileId,writeData, write_offset)
|
|
writeResponse = SMBCommand(smb['Data'][0])
|
|
writeResponseParameters = SMBWriteAndXResponse_Parameters(writeResponse['Parameters'])
|
|
write_offset += writeResponseParameters['Count']
|
|
|
|
return write_offset
|
|
|
|
def get_socket(self):
|
|
return self._sess.get_socket()
|
|
|
|
def send_nt_trans(self, tid, subcommand, max_param_count, setup='', param='', data=''):
|
|
"""
|
|
[MS-CIFS]: 2.2.4.62.1 SMB_COM_NT_TRANSACT request.
|
|
:param tid:
|
|
:param subcommand: The transaction subcommand code
|
|
:param max_param_count: This field MUST be set as specified in the subsections of Transaction subcommands.
|
|
:param setup: Transaction context to the server, depends on transaction subcommand.
|
|
:param param: Subcommand parameter bytes if any, depends on transaction subcommand.
|
|
:param data: Subcommand data bytes if any, depends on transaction subcommand.
|
|
:return: Buffer relative to requested subcommand.
|
|
"""
|
|
smb_packet = NewSMBPacket()
|
|
smb_packet['Tid'] = tid
|
|
# setup depends on NT_TRANSACT subcommands so it may be 0.
|
|
setup_bytes = pack('<H', setup) if setup != '' else ''
|
|
|
|
transCommand = SMBCommand(SMB.SMB_COM_NT_TRANSACT)
|
|
transCommand['Parameters'] = SMBNTTransaction_Parameters()
|
|
transCommand['Parameters']['MaxDataCount'] = self._dialects_parameters['MaxBufferSize']
|
|
transCommand['Parameters']['Setup'] = setup_bytes
|
|
transCommand['Parameters']['Function'] = subcommand
|
|
transCommand['Parameters']['TotalParameterCount'] = len(param)
|
|
transCommand['Parameters']['TotalDataCount'] = len(data)
|
|
transCommand['Parameters']['MaxParameterCount'] = max_param_count
|
|
transCommand['Parameters']['MaxSetupCount'] = 0
|
|
|
|
transCommand['Data'] = SMBNTTransaction_Data()
|
|
|
|
# SMB header size + SMB_COM_NT_TRANSACT parameters size + length of setup bytes.
|
|
offset = 32 + 3 + 38 + len(setup_bytes)
|
|
transCommand['Data']['Pad1'] = ''
|
|
if offset % 4 != 0:
|
|
transCommand['Data']['Pad1'] = '\0' * (4 - offset % 4)
|
|
offset += (4 - offset % 4) # pad1 length
|
|
|
|
if len(param) > 0:
|
|
transCommand['Parameters']['ParameterOffset'] = offset
|
|
else:
|
|
transCommand['Parameters']['ParameterOffset'] = 0
|
|
|
|
offset += len(param)
|
|
transCommand['Data']['Pad2'] = ''
|
|
if offset % 4 != 0:
|
|
transCommand['Data']['Pad2'] = '\0' * (4 - offset % 4)
|
|
offset += (4 - offset % 4)
|
|
|
|
if len(data) > 0:
|
|
transCommand['Parameters']['DataOffset'] = offset
|
|
else:
|
|
transCommand['Parameters']['DataOffset'] = 0
|
|
|
|
transCommand['Parameters']['DataCount'] = len(data)
|
|
transCommand['Parameters']['ParameterCount'] = len(param)
|
|
transCommand['Data']['NT_Trans_Parameters'] = param
|
|
transCommand['Data']['NT_Trans_Data'] = data
|
|
smb_packet.addCommand(transCommand)
|
|
|
|
self.sendSMB(smb_packet)
|
|
|
|
def query_sec_info(self, tid, fid, additional_information=7):
|
|
"""
|
|
[MS-CIFS]: 2.2.7.6.1
|
|
NT_TRANSACT_QUERY_SECURITY_DESC 0x0006
|
|
:param tid: valid tree id.
|
|
:param fid: valid file handle.
|
|
:param additional_information: SecurityInfoFields. default = owner + group + dacl ie. 7
|
|
:return: security descriptor buffer
|
|
"""
|
|
self.send_nt_trans(tid, subcommand=0x0006, max_param_count=4,
|
|
param=pack('<HHL', fid, 0x0000, additional_information))
|
|
resp = self.recvSMB()
|
|
if resp.isValidAnswer(SMB.SMB_COM_NT_TRANSACT):
|
|
nt_trans_response = SMBCommand(resp['Data'][0])
|
|
nt_trans_parameters = SMBNTTransactionResponse_Parameters(nt_trans_response['Parameters'])
|
|
# Remove Potential Prefix Padding
|
|
return nt_trans_response['Data'][-nt_trans_parameters['TotalDataCount']:]
|
|
|
|
def echo(self, text = '', count = 1):
|
|
|
|
smb = NewSMBPacket()
|
|
comEcho = SMBCommand(SMB.SMB_COM_ECHO)
|
|
comEcho['Parameters'] = SMBEcho_Parameters()
|
|
comEcho['Data'] = SMBEcho_Data()
|
|
comEcho['Parameters']['EchoCount'] = count
|
|
comEcho['Data']['Data'] = text
|
|
smb.addCommand(comEcho)
|
|
|
|
self.sendSMB(smb)
|
|
|
|
for i in range(count):
|
|
resp = self.recvSMB()
|
|
resp.isValidAnswer(SMB.SMB_COM_ECHO)
|
|
return True
|
|
|
|
ERRDOS = { 1: 'Invalid function',
|
|
2: 'File not found',
|
|
3: 'Invalid directory',
|
|
4: 'Too many open files',
|
|
5: 'Access denied',
|
|
6: 'Invalid file handle. Please file a bug report.',
|
|
7: 'Memory control blocks destroyed',
|
|
8: 'Out of memory',
|
|
9: 'Invalid memory block address',
|
|
10: 'Invalid environment',
|
|
11: 'Invalid format',
|
|
12: 'Invalid open mode',
|
|
13: 'Invalid data',
|
|
15: 'Invalid drive',
|
|
16: 'Attempt to remove server\'s current directory',
|
|
17: 'Not the same device',
|
|
18: 'No files found',
|
|
32: 'Sharing mode conflicts detected',
|
|
33: 'Lock request conflicts detected',
|
|
80: 'File already exists'
|
|
}
|
|
|
|
ERRSRV = { 1: 'Non-specific error',
|
|
2: 'Bad password',
|
|
4: 'Access denied',
|
|
5: 'Invalid tid. Please file a bug report.',
|
|
6: 'Invalid network name',
|
|
7: 'Invalid device',
|
|
49: 'Print queue full',
|
|
50: 'Print queue full',
|
|
51: 'EOF on print queue dump',
|
|
52: 'Invalid print file handle',
|
|
64: 'Command not recognized. Please file a bug report.',
|
|
65: 'Internal server error',
|
|
67: 'Invalid path',
|
|
69: 'Invalid access permissions',
|
|
71: 'Invalid attribute mode',
|
|
81: 'Server is paused',
|
|
82: 'Not receiving messages',
|
|
83: 'No room to buffer messages',
|
|
87: 'Too many remote user names',
|
|
88: 'Operation timeout',
|
|
89: 'Out of resources',
|
|
91: 'Invalid user handle. Please file a bug report.',
|
|
250: 'Temporarily unable to support raw mode for transfer',
|
|
251: 'Temporarily unable to support raw mode for transfer',
|
|
252: 'Continue in MPX mode',
|
|
65535: 'Unsupported function'
|
|
}
|
|
|
|
ERRHRD = { 19: 'Media is write-protected',
|
|
20: 'Unknown unit',
|
|
21: 'Drive not ready',
|
|
22: 'Unknown command',
|
|
23: 'CRC error',
|
|
24: 'Bad request',
|
|
25: 'Seek error',
|
|
26: 'Unknown media type',
|
|
27: 'Sector not found',
|
|
28: 'Printer out of paper',
|
|
29: 'Write fault',
|
|
30: 'Read fault',
|
|
31: 'General failure',
|
|
32: 'Open conflicts with an existing open',
|
|
33: 'Invalid lock request',
|
|
34: 'Wrong disk in drive',
|
|
35: 'FCBs not available',
|
|
36: 'Sharing buffer exceeded'
|
|
}
|