Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1296

VirtualBox: Windows Process DLL UNC Path Signature Bypass EoP
Platform: VirtualBox v5.1.22 r115126 x64 (Tested on Windows 10)
Class: Elevation of Privilege

Summary:
The process hardening implemented by the VirtualBox driver can be circumvented to load arbitrary code inside a VirtualBox process giving access to the VBoxDrv driver which can allow routes to EoP from a normal user.

Description:

NOTE: I don’t know if you consider this an issue or not, however you fixed the last bypass I sent so it’s possible you still consider it a security boundary.

This is a similar issue in impact to the one I reported in S0867394 but it uses a completely different mechanism. Once again we can use a current user COM registration to redirect the VBOX COM object to an arbitrary DLL path. However in this case rather than using MS signed code or abusing path behaviours in DLL library loading (and a bug in the hardening code) we'll instead abuse the way Windows handles image mapping from a kernel perspective.

On Windows mapped DLLs use an Image Section under the hood. This is a special type of file mapping where the parsing and relocating of the PE file is all handled by the kernel. To allow for sharing of image mappings (so ideally the kernel only needs to do the parsing and relocation once) the kernel memory manager ties the file object to an existing section object by using a unique section pointer set by the file system driver. The interesting thing about this is the section pointer doesn't necessarily ensure the file hasn't changed, just that the file system considered the file the "same". Therefore it's possible that opening a file and reading it returns a completely different PE file than the one you'll get if you then map that file as an image section.

If we can get a file mapped via one section pointer, then change the file underneath to be a different one we can exploit this. However on a default NTFS drive this isn't really possible due to things like sharing (outside of having admin privileges) so we need to use something different. For that we can use the SMB client file system. When you open a file via a UNC path any queries for the path return a MUP device path such as \Device\Mup\server\share\path\file.dll. When this is first mapped into memory the section pointer is used to refer to any file with that same path, however when opening the file the SMB client still needs to go to the server and receive the current data. Even with SMB supporting locking hopefully you can see that if you control the server as an admin then all bets are off with regards to returning the same data. In the worst case you could compile SAMBA with some custom code to do the attack (not that it would be needed). SMB also supports all the necessary file operations the hardening code checks for such as requesting the file Owner so we can pass all the checks with this. So to do the attack we can do the following:

1. Load our untrusted DLL from \\server\share\path\file.dll and map it into memory using something like LoadLibrary. This mapping needs to stay valid for the entire exploit to work.
2. Change the untrusted DLL file on the server to one which is a valid file for the hardening code and also has a owner set appropriately.
3. Add current user COM redirection to point VBOX class to the original UNC path.
4. Run VirtualBox. The hardening code will read the UNC path and find it's a valid file, however when the kernel maps the image section in NtMapViewOfSection it will find it's already got a mapped image loaded and will use that instead, the mapped image is of course the untrusted DLL, not the one the hardening code checked.
5. The untrusted DLL will be loaded into memory and executed.

This is easy enough using a remote server but it would be more useful to exploit locally. For that we can use the default admin shares on a Windows system which expose the drives over SMB. Even though their primary purpose is for administration you don't need to be an administrator if you access them locally, you'll just get access just a the same user account. So we can do the redirection of the file access as follows:

1. Set up a path such as c:\poc somewhere on an NTFS drive and copy the untrusted DLL to that directory called vboxc.dll.
2. Create a mount point (a directory symlink) at c:\poc\dummy which redirects to c:\poc.
3. Map \\localhost\c$\poc\dummy\vboxc.dll. The SMB server will follow the mount point and open c:\poc\vboxc.dll, however from the client perspective (even though we're on the same machine) the file still thinks the original UNC path.
4. Change the mount point to c:\program files\oracle\virtualbox.  Now when accessing the UNC path the file opened will be the real vboxc.dll file which is signed and has a trusted owner.

The main reason this works is the fact that from a client perspective the filename never changes therefore the hardening code can't do much about it. The only way to tell the mapped file doesn't match the original would be to check the acual mapped image data as requesting its path using the memory manager would just return the UNC path. Ultimately I guess you probably shouldn't be trusting code on UNC paths.

Proof of Concept:

I’ve provided a PoC which will abuse the VBox COM Client loading process in source form.

Prerequisites:
Compile the supplied project in release mode using VS2015. I've only provided x64 version, it should work on x86 but I've not got anything to test it on. Then follow these steps:

1) Create the directory c:\poc and copy RunPoc.exe, NtApiDotNet.dll and the fake vboxc.dll to that directory.
2) Execute RunPoc.exe, it should print that it's successfully loaded the file. At this point DO NOT close the RunPoc executable as the file needs to stay mapped.
3) Start a VM. Each process the DLL is injected into will show a message box. This will include the protected VirtualBox.exe process.
4) When done testing hit enter in the RunPoc executable to ensure the keys get deleted.

Expected Result:
Untrusted DLL loading should fail inside a protected process.

Observed Result:
DLL is loaded into the protected process.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/42426.zip