Windows: CiSetFileCache TOCTOU CVE-2017-11830 Variant WDAC Security Feature Bypass
Platform: Windows 10 1803, 1709 (should include S-Mode but not tested)
Class: Security Feature Bypass
While the TOCTOU attack against cache signing has been mitigated through NtSetCachedSigningLevel it’s possible to reach the same code via NtCreateSection leading to circumventing WDAC policies and CIG/PPL.
I'm reporting this as you've fixed the previous issues (cases 43036 and 40101) so I'm making an assumption you'd also fix this one. The previous issues allowed a unprivileged caller to exploit a race condition in the CiSetFileCache kernel function by calling NtSetCachedSigningLevel. These issues should now be fixed. During my research into PPL/PP bypasses I noticed that the cache will also be written during the initial creation of an image section, when the process is running with an increased section signing level. This is presumably to allow the kernel to cache the signature automatically. This is an issue because it’s possible to create an image section with a writable (and executable) handle to the file and no part of CI then checks whether the caller has write access. It’s possible to have an elevated section signing level by enabling the ProcessSignaturePolicy process mitigation policy, it’s not required to be in a PPL. In fact, while I’ve not tested it, it’s possible that just running inside a process on Windows 10 S-Mode would be sufficient as the section signing level should be elevated for WDAC.
So to exploit this we can do the following:
1. Elevated the section signing level of the current process using SetProcessMitigationPolicy or just running in a WDAC/CIG process.
2. Copy a valid signed file to a known name then open a writable and executable handle to that file.
3. Set an oplock on a known catalog file which will be checked
4. Call NtCreateSection with the handle requesting SEC_IMAGE.
5. Wait for oplock to fire, rewrite the file with an untrusted binary, then release oplock.
6. Close section and file handles. The cache should have been applied to the untrusted file.
Perhaps CI should check whether the file handle has been opened for write access and not write out the cache in those cases as realistically creating an image section from a writable handle should be an unusual operation. The normal loader process opens the handle only for read/execute.
Proof of Concept:
I’ve provided a PoC as a C# project. It will allow you to “cache sign” an arbitrary executable. To test on S-Mode you’ll need to sign the PoC (and the NtApitDotNet.dll assembly) so it’ll run. It copies notepad to a file, attempts to verify it but uses an oplock to rewrite the contents of the file with the untrusted file before it can set the kernel EA.
1) Compile the C# project. It will need to grab the NtApiDotNet v1.1.15 package from NuGet to work.
2) Execute the PoC passing the path to an unsigned file and to the output “cache signed” file, e.g. poc unsigned.exe output.exe. Make sure the output file is on a volume which supports cached signing level such as the main boot volume.
3) You should see it print the signing level, if successful.
4) You should now be able to execute the unsigned file, bypassing the security policy enforcement.
NOTE: If it prints an exception then the exploit failed. The opened catalog files seemed to be cached for some unknown period of time after use so if the catalog file I’m using for a timing signal is already open then the oplock is never broken. Just rerun the poc which will pick a different catalog file to use. Also the output file must be on to a NTFS volume with the USN Change Journal enabled as that’s relied upon by the signature level cache code. Best to do it to the boot drive as that ensures everything should work correctly.
Access denied or at least an error setting the cached signing level.
The signing level cache is applied to the file with no further verification. You can now execute the file as if it was signed.
Proof of Concept: