Thursday, 27 December 2018

Abusing Mount Points over the SMB Protocol

This blog post is a quick writeup on an interesting feature of SMBv2 which might have uses for lateral movement and red-teamers. When I last spent significant time looking at symbolic link attacks on Windows I took a close look at the SMB server. Since version 2 the SMB protocol has support for symbolic links, specifically the NTFS Reparse Point format. If the SMB server encounters an NTFS symbolic link within a share it'll extract the REPARSE_DATA_BUFFER and return it to the client based on the SMBv2 protocol specification §

Screenshot of symbolic link error response from SMB specifications.

The client OS is responsible for parsing the REPARSE_DATA_BUFFER and following it locally. This means that only files the client can already access can be referenced by symbolic links. In fact even resolving symbolic links locally isn't enabled by default, although I did find a bypass which allowed a malicious server to bypass the client policy and allowing resolving symbolic links locally. Microsoft declined to fix the bypass at the time, it's issue 138 if you're interested.

What I found interesting is while IO_REPARSE_TAG_SYMLINK is handled specially on the client, if the server encounters the IO_REPARSE_TAG_MOUNT_POINT reparse point it would follow it on the server. Therefore, if you could introduce a mount point within a share you could access any fixed disk on the server, even if it's not shared directly. That could have many uses for lateral movement, but the question becomes how could we add a mount point without already having local access to the disk?

First thing to try is to just create a mount point via a UNC path and see what happens. Using the MKLINK CMD built-in you get the following:

Using mklink on \\localhost\c$\abc returns the error "Local NTFS volumes are required to complete the operation."

The error would indicate that setting mount points on remote servers isn't supported. This would make some sense, setting a mount point on a remote drive would result in unexpected consequences. You'd assume the protocol either doesn't support setting reparse points at all, or at least restricts them to only allowing symbolic links. We can get a rough idea what the protocol expects by looking up the details in the protocol specification. Setting a reparse point requires sending the FSCTL_SET_REPARSE_POINT IO control code to a file, therefore we can look up the section on the SMB2 IOCTL command to see if any there's any information about the control code.

After a bit of digging you'll find that FSCTL_SET_REPARSE_POINT is indeed supported and there's a note in § which I've reproduced below.

"When the server receives a request that contains an SMB2 header with a Command value equal to SMB2 IOCTL and a CtlCode of FSCTL_SET_REPARSE_POINT, message handling proceeds as follows:
If the ReparseTag field in FSCTL_SET_REPARSE_POINT, as specified in [MS-FSCC] section 2.3.65, is not IO_REPARSE_TAG_SYMLINK, the server SHOULD verify that the caller has the required permissions to execute this FSCTL.<330> If the caller does not have the required permissions, the server MUST fail the call with an error code of STATUS_ACCESS_DENIED."
The text in the specification seems to imply the server only needs to check explicitly for IO_REPARSE_TAG_SYMLINK, and if the tag is something different it should do some sort of check to see if it's allowed, but it doesn't say anything about setting a different tag to be explicitly banned. Perhaps it's just the MKLINK built-in which doesn't handle this scenario? Let's try the CreateMountPoint tool from my symboliclink-testing-tools project and see if that helps.

Using CreateMountPoint on \\localhost\c$\abc gives access denied.

CreateMountPoint doesn't show an error about only supporting local NTFS volumes, but it does return an access denied error. This ties in with the description §, if the implied check fails the code should return access denied. Of course the protocol specification doesn't actually say what check should be performed, I guess it's time to break out the disassembler and look at the implementation in the SMBv2 driver, srv2.sys.

I used IDA to look for immediate values for IO_REPARSE_TAG_SYMLINK which is 0xA000000C. It seems likely that any check would first look for that value along with any other checking for the other tags. In the driver from Windows 10 1809 there was only one hit in Smb2ValidateIoctl. The code is roughly as follows:

NTSTATUS Smb2ValidateIoctl(SmbIoctlRequest* request) { // ... switch(request->IoControlCode) { case FSCTL_SET_REPARSE_POINT: REPARSE_DATA_BUFFER* reparse = (REPARSE_DATA_BUFFER*)request->Buffer;
// Validate length etc. if (reparse->ReparseTag != IO_REPARSE_TAG_SYMLINK &&
!request->SomeOffset->SomeByteValue) {
return STATUS_ACCESS_DENIED; } // Complete FSCTL_SET_REPARSE_POINT request. } }

The code extracts the data from the IOCTL request, it fails with STATUS_ACCESS_DENIED if the tag is not IO_REPARSE_TAG_SYMLINK and some byte value is 0 which is referenced from the request data. Tracking down who sets this value can be tricky sometimes, however I usually have good results by just searching for the variables offset as an immediate value in IDA, in this case 0x200 and just go through the results looking for likely MOV instructions. I found an instruction "MOV [RCX+0x200], AL" inside Smb2ExecuteSessionSetupReal which looked to be the one. The variable is being set with the result of the call to Smb2IsAdmin which just checks if the caller has the BUILTIN\Administrators group in their token. It seems that we can set arbitrary reparse points on a remote share, as long as we're an administrator on the machine. We should still test that's really the case:

Using CreateMountPoint on \\localhost\c$\abc is successful and listing the directory showing the windows folder.

Testing from an administrator account allows us to create the mount point, and when listing the directory from a UNC path the Windows folder is shown. While I've demonstrated this on local admin shares this will work on any share and the mount point is followed on the remote server.

Is this trick useful? Requiring administrator access does mean it's not something you could abuse for local privilege escalation and if you have administrator access remotely there's almost certainly nastier things you could do. Still it could be useful if the target machine has the admin shares disabled, or there's monitoring in place which would detect the use of ADMIN$ or C$ in lateral movement as if there's any other writable share you could add a new directory which would give full control over any other fixed drive.

I can't find anyone documenting this before, but I could have missed it as the search results are heavily biased towards SAMBA configurations when you search for SMB and mount points (for obvious reasons). This trick is another example of ensuring you test any assumptions about the security behavior of a system as it's probably not documented what the actual behavior is. Even though a tool such as MKLINK claims a lack of a support for setting remote mount points by digging into available specification and looking at the code itself you can find some interesting stuff.