Update July 3, 2017: FuzzySec has also previously written some info about this.
Ever since I began reverse engineering Shadow Brokers dumps [1] [2] [3], I've gotten into the habit of codenaming my projects. This trick is called Puppet Strings , and it lets you hitch a free ride into Ring 0 (kernel mode) on Windows.
Some nation-state malware, such as Backdoor.Remsec by the ProjectSauron/Strider APT and Trojan.Turla by the Turla APT, performs a similar operation. However, the traditional nation-state modus operandi involves 0-day exploitation.
But why waste 0-days when you can use kn0wn-days?
Premise
-
If you're running as an elevated admin, you're allowed to load (signed) drivers.
- Local users are almost always admins.
- UAC is known to be fundamentally broken.
-
Load any (signed) driver with a kn0wn code execution vulnerability and exploit it.
- It's a fairly obvious idea, and elementary to perform.
- Windows does not have robust certificate revocation.
- Thus, the DSE trust model is fundamentally broken!
Ordinarily, Ring 0 is forbidden unless you have an approved Extended Validation (EV) Code-Signing Certificate (out of reach for most, especially for malicious purposes). There is a "Driver Signature Enforcement" (DSE) security feature present in all modern 64-bit versions of Windows.
This enforcement can only be "officially" bypassed in two ways: attaching a kernel debugger or configuration at the advanced boot options menu. While these are common procedures for driver developers, they are highly-atypical actions for the average user.
That's right, I'm talking about simply loading high-profile vulnerable drivers like capcom.sys:
oh dear god this capcom.sys has an ioctl that disables smep and calls a provided function pointer, and sets SMEP back what even pic.twitter.com/jBCXO7YtNe
— slipstream/RoL (@TheWack0lian) September 23, 2016
Originally introduced in September 2016 as a form of video game anti-cheat, it was quickly discovered that the capcom.sys driver has an ioctl which disables Supervisor Mode Execution Prevention (SMEP) and executes a provided Ring 3 (user mode) function pointer with Ring 0 privileges. It's even kind enough to pass you a function pointer to MmGetSystemRoutineAddress(), which is basically like GetProcAddress() but for ntoskrnl.exe exports.
The unfortunate part is it can still be easily loaded and exploited to this day.
My opinion: file reputation for signed binaries should factor in cert validity period, revocation, digest algorithm, and file prevalence.
— Matt Graeber (@mattifestation) June 24, 2017
If a driver is signed with a valid timestamp, it also doesn't matter if the certificate has expired, as long as it isn't revoked. This trick is only possible because the Microsoft and root CA mechanisms for revoking driver signatures seems bad. This halfhearted approach violates the trust model that public key infrastructure is supposed to be built upon, as defined in the X.509 standard. Perhaps like UAC it is not a security boundary?
Capcom.sys has been around for almost a year, and is easily one of the most well-known and simplest driver exploits of all time.
While this driver is flagged 15/61 on VirusTotal, I have a personal list of known-vulnerable drivers that are 0/61 detection. They aren't too hard to find if you keep your eyes open to netsec news.
Proof of Concept
Code is available on GitHub at zerosum0x0/puppetstrings. To run it, you will need to independently obtain the capcom.sys driver (I don't want to deal with weird licensing issues).
Test system was Windows 10 x64 Redstone 3 (Insider pre-release), just to show the new Driver Signing Policies (and its list of exceptions) introduced in Redstone 1 do not address this issue. This works on all versions of Windows if you update the EPROCESS.ActiveProcessLinks offset.
1: kd> dt !_EPROCESS ActiveProcessLinks +0x2e8 ActiveProcessLinks : _LIST_ENTRY
For the PoC, I had to do something relatively malicious to get the point across. Getting to Ring 0 with this technique is simple, doing something interesting once there is more difficult (e.g. we can already load drivers, the usual SYSTEM shell can be obtained through less dangerous methods).
I load capcom.sys, pass it a function which performs the old rootkit technique of unlinking the current process from the EPROCESS.ActiveProcessLinks circularly-linked list, and then unload capcom.sys. This methodology is instant and makes the current process not show up in user mode tools like tasklist.exe.
static void rootkit_unlink(PEPROCESS pProcess) { static const DWORD WIN10_RS3_OFFSET = 0x2e8; PLIST_ENTRY plist = (PLIST_ENTRY)((LPBYTE)pProcess + WIN10_RS3_OFFSET); *((DWORD64*)plist->Blink) = (DWORD64)plist->Flink; *((DWORD64*)plist->Flink + 1) = (DWORD64)plist->Blink; plist->Flink = (PLIST_ENTRY) &(plist->Flink); plist->Blink = (PLIST_ENTRY) &(plist->Flink); }
Of course, doing this in a modern rootkit is foolish, as PatchGuard has at least 4 different process list checks (CRITICAL_STRUCTURE_CORRUPTION Bug Check Arg4 = 4, 5, 1A, and 1B). But you can get experimental and think of something else cool to do, as you enjoy all of the freedoms Ring 0 brings.
DOUBLEPULSAR showed us there's a lot of creative ideas to run in the kernel, even outside of a driver context. DSEFix exploits the same vulnerable VirtualBox driver used by Trojan.Turla to disable Driver Signature Enforcement entirely. It's even possible to use some undocumented features to create a reflectively-loaded driver, if one were so inclined...
If you want to learn more about techniques like this, come to the Advanced Windows Post-Exploitation / Malware Forward Engineering DEF CON 25 workshop.