Windows: SMB Server (v1 and v2) Mount Point Arbitrary Device Open EoP
Platform: Windows 10 1703 and 1709 (seems the same on 7 and 8.1 but not extensively tested)
Class: Elevation of Privilege
The SMB server driver (srv.sys and srv2.sys) don't check the destination of a NTFS mount point when manually handling a reparse operation leading to being able to locally open an arbitrary device via an SMB client which can result in EoP.
Note before I start event though this involves SMB this is only a local issue, I don't know of anyway to exploit this remotely without being able to run an application on the local machine.
NTFS mount points are handled local to the SMB server so that the client does not see them. This is different from NTFS symbolic links which are passed back to the client to deal with. In order to handle the symbolic link case the server calls IoCreateFileEx from Smb2CreateFile passing the IO_STOP_ON_SYMLINK flag which results in the IoCreateFileEx call failing with the STATUS_STOPPED_ON_SYMLINK code. The server can then extract the substitution path from the reparse pointer buffer and either pass the buffer to the client if it's a symbolic link or handle it if it's a mount point.
The way the server handles a symbolic link is to recall IoCreateFileEx in a loop (it does check for a maximum iteration count although I'd swear that's a recent change) passing the new substitute path. This is different to how the IO manager would handle this operation. In the IO manager's case the reparse operation is limited to a small subset of device types, such as Disk Volumes. If the new target isn't in the small list of types then the reparse will fail with an STATUS_IO_REPARSE_DATA_INVALID error. However the SMB server does no checks so the open operation can be redirected to any device. This is interesting due to the way in which the device is being opened, it's in a system thread and allows a caller to pass an arbitrary EA block which can be processed by the device create handler.
One use for this is being able to the spoof the process ID and session ID accessible from a named pipe using APIs such as GetNamedPipeClientProcessId. Normally to set these values to arbitrary values requires kernel mode access, which the SMB driver provides. While you can open a named pipe via SMB anyway in that case you can't specify the arbitrary values as the driver provides its own to set the computer name accessible with GetNamedPipeClientComputerName. I've not found any service which uses these values for security related properties.
Note that both SMBv1 and SMBv2 are vulnerable to the same bug even the code isn't really shared between them.
Proof of Concept:
I’ve provided a PoC as a C# project. It creates a mount point to \Device and then tries to open the CNG driver directly and via the local share for the drive.
1) Compile the C# project. It will need to grab the NtApiDotNet from NuGet to work.
2) Execute the PoC as a normal user.
Both direct and via SMB should fail with STATUS_IO_REPARSE_DATA_INVALID error.
The direct open fails with STATUS_IO_REPARSE_DATA_INVALID however the one via SMB fails with STATUS_INVALID_INFO_CLASS which indicates that the CNG driver was opened.
Proof of Concept: