Server IP : 85.214.239.14 / Your IP : 18.119.28.173 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /bin/ |
Upload File : |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # smbinfo is a cmdline tool to query SMB-specific file and fs # information on a Linux SMB mount (cifs.ko). # # Copyright (C) 2019 Aurelien Aptel <aaptel@suse.com> # Copyright (C) 2019 Ronnie Sahlberg <lsahlberg@redhat.com> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import os import re import argparse import fcntl import struct import stat import datetime import calendar VERBOSE = False # ioctl ctl codes CIFS_QUERY_INFO = 0xc018cf07 CIFS_ENUMERATE_SNAPSHOTS = 0x800ccf06 CIFS_DUMP_KEY = 0xc03acf08 CIFS_DUMP_FULL_KEY = 0xc011cf0a # large enough input buffer length INPUT_BUFFER_LENGTH = 16384 # length of a @GMT- token in bytes GMT_TOKEN_LEN_IN_BYTES = 24 * 2 # GMT format string GMT_FORMAT = "@GMT-%Y.%m.%d-%H.%M.%S" # cifs query flags PASSTHRU_QUERY_INFO = 0x00000000 PASSTHRU_FSCTL = 0x00000001 DIR_ACCESS_FLAGS = [ (0x00000001, "LIST_DIRECTORY"), (0x00000002, "ADD_FILE"), (0x00000004, "ADD_SUBDIRECTORY"), (0x00000008, "READ_EA"), (0x00000010, "WRITE_EA"), (0x00000020, "TRAVERSE"), (0x00000040, "DELETE_CHILD"), (0x00000080, "READ_ATTRIBUTES"), (0x00000100, "WRITE_ATTRIBUTES"), (0x00010000, "DELETE"), (0x00020000, "READ_CONTROL"), (0x00040000, "WRITE_DAC"), (0x00080000, "WRITE_OWNER"), (0x00100000, "SYNCHRONIZER"), (0x01000000, "ACCESS_SYSTEM_SECURITY"), (0x02000000, "MAXIMUM_ALLOWED"), (0x10000000, "GENERIC_ALL"), (0x20000000, "GENERIC_EXECUTE"), (0x40000000, "GENERIC_WRITE"), (0x80000000, "GENERIC_READ"), ] FILE_ACCESS_FLAGS = [ (0x00000001, "READ_DATA"), (0x00000002, "WRITE_DATA"), (0x00000004, "APPEND_DATA"), (0x00000008, "READ_EA"), (0x00000010, "WRITE_EA"), (0x00000020, "EXECUTE"), (0x00000040, "DELETE_CHILD"), (0x00000080, "READ_ATTRIBUTES"), (0x00000100, "WRITE_ATTRIBUTES"), (0x00010000, "DELETE"), (0x00020000, "READ_CONTROL"), (0x00040000, "WRITE_DAC"), (0x00080000, "WRITE_OWNER"), (0x00100000, "SYNCHRONIZER"), (0x01000000, "ACCESS_SYSTEM_SECURITY"), (0x02000000, "MAXIMUM_ALLOWED"), (0x10000000, "GENERIC_ALL"), (0x20000000, "GENERIC_EXECUTE"), (0x40000000, "GENERIC_WRITE"), (0x80000000, "GENERIC_READ"), ] FILE_ATTR_FLAGS = [ (0x00000001, "READ_ONLY"), (0x00000002, "HIDDEN"), (0x00000004, "SYSTEM"), (0x00000010, "DIRECTORY"), (0x00000020, "ARCHIVE"), (0x00000080, "NORMAL"), (0x00000100, "TEMPORARY"), (0x00000200, "SPARSE_FILE"), (0x00000400, "REPARSE_POINT"), (0x00000800, "COMPRESSED"), (0x00001000, "OFFLINE"), (0x00002000, "NOT_CONTENT_INDEXED"), (0x00004000, "ENCRYPTED"), (0x00008000, "INTEGRITY_STREAM"), (0x00020000, "NO_SCRUB_DATA"), ] FILE_MODE_FLAGS = [ (0x00000002, "WRITE_THROUGH"), (0x00000004, "SEQUENTIAL_ONLY"), (0x00000008, "NO_INTERMEDIATE_BUFFERING"), (0x00000010, "SYNCHRONOUS_IO_ALERT"), (0x00000020, "SYNCHRONOUS_IO_NONALERT"), (0x00001000, "DELETE_ON_CLOSE"), ] ALIGN_TYPES = [ (0, "BYTE_ALIGNMENT"), (1, "WORD_ALIGNMENT"), (3, "LONG_ALIGNMENT"), (7, "QUAD_ALIGNMENT"), (15, "OCTA_ALIGNMENT"), (31, "32_bit_ALIGNMENT"), (63, "64_bit_ALIGNMENT"), (127, "128_bit_ALIGNMENT"), (255, "254_bit_ALIGNMENT"), (511, "512_bit_ALIGNMENT"), ] COMPRESSION_TYPES = [ (0x0000, "NONE"), (0x0002, "LZNT1"), ] CONTROL_FLAGS = [ (0x8000, "SR"), (0x4000, "RM"), (0x2000, "PS"), (0x1000, "PD"), (0x0800, "SI"), (0x0400, "DI"), (0x0200, "SC"), (0x0100, "DC"), (0x0080, "DT"), (0x0040, "SS"), (0x0020, "SD"), (0x0010, "SP"), (0x0008, "DD"), (0x0004, "DP"), (0x0002, "GD"), (0x0001, "OD"), ] ACE_TYPES = [ (0x00, "ALLOWED"), (0x01, "DENIED"), (0x02, "AUDIT"), (0x03, "ALARM"), (0x04, "ALLOWED_COMPOUND"), (0x05, "ALLOWED_OBJECT"), (0x06, "DENIED_OBJECT"), (0x07, "AUDIT_OBJECT"), (0x08, "ALARM_OBJECT"), (0x09, "ALLOWED_CALLBACK"), (0x0a, "DENIED_CALLBACK"), (0x0b, "ALLOWED_CALLBACK_OBJECT"), (0x0c, "DENIED_CALLBACK_OBJECT"), (0x0d, "AUDIT_CALLBACK"), (0x0e, "ALARM_CALLBACK"), (0x0f, "AUDIT_CALLBACK_OBJECT"), (0x10, "ALARM_CALLBACK_OBJECT"), (0x11, "MANDATORY_LABEL"), (0x12, "RESOURCE_ATTRIBUTE"), (0x13, "SCOPED_POLICY_ID"), ] ACE_FLAGS = [ (0x80, "FAILED_ACCESS"), (0x40, "SUCCESSFUL_ACCESS"), (0x10, "INHERITED"), (0x08, "INHERIT_ONLY"), (0x04, "NO_PROPAGATE_INHERIT"), (0x02, "CONTAINER_INHERIT"), (0x01, "OBJECT_INHERIT"), ] CIPHER_TYPES = [ (0x00, "AES-128-CCM"), (0x01, "AES-128-CCM"), (0x02, "AES-128-GCM"), (0x03, "AES-256-CCM"), (0x04, "AES-256-GCM"), ] def main(): # # Global options and arguments # ap = argparse.ArgumentParser(description="Display SMB-specific file information using cifs IOCTL") ap.add_argument("-V", "--verbose", action="store_true", help="verbose output") subp = ap.add_subparsers(help="sub-commands help") subp.required = True subp.dest = 'subcommand' # # To add a new sub-command xxx, add a subparser xxx complete with # help, options and/or arguments and implement cmd_xxx() # sap = subp.add_parser("fileaccessinfo", help="Prints FileAccessInfo for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_fileaccessinfo) sap = subp.add_parser("filealigninfo", help="Prints FileAlignInfo for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_filealigninfo) sap = subp.add_parser("fileallinfo", help="Prints FileAllInfo for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_fileallinfo) sap = subp.add_parser("filebasicinfo", help="Prints FileBasicInfo for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_filebasicinfo) sap = subp.add_parser("fileeainfo", help="Prints FileEAInfo for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_fileeainfo) sap = subp.add_parser("filefsfullsizeinfo", help="Prints FileFsFullSizeInfo for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_filefsfullsizeinfo) sap = subp.add_parser("fileinternalinfo", help="Prints FileInternalInfo for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_fileinternalinfo) sap = subp.add_parser("filemodeinfo", help="Prints FileModeInfo for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_filemodeinfo) sap = subp.add_parser("filepositioninfo", help="Prints FilePositionInfo for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_filepositioninfo) sap = subp.add_parser("filestandardinfo", help="Prints FileStandardInfo for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_filestandardinfo) sap = subp.add_parser("filestreaminfo", help="Prints FileStreamInfo for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_filestreaminfo) sap = subp.add_parser("fsctl-getobjid", help="Prints the objectid of the file and GUID of the underlying volume.") sap.add_argument("file") sap.set_defaults(func=cmd_fsctl_getobjid) sap = subp.add_parser("getcompression", help="Prints the compression setting for the file") sap.add_argument("file") sap.set_defaults(func=cmd_getcompression) sap = subp.add_parser("setcompression", help="Sets the compression level for the file") sap.add_argument("type", choices=['no','default','lznt1']) sap.add_argument("file") sap.set_defaults(func=cmd_setcompression) sap = subp.add_parser("list-snapshots", help="List the previous versions of the volume that backs this file") sap.add_argument("file") sap.set_defaults(func=cmd_list_snapshots) sap = subp.add_parser("quota", help="Prints the quota for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_quota) sap = subp.add_parser("secdesc", help="Prints the security descriptor for a cifs file") sap.add_argument("file") sap.set_defaults(func=cmd_secdesc) sap = subp.add_parser("keys", help="Prints the decryption information needed to view encrypted network traces") sap.add_argument("file") sap.set_defaults(func=cmd_keys) # parse arguments args = ap.parse_args() # act on any global options if args.verbose: global VERBOSE VERBOSE = True # call subcommand function args.func(args) class QueryInfoStruct: def __init__(self, info_type=0, file_info_class=0, additional_information=0, flags=0, input_buffer_length=0, output_buffer_length=0): self.info_type = info_type self.file_info_class = file_info_class self.additional_information = additional_information self.flags = flags self.input_buffer_length = input_buffer_length self.output_buffer_length = output_buffer_length buf_size = max(self.input_buffer_length, self.output_buffer_length) self.input_buffer = bytearray(buf_size) def pack_input(self, fmt, offset, *vals): struct.pack_into(fmt, self.input_buffer, offset, *vals) def ioctl(self, fd, out_fmt=None): buf = bytearray() buf.extend(struct.pack("IIIIII", self.info_type, self.file_info_class, self.additional_information, self.flags, self.input_buffer_length, self.output_buffer_length)) in_len = len(buf) buf.extend(self.input_buffer) fcntl.ioctl(fd, CIFS_QUERY_INFO, buf, True) if out_fmt: return struct.unpack_from(out_fmt, buf, in_len) else: return buf[in_len:] def flags_to_str(flags, bitlist, verbose=None): if verbose is None: verbose = VERBOSE if not verbose: return "0x%08x"%flags out = [] for bit, name in bitlist: if flags & bit: out.append(name) return "0x%08x (%s)"%(flags, ",".join(out)) def type_to_str(typ, typelist, verbose=None): if verbose is None: verbose = VERBOSE if not verbose: return "0x%08x"%typ s = "Unknown" for val, name in typelist: if typ == val: s = name return "0x%08x (%s)"%(typ, s) def cmd_fileaccessinfo(args): qi = QueryInfoStruct(info_type=0x1, file_info_class=8, input_buffer_length=4) try: fd = os.open(args.file, os.O_RDONLY) info = os.fstat(fd) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False print_fileaccessinfo(buf, info) def print_fileaccessinfo(buf, info): flags = struct.unpack_from('<I', buf, 0)[0] if stat.S_ISDIR(info.st_mode): print("Directory access flags:", flags_to_str(flags, DIR_ACCESS_FLAGS)) else: print("File/Printer access flags:", flags_to_str(flags, FILE_ACCESS_FLAGS)) def cmd_filealigninfo(args): qi = QueryInfoStruct(info_type=0x1, file_info_class=17, input_buffer_length=4) try: fd = os.open(args.file, os.O_RDONLY) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False print_filealigninfo(buf) def print_filealigninfo(buf): mask = struct.unpack_from('<I', buf, 0)[0] print("File alignment: %s"%type_to_str(mask, ALIGN_TYPES)) def cmd_fileallinfo(args): qi = QueryInfoStruct(info_type=0x1, file_info_class=18, input_buffer_length=INPUT_BUFFER_LENGTH) try: fd = os.open(args.file, os.O_RDONLY) info = os.fstat(fd) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False print_filebasicinfo(buf) print_filestandardinfo(buf[40:]) print_fileinternalinfo(buf[64:]) print_fileeainfo(buf[72:]) print_fileaccessinfo(buf[76:], info) print_filepositioninfo(buf[80:]) print_filemodeinfo(buf[88:]) print_filealigninfo(buf[92:]) def win_to_datetime(smb2_time): usec = (smb2_time / 10) % 1000000 sec = (smb2_time - 116444736000000000) // 10000000 return datetime.datetime.fromtimestamp(sec + usec/10000000) def cmd_filebasicinfo(args): qi = QueryInfoStruct(info_type=0x1, file_info_class=4, input_buffer_length=40) try: fd = os.open(args.file, os.O_RDONLY) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False print_filebasicinfo(buf) def print_filebasicinfo(buf): ctime, atime, wtime, mtime, attrs = struct.unpack_from('<QQQQI', buf, 0) print("Creation Time: %s"%win_to_datetime(ctime)) print("Last Access Time: %s"%win_to_datetime(atime)) print("Last Write Time: %s"%win_to_datetime(wtime)) print("Last Change Time: %s"%win_to_datetime(mtime)) print("File Attributes: %s"%flags_to_str(attrs, FILE_ATTR_FLAGS)) def cmd_fileeainfo(args): qi = QueryInfoStruct(info_type=0x1, file_info_class=7, input_buffer_length=4) try: fd = os.open(args.file, os.O_RDONLY) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False print_fileeainfo(buf) def print_fileeainfo(buf): size = struct.unpack_from('<I', buf, 0)[0] print("EA Size: %d"%size) def cmd_filefsfullsizeinfo(args): qi = QueryInfoStruct(info_type=0x2, file_info_class=7, input_buffer_length=32) try: fd = os.open(args.file, os.O_RDONLY) total, caller_avail, actual_avail, sec_per_unit, byte_per_sec = qi.ioctl(fd, '<QQQII') except Exception as e: print("syscall failed: %s"%e) return False print("Total Allocation Units: %d"%total) print("Caller Available Allocation Units: %d"%caller_avail) print("Actual Available Allocation Units: %d"%actual_avail) print("Sectors Per Allocation Unit: %d"%sec_per_unit) print("Bytes Per Sector: %d"%byte_per_sec) def cmd_fileinternalinfo(args): qi = QueryInfoStruct(info_type=0x1, file_info_class=6, input_buffer_length=8) try: fd = os.open(args.file, os.O_RDONLY) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False print_fileinternalinfo(buf) def print_fileinternalinfo(buf): index = struct.unpack_from('<Q', buf, 0)[0] print("Index Number: %d"%index) def cmd_filemodeinfo(args): qi = QueryInfoStruct(info_type=0x1, file_info_class=16, input_buffer_length=4) try: fd = os.open(args.file, os.O_RDONLY) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False print_filemodeinfo(buf) def print_filemodeinfo(buf): mode = struct.unpack_from('<I', buf, 0)[0] print("Mode: %s"%flags_to_str(mode, FILE_MODE_FLAGS)) def cmd_filepositioninfo(args): qi = QueryInfoStruct(info_type=0x1, file_info_class=14, input_buffer_length=8) try: fd = os.open(args.file, os.O_RDONLY) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False print_filepositioninfo(buf) def print_filepositioninfo(buf): offset = struct.unpack_from('<Q', buf, 0)[0] print("Current Byte Offset: %d"%offset) def cmd_filestandardinfo(args): qi = QueryInfoStruct(info_type=0x1, file_info_class=5, input_buffer_length=24) try: fd = os.open(args.file, os.O_RDONLY) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False print_filestandardinfo(buf) def print_filestandardinfo(buf): nalloc, eof, nlink, del_pending, del_dir = struct.unpack_from('<QQIBB', buf, 0) print("Allocation Size: %d"%nalloc) print("End Of File: %d"%eof) print("Number of Links: %d"%nlink) print("Delete Pending: %d"%del_pending) print("Delete Directory: %d"%del_dir) def guid_to_str(buf): return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"%struct.unpack_from('<IHHBBBBBBBB', buf, 0) def cmd_fsctl_getobjid(args): qi = QueryInfoStruct(info_type=0x9009c, file_info_class=5, flags=PASSTHRU_FSCTL, input_buffer_length=64) try: fd = os.open(args.file, os.O_RDONLY) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False print("Object-ID: %s"%guid_to_str(buf)) print("Birth-Volume-ID: %s"%guid_to_str(buf[16:])) print("Birth-Object-ID: %s"%guid_to_str(buf[32:])) print("Domain-ID: %s"%guid_to_str(buf[48:])) def cmd_getcompression(args): qi = QueryInfoStruct(info_type=0x9003c, flags=PASSTHRU_FSCTL, input_buffer_length=2) try: fd = os.open(args.file, os.O_RDONLY) ctype = qi.ioctl(fd, '<H')[0] except Exception as e: print("syscall failed: %s"%e) return False ctype_name = "UNKNOWN" for val, name in COMPRESSION_TYPES: if ctype == val: ctype_name = name break print("Compression: %d (%s)"%(ctype, ctype_name)) def cmd_setcompression(args): qi = QueryInfoStruct(info_type=0x9c040, flags=PASSTHRU_FSCTL, output_buffer_length=2) type_map = {'no': 0, 'default': 1, 'lznt1': 2} qi.pack_input('<H', 0, type_map[args.type]) try: fd = os.open(args.file, os.O_RDONLY) qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False class SnapshotArrayStruct: def __init__(self, nb_snapshots=0, nb_snapshots_returned=0, snapshot_array_size=12): self.nb_snapshots = nb_snapshots self.nb_snapshots_returned = nb_snapshots_returned self.snapshot_array_size = snapshot_array_size self.snapshot_array = [] def ioctl(self, fd, op): buf = bytearray() buf.extend(struct.pack("III", self.nb_snapshots, self.nb_snapshots_returned, self.snapshot_array_size)) buf.extend(bytearray(16 + self.snapshot_array_size)) fcntl.ioctl(fd, op, buf, True) out = SnapshotArrayStruct() out.nb_snapshots, out.nb_snapshots_returned, out.snapshot_array_size = struct.unpack_from('III', buf, 0) data = buf[12:] # '@\x00G\x00M\x00T\x00-\x002\x000\x001\x009\x00.\x000\x004\x00.\x000\x005\x00-\x002\x003\x00.\x001\x000\x00.\x005\x000\x00\x00\x00' index_start = 0 while index_start < len(data): gmt_start = data.find(b'@', index_start) if gmt_start == -1 or len(data) - gmt_start < GMT_TOKEN_LEN_IN_BYTES: break gmt = data[gmt_start:gmt_start + GMT_TOKEN_LEN_IN_BYTES] index_start = gmt_start + GMT_TOKEN_LEN_IN_BYTES out.snapshot_array.append(datetime.datetime.strptime(gmt.decode('utf-16'), GMT_FORMAT)) return out def datetime_to_smb(dt): ntfs_time_offset = (369*365 + 89) * 24 * 3600 * 10000000 return calendar.timegm(dt.timetuple()) * 10000000 + ntfs_time_offset def cmd_list_snapshots(args): sa1req = SnapshotArrayStruct() sa1res = None sa2req = None sa2res = None try: fd = os.open(args.file, os.O_RDONLY) sa1res = sa1req.ioctl(fd, CIFS_ENUMERATE_SNAPSHOTS) except Exception as e: print("syscall failed: %s"%e) return False if sa1res.nb_snapshots == 0: return sa2req = SnapshotArrayStruct(nb_snapshots=sa1res.nb_snapshots, snapshot_array_size=sa1res.snapshot_array_size) try: fd = os.open(args.file, os.O_RDONLY) sa2res = sa2req.ioctl(fd, CIFS_ENUMERATE_SNAPSHOTS) except Exception as e: print("syscall failed: %s"%e) return False print("Number of snapshots: %d Number of snapshots returned: %d"%(sa2res.nb_snapshots, sa2res.nb_snapshots_returned)) print("Snapshot list in GMT (Coordinated UTC Time) and SMB format (100 nanosecond units needed for snapshot mounts):") for i, d in enumerate(sa2res.snapshot_array): print("%d) GMT:%s\n SMB3:%d"%(i + 1, d.strftime(GMT_FORMAT), datetime_to_smb(d))) class SID: def __init__(self, buf, off=0): rev, sublen = struct.unpack_from('BB', buf, off+0) off += 2 auth = 0 subauth = [] for i in range(6): auth = (auth << 8)|buf[off] off += 1 for i in range(sublen): subauth.append(struct.unpack_from('<I', buf, off)) off += 4 self.rev = rev self.auth = auth self.subauth = subauth def __str__(self): auth = ("0x%x" if self.auth >= 2**32 else "%d")%self.auth return "S-%d-%s-%s"%(self.rev, auth, '-'.join(["%d"%x for x in self.subauth])) class ACE: def __init__(self, buf, off=0, is_dir=False): self.typ, self.flags, self.size = struct.unpack_from('<BBH', buf, off) self.is_dir = is_dir if self.typ not in [0,1,2]: self.buf = buf[4:] else: self.mask = struct.unpack_from('<I', buf, off+4)[0] self.sid = SID(buf, off+8) def __str__(self): s = [] s.append("Type: %s" % type_to_str(self.typ, ACE_TYPES)) s.append("Flags: %s" % flags_to_str(self.flags, ACE_FLAGS)) if self.typ not in [0,1,2]: s.append("<%s>"%(" ".join(["%02x"%x for x in self.buf]))) else: s.append("Mask: %s"%flags_to_str(self.mask, (DIR_ACCESS_FLAGS if self.is_dir else FILE_ACCESS_FLAGS))) s.append("SID: %s"%self.sid) return ", ".join(s) def cmd_quota(args): qi = QueryInfoStruct(info_type=0x04, input_buffer_length=INPUT_BUFFER_LENGTH) qi.pack_input('BBI', 0, 0, # return single 1, # restart scan 0, # sid list length ) qi.output_buffer_length = 16 buf = None try: fd = os.open(args.file, os.O_RDONLY) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False off = 0 while off < len(buf): next_off = struct.unpack_from('<I', buf, off+ 0)[0] sid_len = struct.unpack_from('<I', buf, off+ 4)[0] atime = struct.unpack_from('<Q', buf, off+ 8)[0] qused = struct.unpack_from('<Q', buf, off+16)[0] qthresh = struct.unpack_from('<Q', buf, off+24)[0] qlimit = struct.unpack_from('<Q', buf, off+32)[0] sid = SID(buf, off+40) print("SID Length: %d"%sid_len) print("Change Time: %s"%win_to_datetime(atime)) print("Quota Used: %d"%qused) print("Quota Threshold:", ("NO THRESHOLD" if qthresh == 0xffffffffffffffff else "%d"%qthresh)) print("Quota Limit:", ("NO LIMIT" if qlimit == 0xffffffffffffffff else "%d"%qlimit)) print("SID: %s"%sid) if next_off == 0: break off += next_off def cmd_secdesc(args): qi = QueryInfoStruct(info_type=0x03, additional_information=0x7, # owner, group, dacl input_buffer_length=INPUT_BUFFER_LENGTH) buf = None info = None try: fd = os.open(args.file, os.O_RDONLY) info = os.fstat(fd) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False is_dir = stat.S_ISDIR(info.st_mode) rev, ctrl, off_owner, off_group, off_dacl = struct.unpack_from('<BxHIIxxxxI', buf, 0) print("Revision: %d"%rev) print("Control: %s"%flags_to_str(ctrl, CONTROL_FLAGS)) if off_owner: print("Owner: %s"%SID(buf, off_owner)) if off_group: print("Group: %s"%SID(buf, off_group)) if off_dacl: print("DACL:") rev, count = struct.unpack_from('<BxxxH', buf, off_dacl) off_dacl += 8 for i in range(count): ace = ACE(buf, off_dacl, is_dir=is_dir) print(ace) off_dacl += ace.size def cmd_filestreaminfo(args): qi = QueryInfoStruct(info_type=0x1, file_info_class=22, input_buffer_length=INPUT_BUFFER_LENGTH) try: fd = os.open(args.file, os.O_RDONLY) info = os.fstat(fd) buf = qi.ioctl(fd) except Exception as e: print("syscall failed: %s"%e) return False print_filestreaminfo(buf) def print_filestreaminfo(buf): offset = 0 while offset < len(buf): next_offset = struct.unpack_from('<I', buf, offset + 0)[0] name_length = struct.unpack_from('<I', buf, offset + 4)[0] if (name_length > 0): stream_size = struct.unpack_from('<q', buf, offset + 8)[0] stream_alloc_size = struct.unpack_from('<q', buf, offset + 16)[0] stream_utf16le_name = struct.unpack_from('< %ss'% name_length, buf, offset + 24)[0] stream_name = stream_utf16le_name.decode("utf-16le") if (offset > 0): print() if (stream_name=="::$DATA"): print("Name: %s"% stream_name) else: print("Name: %s"% stream_name[stream_name.find(":") + 1 : stream_name.rfind(':$DATA')]) print("Size: %d bytes"% stream_size) print("Allocation size: %d bytes "% stream_alloc_size) if (next_offset == 0): break offset+=next_offset class KeyDebugInfoStruct: def __init__(self): self.suid = bytearray() self.cipher = 0 self.session_key = bytearray() self.enc_key = bytearray() self.dec_key = bytearray() def ioctl(self, fd): buf = bytearray() buf.extend(struct.pack("= 8s H 16s 16s 16s", self.suid, self.cipher, self.session_key, self.enc_key, self.dec_key)) fcntl.ioctl(fd, CIFS_DUMP_KEY, buf, True) (self.suid, self.cipher, self.session_key, self.enc_key, self.dec_key) = struct.unpack_from('= 8s H 16s 16s 16s', buf, 0) class FullKeyDebugInfoStruct: def __init__(self): # lets pick something large to be future proof # 17 + 3*32 would be strict minimum as of linux 5.13 self.in_size = 1024 self.suid = bytearray() self.cipher = 0 self.session_key_len = 0 self.server_in_key_len = 0 self.server_out_key_len = 0 def ioctl(self, fd): fmt = "= I 8s H B B B" size = struct.calcsize(fmt) buf = bytearray() buf.extend(struct.pack(fmt, self.in_size, self.suid, self.cipher, self.session_key_len, self.server_in_key_len, self.server_out_key_len)) buf.extend(bytearray(self.in_size-size)) fcntl.ioctl(fd, CIFS_DUMP_FULL_KEY, buf, True) (self.in_size, self.suid, self.cipher, self.session_key_len, self.server_in_key_len, self.server_out_key_len) = struct.unpack_from(fmt, buf, 0) end = size self.session_key = buf[end:end+self.session_key_len] end += self.session_key_len self.server_in_key = buf[end:end+self.server_in_key_len] end += self.server_in_key_len self.server_out_key = buf[end:end+self.server_out_key_len] def bytes_to_hex(buf): return " ".join(["%02x"%x for x in buf]) def cmd_keys(args): fd = os.open(args.file, os.O_RDONLY) kd = FullKeyDebugInfoStruct() try: # try new call first kd.ioctl(fd) except Exception as e: # new failed, try old call kd = KeyDebugInfoStruct() try: kd.ioctl(fd) except Exception as e: # both new and old call failed print("syscall failed: %s"%e) return False print("Session Id: %s"%bytes_to_hex(kd.suid)) print("Cipher: %s"%type_to_str(kd.cipher, CIPHER_TYPES, verbose=True)) print("Session Key: %s"%bytes_to_hex(kd.session_key)) print("Encryption key: %s"%bytes_to_hex(kd.enc_key)) print("Decryption key: %s"%bytes_to_hex(kd.dec_key)) else: # no exception, new call succeeded print("Session Id: %s"%bytes_to_hex(kd.suid)) print("Cipher: %s"%type_to_str(kd.cipher, CIPHER_TYPES, verbose=True)) print("Session Key: %s"%bytes_to_hex(kd.session_key)) print("ServerIn Key: %s"%bytes_to_hex(kd.server_in_key)) print("ServerOut key: %s"%bytes_to_hex(kd.server_out_key)) if __name__ == '__main__': main()