Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions impacket/examples/smbclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import cmd
import os
import ntpath
import shlex

from six import PY2
from impacket.dcerpc.v5 import samr, transport, srvs
Expand Down Expand Up @@ -123,6 +124,8 @@ def do_help(self,line):
mget {mask} - downloads all files from the current directory matching the provided mask
cat {filename} - reads the filename from the current path
mount {target,path} - creates a mount point from {path} to {target} (admin required)
create_symlink {target,path} - creates a symlink from {path} to {target}, can be file or dir, path must exist (admin required)
remove_symlink {path} - removes the symlink at {path} without deleting the directory (admin required)
umount {path} - removes the mount point at {path} without deleting the directory (admin required)
list_snapshots {path} - lists the vss snapshots for the specified path
info - returns NetrServerInfo main results
Expand Down Expand Up @@ -671,6 +674,20 @@ def do_umount(self, mountpoint):

self.smb.removeMountPoint(self.tid, mountPath)

def do_create_symlink(self, line):
target, path = shlex.split(line)
target = target.replace('/','\\')
path = path.replace('/','\\')
if not path.startswith('\\'):
path = ntpath.join(self.pwd, path)
self.smb.createSymlink(self.tid, path, target)

def do_remove_symlink(self, line):
path = line.replace('/','\\')
if not path.startswith('\\'):
path = ntpath.join(self.pwd, path)
self.smb.removeSymlink(self.tid, path)

def do_EOF(self, line):
print('Bye!\n')
return True
22 changes: 22 additions & 0 deletions impacket/smb3structs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1305,6 +1305,28 @@ class MOUNT_POINT_REPARSE_GUID_DATA_STRUCTURE(Structure):
("DataBuffer", ":")
)

class SYMLINK_REPARSE_DATA_STRUCTURE(Structure):
structure = (
('ReparseTag', '<L=0xA000000C'),
('ReparseDataLen', '<H=len(self["PathBuffer"]) + 8'),
('Reserved', '<H=0'),
('SubstituteNameOffset', '<H=0'),
('SubstituteNameLength', '<H=0'),
('PrintNameOffset', '<H=0'),
('PrintNameLength', '<H=0'),
('Flags', '<L=0'),
('PathBuffer', ':')
)

class SYMLINK_REPARSE_GUID_DATA_STRUCTURE(Structure):
structure = (
('ReparseTag', '<L=0xA000000C'),
('ReparseDataLen', '<H=len(self["DataBuffer"])'),
('Reserved', '<H=0'),
('ReparseGuid', '16s=""'),
('DataBuffer', ':')
)

class SMB2Ioctl_Response(Structure):
structure = (
('StructureSize','<H=49'),
Expand Down
49 changes: 48 additions & 1 deletion impacket/smbconnection.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@
## Access Mask.
from impacket.smb import FILE_READ_DATA , FILE_WRITE_DATA, GENERIC_READ, GENERIC_WRITE, GENERIC_ALL, READ_CONTROL, \
FILE_READ_ATTRIBUTES, FILE_READ_EA, SYNCHRONIZE

## Share Access Modes.
from impacket.smb import FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_DELETE

from impacket.smb3structs import SMB2_0_IOCTL_IS_FSCTL, FSCTL_DELETE_REPARSE_POINT, FSCTL_SET_REPARSE_POINT, SYMLINK_REPARSE_DATA_STRUCTURE, SYMLINK_REPARSE_GUID_DATA_STRUCTURE


class SMBConnection:
"""
Expand Down Expand Up @@ -966,6 +967,52 @@ def removeMountPoint(self, tid, path):

self.closeFile(tid, fid)

def createSymlink(self, tid, path, target):
"""
creates a symlink from path to target

:param int tid: tree id of current connection
:param string path: file or directory where symlink is created (must already exist)
:param string target: file or directory where symlink will point to (can be inexistent)

:raise SessionError: if error
"""

substitute_name = f'\\??\\{target}'.encode('utf-16le')
print_name = target.encode('utf-16le')
reparse_data = SYMLINK_REPARSE_DATA_STRUCTURE()
reparse_data['PathBuffer'] = print_name + substitute_name
reparse_data['ReparseDataLen'] = len(print_name + substitute_name) + 12
reparse_data['SubstituteNameOffset'] = len(print_name)
reparse_data['SubstituteNameLength'] = len(substitute_name)
reparse_data['PrintNameOffset'] = 0
reparse_data['PrintNameLength'] = len(print_name)

fid = self.openFile(tid, path, GENERIC_READ|GENERIC_WRITE, creationOption=FILE_OPEN_REPARSE_POINT)
try:
self._SMBConnection.ioctl(tid, fid, FSCTL_SET_REPARSE_POINT, flags=SMB2_0_IOCTL_IS_FSCTL, inputBlob=reparse_data)
finally:
self.closeFile(tid, fid)

def removeSymlink(self, tid, path):
"""
removes a symlink without deleting the underlying file or directory

:param int tid: tree id of current connection
:param string path: path to symlink

:raise SessionError: if error
"""

reparse_data = SYMLINK_REPARSE_GUID_DATA_STRUCTURE()
reparse_data['DataBuffer'] = b''

fid = self.openFile(tid, path, GENERIC_READ|GENERIC_WRITE, creationOption=FILE_OPEN_REPARSE_POINT)
try:
self._SMBConnection.ioctl(tid, fid, FSCTL_DELETE_REPARSE_POINT, flags=SMB2_0_IOCTL_IS_FSCTL, inputBlob=reparse_data)
finally:
self.closeFile(tid, fid)

def rename(self, shareName, oldPath, newPath):
"""
Renames a file/directory.
Expand Down