Posted by James Forshaw, Project Zero
Processes on Windows are securable objects, which prevents one user logged into a Windows machine from compromising anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r user’s processes. This is a pretty important security feature, at least from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 perspective of a non-administrator user. The security prevents a non-administrator user from compromising cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 integrity of an arbitrary process. This security barrier breaks down when trying to protect against administrators, specifically administrators with Debug privilege, as enabling this privilege allows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 administrator to open any process regardless of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 security applied to it.
There are cases where applications or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 operating system want to actively defend processes from users such as administrators or even, in some cases, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same user as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 running process who’d normally have full access. Protecting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 processes is a pretty hard challenge if done entirely from user mode applications. Therefore many solutions use kernel support to perform cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protection. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 majority of cases cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se sorts of techniques still have flaws, which we can exploit to compromise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “protected” process.
This blog post will describe cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation of Oracle’s VirtualBox protected process and detail three different, but now fixed, ways of bypassing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protection and injecting arbitrary code into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process. The techniques I’ll present can equally be applied to similar implementations of “protected” processes in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r applications.
Oracle VirtualBox Process Hardening
Protecting processes entirely in user mode is pretty much impossible, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are just too many ways of injecting content into a process. This is especially true when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process you’re trying to protect is running under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same context as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user you’re trying to block. An attacker could, for example, open a handle to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process with PROCESS_CREATE_THREAD access and directly inject a new thread. Or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y could open a thread in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process with THREAD_SET_CONTEXT access and directly change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Instruction Pointer to jump to an arbitrary location. These are just cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 direct attacks. The attacker could also modify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 registry or environment cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process is running under, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n force cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process to load arbitrary COM objects, or Windows Hooks. The list of possible modifications is almost endless.
Therefore, VirtualBox (VBOX) enlists cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 help of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel to try to protect its processes. The source code refers to this as Process Hardening. VBOX tries to protect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 processes from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same user cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process is running under. A detailed rationale and technical overview is provided in source code comments. The TL;DR; is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protection gates access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VBOX kernel drivers, which due to design have a number of methods which can be used to compromise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel, or at least elevate privileges. This is why VBOX tries to prevent cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current user compromising cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process, getting access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VBOX kernel driver would be a route to Kernel or System privileges. As we’ll see though while some protections also prevent administrators compromising cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 processes that’s not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 aim of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardening code.
Multiple examples of issues with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver and protection from device access were discovered by my colleague Jann in VBOX on Linux. On Linux, VBOX limits access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VBOX driver to root only, and uses SUID binaries to allow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VBOX user processes to get access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver before dropping privileges. On Windows instead of SUID binaries cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VBOX driver uses kernel APIs to try to stop users and administrators opening protected processes and injecting code.
The core of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel component is in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Support\win\SUPDrv-win.cpp file. This code registers with two callback mechanisms supported by modern Windows kernels:
- PsSetCreateProcessNotifyRoutineEx - Driver is notified when a new process is created.
- ObRegisterCallback - Driver is notified when Process and Thread handles are created or duplicated.
The notification from PsSetCreateProcessNotifyRoutineEx is used to configure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protection structures for a new process. When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process subsequently tries to open a handle to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VBOX driver cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardening will only permit access after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following verification steps are performed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call to supHardenedWinVerifyProcess:
- Ensure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are no debuggers attached to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process.
- Ensure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is only a single thread in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process, which should be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 one opening cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver to prevent in-process races.
- Ensure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are no executable memory pages outside of a small set of permitted DLLs.
- Verify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signatures of all loaded DLLs.
- Check cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 main executable’s signature and that it is of a permitted type of executable (e.g. VirtualBox.exe).
Signature verification in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel is done using custom runtime code compiled into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver. Only a limited set of Trusted Roots are permitted to be verified at this step, primarily Microsoft’s OS and Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticode certificates as well as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Oracle certificate that all VBOX binaries are signed with. You can find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 list of permitted certificates in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source repository.
The ObRegisterCallback notification is used to limit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 maximum access any ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r user process on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system can be granted to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protected process. The ObRegisterCallback API was designed for Anti-Virus to protect processes from being injected into or terminated by malicious code. VBOX uses a similar approach and limits any handle to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protected process to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following access rights:
- PROCESS_TERMINATE
- PROCESS_VM_READ
- PROCESS_QUERY_INFORMATION
- PROCESS_QUERY_LIMITED_INFORMATION
- PROCESS_SUSPEND_RESUME
- DELETE
- READ_CONTROL
- SYNCHRONIZE
The permitted access rights give cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user most of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 typical rights cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y’d expect, such as being able to read memory, synchronize to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process and terminate it but does not allow injecting new code into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process. Similarly, access to threads is restricted to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following access rights to prevent modification of a thread’s context or similar attacks.
- THREAD_TERMINATE
- THREAD_GET_CONTEXT
- THREAD_QUERY_INFORMATION
- THREAD_QUERY_LIMITED_INFORMATION
- DELETE
- READ_CONTROL
- SYNCHRONIZE
We can verify this access limitation by opening cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VirtualBox process and one of its threads and see what access rights we’re granted. For example cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following picture highlights cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process and thread granted access.
While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel callbacks prevent direct modification of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process as well as a user trying to compromise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 integrity of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process at startup cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y do very little against runtime DLL injection such as through COM. The hardening implementation needs to decide on what modules it’ll allow to be loaded into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process. The decision, fundamentally, is based on Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticode code signing.
There are mitigation options to enable loading only Microsoft signed binaries (such as PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY). However, this policy isn’t very flexible. Therefore, protected VBOX processes install hooks to a couple of internal functions in user-mode to verify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 integrity of any DLL which is being loaded into memory. The hooked functions are:
- LdrLoadDll - Called to load a DLL into memory.
- NtCreateSection - Called to create an Image Section object for a PE file on disk.
- LdrRegisterDllNotification - This is a quasi-officially supported callback which notifies cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 application when a new DLL is loaded or unloaded.
These hooks expand cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 permitted set of signed DLLs which can be loaded. The kernel signature verification is okay for bootstrapping cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process as only Oracle and Microsoft code should be present. However, when it comes to running a non-trivial application ( VirtualBox.exe is certainly non-trivial) you’re likely to need to load third-party signed code such as GPU drivers. As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hooks are in user mode it’s easier to call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system WinVerifyTrust API which will verify certificate chains using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system certificate stores as well as handling cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 verification of files signed in a Catalog file.
If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL being loaded doesn’t meet VBOX’s expected criteria for signing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user-mode hooks will reject loading that DLL. VBOX still doesn't completely trust cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user; WinVerifyTrust will chain certificates back to a root certificate in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user’s CA certificates. However, VBOX will only trust system CA certificates. As a non-administrator cannot add a new trusted root certificate to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system’s list of CA certificates this should severely limit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 injection of malicious DLLs.
You can get a real code signing certificate which should also be trusted, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 assumption is malicious code wouldn’t want to go down that route. Even if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code is signed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 loader also checks that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL file is owned by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TrustedInstaller user. This is checked in supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar. A normal user should not be able to change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 owner of a file to anything but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365mselves, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore it should limit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 impact of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 behavior to allow any signed file to load.
The VBOX code does have a function which is supposed to restrict what certificates are permitted supR3HardenedWinIsDesiredRootCA as roots. In official builds cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function’s whitelist of specific CAs is commented out. There’s a blacklist of certificates, however, unless your company is called “U.S. Robots and Mechanical Men, Inc” cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 blacklist won’t affect you.
Even with all this protection cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process isn’t secure against an administrator. While an administrator can’t bypass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 security on opening cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can install a local machine Trusted Root CA certificate and sign a DLL, set its owner and force it to be loaded. This will bypass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image verification and load into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 verified VBOX process.
In summary cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VBOX hardening is attempting to provide cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following protections:
- Ensure that no code is injected into protected binaries during initialization.
- Prevent user processes from opening “writable” handles to protected processes or threads which would allow arbitrary code injection.
- Prevent injection of untrusted DLLs through normal loading routes such as COM.
This whole process is likely to have some bugs and edge cases. There’s so many different verification checks which must all fit togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r. So, assuming we don’t want to get a code signing certificate and we don’t have administrator rights how can we get arbitrary code running inside a protected VBOX process? We’ll focus primarily on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 third protection in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 list, as this is perhaps cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most complex part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protection and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore is likely to have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most issues.
Exploiting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Chain-of-Trust in COM Registration
The first bug I’m going to describe was fixed as CVE-2017-3563 in VBOX version 5.0.38/5.1.20. This issue exploits cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 chain-of-trust for DLL loading to trick VBOX into loading Microsoft signed DLLs which just happen to allow untrusted arbitrary code execution.
If you run Process Monitor against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protected VBOX process you’ll notice that it uses COM, specifically it uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VirtualBoxClient class which is implemented in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VBoxC.dll COM server.
The nice thing about COM server registration, at least from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 perspective of an attacker, is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 registration for a COM object can be in one of two places, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user’s registry hive, or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local machine registry hive. For reasons of compatibility cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user’s hive is checked first, before falling back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local machine hive. Therefore it’s possible to override a COM registration with a normal user’s permission, so when an application tries to load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 designated COM object cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 application will instead load whatever DLL we’ve overridden it with.
Hijacking COM objects is not a new technique, it’s been known for many years especially for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 purposes of Malware persistence. It’s seen a resurgence of late because of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 renewed interest in all things COM. However, it’s rare that COM hijacking is of importance for elevation of privilege outside of UAC bypasses.
As an aside, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 connection between UAC and COM hijacking is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 COM runtime actively tries to prevent cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hijack being used as an EoP route by disabling certain User registry lookups if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current process is elevated. Of course it wasn’t always successful. This behavior only makes sense if you view UAC through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 prism of it being a defendable security boundary, which Microsoft categorically claim it’s not and never was. For example this blog post from early 2007 specifically states this behavior is to prevent Elevation of Privilege. I think cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 COM lookup behavior is one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clearest indicators that UAC was originally designed to be a security boundary. It failed to meet cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 security bar and so was famously retconned into helping “developers” write better code.
If we could replace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 COM registration with our own code we should be able to get code execution inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardened process. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ory all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardening signing checks should stop us from loading untrusted code. In research, it’s always worth trying something which you believe should fail just in case as sometimes you get a nice surprise. At minimum it’ll give you insight into how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protection really works. I registered a COM object to hijack cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VirtualBoxClient class in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user’s hive and pointed it at an unsigned DLL (Full Disclosure, I used an admin account to tweak cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Owner to TrustedInstaller just to test). When I tried to start a Virtual Machine I got cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following dialog.
It’s possible that I just made a mistake in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 COM registration, however testing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 COM object in a separate application worked as expected. Therefore this error is likely a result of failing to load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL. Fortunately, VBOX is generous and enables by default a log of all Process Hardening events. It’s named VBoxHardening.log and is located in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Logs folder in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Virtual Machine you tried to start. Searching for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL we find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following entries (heavily modified for brevity):
supHardenedWinVerifyImageByHandle: -> -22900 (c:\dummy\testdll.dll)
supR3HardenedScreenImage/LdrLoadDll: c:\dummy\testdll.dll: Not signed.
supR3HardenedMonitor_LdrLoadDll: rejecting 'c:\dummy\testdll.dll'
supR3HardenedMonitor_LdrLoadDll: returns rcNt=0xc0000190
So clearly our test DLL isn’t signed and so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 LdrLoadDll hook rejects it. The LdrLoadDll hook returns an error code which propagates back up to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 COM DLL loader, which results in COM thinking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 class doesn’t exist.
While it’s not surprising that it wasn’t as simple as just specifying our own DLL (and don’t forget we cheated with setting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Owner) it at least gives us hope as this result means cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VBOX process will use our hijacked COM registration. All we need cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore is a COM object which meets cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following criteria:
- It’s signed by a trusted certificate.
- It’s owned by TrustedInstaller.
- When loaded will do something that allows for arbitrary code execution in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process.
Criteria 1 and 2 are easy to meet, any Microsoft COM object on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system is signed by a trusted certificate (one of Microsoft’s publisher certificates) and is almost certainly owned by TrustedInstaller. However, criteria 3 would seem much more difficult to meet, a COM object is usually implemented inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL and we can’t modify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL itself, ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise it would no longer be signed. It just so happens that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is a Microsoft signed COM object installed by default which will allow us to meet criteria 3, Windows Script Components (WSC).
WSC, also sometimes called Scriptlets are also having a good run at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 moment. They can be used as an AppLocker bypass as well as being loaded from HTTP URLs. What’s of most interest in this case is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can also be registered as a COM object.
A registered WSC consists of two parts:
- The WSC runtime scrobj.dll which acts as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 in-process COM server.
- A file which contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Scriptlet in a compatible scripting language.
When an application tries to load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 registered class scrobj.dll gets loaded into memory. The COM runtime requests a new object of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 required class which causes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WSC runtime to go back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 registry to lookup cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 URL to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation Scriptlet file. The WSC runtime cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n loads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Scriptlet file and executes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 embedded script contained in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file in-process. The key here is that as long as scrobj.dll (and any associated script language libraries such as JScript.dll) are valid signed DLLs from VBOX’s perspective cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 script code will run as it can never be checked by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardening code. This would get arbitrary code running inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardened process. First let’s check that scrobj.dll is likely to be allowed to be loaded by VBOX. The following screenshot shows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL is both signed by Microsoft and is also owned by TrustedInstaller.
So what does a valid Scriptlet file look like? It’s a simple XML file, I’m not going to go into much detail about what each XML element means, ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than to point out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 script block which will execute arbitrary JScript code. In this case all this Scriptlet will do when loaded is start cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Calculator process.
<>
description ="Component"
progid="Component"
version="1.00"
classid="{DD3FC71D-26C0-4FE1-BF6F-67F633265BBA}"
/>
<> language = "JScript" >
new ActiveXObject('WScript.Shell').Exec('calc');
]]>
If you’re written much code in JScript or VBScript you might now notice a problem, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se languages can’t do that much unless it’s implemented by a COM object. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 example Scriptlet file we can’t create a new process without loading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 WScript.Shell COM object and calling its Exec method. In order to talk to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VBOX driver, which is whole purpose of injecting code in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first place, we’d need a COM object which gives us that functionality. We can’t implement cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code in anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r COM object as that wouldn’t pass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image signing checks we’re trying to bypass. Of course, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s always memory corruption bugs in scripting engines but, as everyone already knows by now, I’m not a fan of exploiting memory corruptions so we need some ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r way of getting fully arbitrary code execution. Time to bring in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 big guns, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 .NET Framework.
The .NET runtime loads code into memory using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 normal DLL loading routines. We can’t cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore load a .NET DLL which isn’t signed into memory as that would still get caught by VBOX’s hardening code. However, .NET does support loading arbitrary code from an in-memory array using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Assembly::Load method and once loaded this code can basically act as if it was native code, calling arbitrary APIs and inspecting/modifying memory. As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 .NET framework is signed by Microsoft all we need to do is somehow call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Load method from our Scriptlet file and we can get full arbitrary code running inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process.
Where do we even start on achieving this goal? From a previous blog post it’s possible to expose .NET objects as COM objects through registration and by abusing Binary Serialization we can load arbitrary code from a byte array. Many core .NET runtime classes are automatically registered as COM objects which can be loaded and manipulated by a scripting engine. The big question can now be asked, is BinaryFormatter exposed as a COM object?
Why, yes it is. BinaryFormatter is a .NET object that a scripting engine can load and interact with via COM. We could now take cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 final binary stream from my previous post and execute arbitrary code from memory. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous blog post cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 execution of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 untrusted code had to occur during deserialization, in this case we can interact with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 results of deserialization in a script which can make cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 serialization gadgets we need much simpler.
In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end I chose to deserialize a Delegate object which when executed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 script engine would load an Assembly from memory and return cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Assembly instance. The script engine could cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n instantiate an instance of a Type in that Assembly and run arbitrary code. It does sound simple in principle, in reality cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are a number of caveats. Racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than bog down this blog post with more detail than necessary cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 tool I used to generate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Scriptlet file, DotNetToJScript is available so you can read how it works yourself. Also cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PoC is available on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issue tracker here. The chain from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JScript component to being able to call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VBOX driver looks something like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following:
I’m not going to go into what you can now do with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VBOX driver once you’ve got arbitrary code running cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardened process, that’s certainly a topic for anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r post. Although you might want to look at one of Jann’s issues which describes what you might do on Linux.
How did Oracle fix cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issue? They added a blacklist of DLLs which are not allowed to be loaded by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardened VBOX process. The only DLL currently in that list is scrobj.dll. The list is checked after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 verification of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file has taken place and covers both cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current filename as well as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 internal Original Filename in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 version resources. This prevents you just renaming cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file to something else, as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 version resources are part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signed PE data and so cannot be modified without invalidating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signature. In fairness to Oracle I’m not sure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re was any ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r sensible way of blocking this attack vector ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than a DLL blacklist.
Exploiting User-Mode DLL Loading Behavior
The second bug I’m going to describe was fixed as CVE-2017-10204 in VBOX version 5.1.24. This issue exploits cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 behavior of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Windows DLL loader and some bugs in VBOX to trick cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardening code to allow an unverified DLL to be loaded into memory and executed.
While this bug doesn’t rely on exploiting COM loading as such, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 per-user COM registration is a convenient technique to get LoadLibrary called with an arbitrary path. Therefore we’ll continue to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 technique of hijacking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VirtualBoxClient COM object and just use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 in-process server path as a means to load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL.
LoadLibrary is an API with a number of well known, but strange behaviors. One of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 more interesting from our perspective is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 behavior with filename extensions. Depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 extension cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 LoadLibrary API might add or remove cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 extension before trying to load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file. I can summarise it in a table, showing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file name as passed to LoadLibrary and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file it actually tries to load.
Original File Name
|
Loaded File Name
|
c:\test\abc.dll
|
c:\test\abc.dll
|
c:\test\abc
|
c:\test\abc.dll
|
c:\test\abc.blah
|
c:\test\abc.blah
|
c:\test\abc.
|
c:\test\abc
|
I’ve highlighted in green cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 two important cases. These are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cases where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 filename passed into LoadLibrary doesn’t match cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 filename which eventually gets loaded. The problem for any code trying to verify a DLL file before loading it is CreateFile doesn’t follow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se rules so in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 highlighted cases if you opened cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file for signature verification using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original file name you’d verify a different file to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 one which eventually gets loaded.
In Windows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s usually a clear separation between Kernel32 code, which tends to deal with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 many weird behaviors Win32 has built up over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 years and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “clean” NT layer exposed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel through NTDLL. Therefore as LoadLibrary is in Kernel32 and LdrLoadDll (which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardening hooks) is in NTDLL cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n this weird extension behavior would be handled in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 former. Let’s look at a very simplified version of LoadLibrary to see if that’s cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case:
HMODULE LoadLibrary(LPCWSTR lpLibFileName)
{
UNICODE_STRING DllPath;
HMODULE ModuleHandle;
ULONG Flags = // Flags;
RtlInitUnicodeString(&DllPath, lpLibFileName);
if (NT_SUCCESS(LdrLoadDll(DEFAULT_SEARCH_PATH,
&Flags, &DllPath, &ModuleHandle))) {
return ModuleHandle;
}
return NULL;
}
{
UNICODE_STRING DllPath;
HMODULE ModuleHandle;
ULONG Flags = // Flags;
RtlInitUnicodeString(&DllPath, lpLibFileName);
if (NT_SUCCESS(LdrLoadDll(DEFAULT_SEARCH_PATH,
&Flags, &DllPath, &ModuleHandle))) {
return ModuleHandle;
}
return NULL;
}
We can see in this code that for all intents and purposes LoadLibrary is just a wrapper around LdrLoadDll. While it’s really more complex than that in reality cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 takeaway is that LoadLibrary does not modify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path it passes to LdrLoadDll in any way ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than converting it to a UNICODE_STRING. Therefore perhaps if we specify a DLL to load without an extension VBOX will check cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 extension-less file for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signature but LdrLoadDll will instead load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 .DLL extension.
Before we can test that we’ve got anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r problem to deal with, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 requirement that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file is owned by TrustedInstaller. For cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file we want VBOX to signature check all we need to do is give an existing valid, signed file a different filename. This is what hard links were created for; we can create a different name in a directory we control which actually links to a system file which is signed and also maintains its original security descriptor including cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 owner. The trouble with hard links is, as I described almost 2 years ago in a blog post, while Windows supports creating links to system files you can’t write to, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Win32 APIs, and by extension cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 easy to access “mklink” command in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CMD shell require cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file be opened with FILE_WRITE_ATTRIBUTES access. Instead of using anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r application to create cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 link we’ll just copy cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file, however cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 copy will no longer have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original security descriptor and so it’ll no longer be owned by TrustedInstaller. To get around that let’s look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 checking code to see if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s a way around it.
The main check for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Owner is in supHardenedWinVerifyImageByLdrMod. Almost cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first thing that function does is call supHardNtViCheckIsOwnedByTrustedInstallerOrSimilar which we saw earlier. However as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 comments above cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 check indicate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code will also allow files under System32 and WinSxS directories to not be owned by TrustedInstaller. This is a bus sized hole in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 point of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 check, as all we need is one writeable directory under System32. We can find some by running cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Get-AccessibleFile cmdlet in my NtObjectManager PS module.
There are plenty to choose from, we’ll just pick cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Tasks folder as it’s guaranteed to always be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re. So cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit should be as follows:
- Copy a signed binary to %SystemRoot%\System32\Tasks\Dummy\ABC
- Copy an unsigned binary to %SystemRoot%\System32\Tasks\Dummy\ABC.DLL
- Register a COM hijack pointing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 in-process server to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signed file path from 1.
If you try to start a Virtual Machine you’ll find that this trick works. The hardening code checks cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ABC file for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signature, but LdrLoadDll ends up loading ABC.DLL. Just to check we didn’t just exploit something else let’s check cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardening log:
\..\Tasks\dummy\ABC: Owner is not trusted installer
\..\Tasks\dummy\ABC: Relaxing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TrustedInstaller requirement for this DLL (it's in system32).
supHardenedWinVerifyImageByHandle: -> 0 (\..\Tasks\dummy\ABC)
supR3HardenedMonitor_LdrLoadDll: pName=c:\..\tasks\dummy\ABC [calling]
The first two lines indicate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bypass of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Owner check as we expected. The second two indicate it’s verified cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ABC file and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore will call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original LdrLoadDll, which ultimately will append cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 extension and try to load ABC.DLL instead. But, wait, how come cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r checks in NtCreateSection and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 loader callback don’t catch loading a completely different file? Let’s search for any instance of ABC.DLL in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardening log to find out:
\..\Tasks\dummy\ABC.dll: Owner is not trusted installer
\..\Tasks\dummy\ABC.dll: Relaxing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TrustedInstaller requirement for this DLL (it's in system32).
supHardenedWinVerifyImageByHandle: -> 22900 (\..\Tasks\dummy\ABC.dll)
supR3HardenedWinVerifyCacheInsert: \..\Tasks\dummy\ABC.dll
supR3HardenedDllNotificationCallback: c:\..\tasks\dummy\ABC.DLL
supR3HardenedScreenImage/LdrLoadDll: cache hit (Unknown Status 22900) on \...\Tasks\dummy\ABC.dll
Again cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first two lines indicate we bypassed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Owner check because of our file's location. The next line, supHardenedWinVerifyImageByHandle is more interesting however. This function verifies cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image file. If you look back in this blog at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 earlier log of this check you’ll find it returned cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result -22900, which was considered an error. However in this case it’s returning 22900, which as VBOX is treating any result >= 0 as success cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardening code gets confused and assumes that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file is valid. The negative error code is VERR_LDRVI_NOT_SIGNED in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source code, whereas cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 positive “success” code is VINF_LDRVI_NOT_SIGNED.
This seems to be a bug in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 verification code when calling code in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL Loader Lock, such as in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NtCreateSection hook. The code can’t call WinVerifyTrust in case it tries to load anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r DLL, which would cause a deadlock. What would normally happen is VINF_LDRVI_NOT_SIGNED is returned from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 internal signature checking implementation. That implementation can only handle files with embedded signatures, so if a file isn’t signed it returns that information code to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 verification code to check if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file is catalog signed. What’s supposed to happen is WinVerifyTrust is called and if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file is still not signed it returns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 error code, however as WinVerifyTrust can’t be called due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lock cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 information code gets propagated to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller which assumed it’s a success code.
The final question is why cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 final Loader Callback doesn’t catch cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unsigned file? VBOX implements a signed file cache based on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path to avoid checking a file multiple times. When cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call to supHardenedWinVerifyImageByHandle was taken to be a success cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 verifier called supR3HardenedWinVerifyCacheInsert to add a cache entry for this path with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “success” code. We can see that in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Loader Callback it tries to verify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file but gets back a “success” code from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cache so assumes everything's okay, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 loading process is allowed to complete.
Quite a complex set of interactions to get code running. How did Oracle fix this issue? They just add cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL extension if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no extension present. They also handle cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 filename has a trailing period (which would be removed when loading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL).
Exploiting Kernel-Mode Image Loading Behavior
The final bug I’m going to describe was fixed as CVE-2017-10129 in VBOX version 5.1.24. This isn’t really a bug in VBOX as much as it’s an unexpected behavior in Windows.
Through all this it’s worth noting that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s an implicit race condition in what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardening code is trying to do, specifically if you could change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 verification point and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 point where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file is mapped. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ory you could do this to VBOX but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 timing window is somewhat short. You could use OPLOCKs and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 like but it’s a bit of a pain, instead it’d be nice to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 TOCTOU attack for free.
Let’s look at how image files are handled in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel. Mapping an image file on Windows is expensive, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OS doesn’t use position independent code and so can’t just map cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL into memory as a simple file. Instead cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL must be relocated to a specific memory address. This requires modifying pages of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL file to ensure any pointers are correctly fixed up. This is even more important when you bring ASLR into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mix as ASLR will almost always force a DLL to be relocated from its base address. Therefore, Windows caches an instance of an image mapping whenever it can, this is why cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 load address of a DLL doesn’t change between processes on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same system, it’s using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same cached image section.
The caching is actually in part under control of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 filesystem driver. When a file is opened cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO manager will allocate a new instance of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 FILE_OBJECT structure and pass it to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IRP_MJ_CREATE handler for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver. One of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fields that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n initialize is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SectionObjectPointer. This is an instance of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SECTION_OBJECT_POINTERS structure, which looks like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following:
struct SECTION_OBJECT_POINTERS {
PVOID DataSectionObject;
PVOID SharedCacheMap;
PVOID ImageSectionObject;
};
PVOID DataSectionObject;
PVOID SharedCacheMap;
PVOID ImageSectionObject;
};
The fields cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365mselves are managed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Cache manager, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 structure itself must be allocated by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 File System driver. Specifically cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 allocation should be one per-file in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 filesystem; while each open instance of a specific file will have unique FILE_OBJECT instances cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SectionObjectPointer should be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same. This allows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Cache manager to fill in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 different fields and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n reuse cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m if anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r instance of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same file tries to be mapped.
The important field here is ImageSectionObject which contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cached data for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mapped image section. I’m not going to delve into detail of what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ImageSectionObject pointer contains as it’s not really relevant. The important thing is if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SectionObjectPointer and by extension cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ImageSectionObject pointers are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same for a FILE_OBJECT instance cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n mapping that file as an image will map cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same cached image mapping. However, as ImageSectionObject pointer is not used when reading from a file it doesn’t follow that what’s actually cached still matches what’s on disk.
Trying to desynchronize cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file data from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SectionObjectPointer seems to be pretty tricky with an NTFS volume, at least without administrator privileges. One scenario where you can do this desynchronization is via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SMB redirector when accessing network shares. The reason is pretty simple, it’s cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local redirector’s responsibility to allocate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SectionObjectPointer structure when a file is opened on a remote server. As far as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 redirector’s concerned if it opens cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file \Share\File.dll on a server twice cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n it’s cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same file. There’s no real ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r information cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 redirector can use to verify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 identity of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file, it has to guess. Any property you can think of, Object ID, Modification Time can just be a lie. You could easily modify a copy of SAMBA to do this lying for you. The redirector also can’t lock cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file and ensure it stays locked. So it seems cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 redirector just doesn’t bocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r with any of it, if it looks like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same file from its perspective it assumes it’s fine.
However this is only for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SectionObjectPointer, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller wants to read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SMB redirector will go out to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server and try to read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current state of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file. Again this could all be lies, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server could return any data it likes. This is how we can create a desynchronization; if we map an image file from a SMB server, change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying file data cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n reopen cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file and map cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 image again cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mapped image will be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cached one, but any data read from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file will be what’s current on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server. This way we can map an untrusted DLL first, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n replace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file data with a signed, valid file (SMB supports reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 owner of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file, so we can spoof TrustedInstaller), when VBOX tries to load it it will verify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signed file but map cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cached untrusted image and it will never know.
Having a remote server isn’t ideal, however we can do everything we need by using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local loopback SMB server and access files via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 admin shares. Contrary to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir names admin shares are not limited to administrators if you’re coming from localhost. The key to getting this to work is to use a Directory Junction. Junctions are resolved on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 redirector client knows nothing about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. Therefore as far as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client is concerned if it opens cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file \\localhost\c$\Dir\File.dll once, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n reopens cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same file cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se could be two completely different files as shown in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following diagram:
Fortunately, one thing which should be evident from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous two issues is that VBOX’s hardening code doesn’t really care where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DLL is located as long as it meets its two criteria, it’s owned by TrustedInstaller and it’s signed. We can point cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 COM hijack to a SMB share on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 local system. Therefore we can perform cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack as follows:
- Set up a junction on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 C: drive pointing at a directory containing our untrusted file.
- Map cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 junction over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 c$ admin share using LoadLibrary, do not release cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mapping until cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit is complete.
- Change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 junction to point to anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r directory with a valid, signed file with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same name as our untrusted file.
- Start VBOX with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 COM hijack pointing at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file. VBOX will read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file and verify it’s signed and owned by TrustedInstaller, however when it maps it cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cached, untrusted image section will be used instead.
So how did Oracle fix this? They now check that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mapped file isn’t on a network share by comparing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 prefix \Device\Mup.
Conclusions
The implementation of process hardening in VirtualBox is complex and because of that it is quite error prone. I’m sure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r ways of bypassing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protection, it just requires people to go looking. Of course none of this would be necessary if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y didn’t need to protect access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 VirtualBox kernel driver from malicious use, but that’s a design decision that’s probably going to be difficult to fix in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 short term.