Tuesday, March 27, 2018

Total Meltdown?

Did you think Meltdown was bad? Unprivileged applications being able to read kernel memory at speeds possibly as high as megabytes per second was not a good thing.

Meet the Windows 7 Meltdown patch from January. It stopped Meltdown but opened up a vulnerability way worse ... It allowed any process to read the complete memory contents at gigabytes per second, oh - it was possible to write to arbitrary memory as well.

No fancy exploits were needed. Windows 7 already did the hard work of mapping in the required memory into every running process. Exploitation was just a matter of read and write to already mapped in-process virtual memory. No fancy APIs or syscalls required - just standard read and write!

Accessing memory at over 4GB/s, dumping to disk is slower due to disk transfer speeds.

How is this possible?
In short - the User/Supervisor permission bit was set to User in the PML4 self-referencing entry. This made the page tables available to user mode code in every process. The page tables should normally only be accessible by the kernel itself.

The PML4 is the base of the 4-level in-memory page table hierarchy that the CPU Memory Management Unit (MMU) uses to translate the virtual addresses of a process into physical memory addresses in RAM. For more in-depth information about paging please have a look at Getting Physical: Extreme abuse of Intel based Paging Systems - Part 1 and Part 2.

PML4 self-referencing entry at offset 0xF68 with value 0x0000000062100867.

Windows have a special entry in this topmost PML4 page table that references itself, a self-referencing entry. In Windows 7 the PML4 self-referencing is fixed at the position 0x1ED, offset 0xF68 (it is randomized in Windows 10). This means that the PML4 will always be mapped at the address: 0xFFFFF6FB7DBED000 in virtual memory. This is normally a memory address only made available to the kernel (Supervisor). Since the permission bit was erroneously set to User this meant the PML4 was mapped into every process and made available to code executing in user-mode.

"kernel address" memory addresses mapped in every process as user-mode read/write pages.

Once read/write access has been gained to the page tables it will be trivially easy to gain access to the complete physical memory, unless it is additionally protected by Extended Page Tables (EPTs) used for Virtualization. All one has to do is to write their own Page Table Entries (PTEs) into the page tables to access arbitrary physical memory.

The last '7' in the PML4e 0x0000000062100867 (from above example) indicates that bits 0, 1, 2 are set, which means it's Present, Writable and User-mode accessible as per the description in the Intel Manual.

Excerpt from the Intel Manual, if bit 2 is set to '1' user-mode access are permitted.

Can I try this out myself?
Yes absolutely. The technique has been added as a memory acquisition device to the PCILeech direct memory access attack toolkit. Just download PCILeech and execute it with device type: -device totalmeltdown on a vulnerable Windows 7 system.

Dump memory to file with the command: pcileech.exe dump -out memorydump.raw -device totalmeltdown -v -force .

If you have the Dokany file system driver installed you should be able to mount the running processes as files and folders in the Memory Process File System - with the virtual memory of the kernel and the processes as read/write.

To mount the processes issue the command: pcileech.exe mount -device totalmeltdown .

Please remember to re-install your security updates if you temporarily uninstall the latest one in order to test this vulnerability.

A vulnerable system is "exploited" and the running processes are mounted with PCILeech.
Process memory maps and PML4 are accessed.

Is my system vulnerable?
Only Windows 7 x64 systems patched with the 2018-01 or 2018-02 patches are vulnerable. If your system isn't patched since December 2017 or if it's patched with the 2018-03 2018-03-29 patches or later it will be secure.

Other Windows versions - such as Windows 10 or 8.1 are completely secure with regards to this issue and have never been affected by it.

I discovered this vulnerability just after it had been patched in the 2018-03 Patch Tuesday. I have not been able to correlate the vulnerability to known CVEs or other known issues.

Windows 2008R2 was vulnerable as well.
OOB security update released to fully resolve the vulnerability on 2018-03-29. CVE-2018-1038. Apply immediately if affected!

2018-03-xx--25: Issue identified in Windows 7 x64. Issue seemed to be patched already. PoC coded. Contacted MSRC with technical description asking if OK to publish a blog entry or if I should hold off publication.
2018-03-26: Green light given by MSRC for me to publish blog entry.
2018-03-27: Published blog entry and PoC.
2018-03-28: Found out that the March patches only partially resolved the vulnerability. Contacted MSRC again.
2018-03-29: OOB security update released by Microsoft. CVE-2018-1038. Apply immediately if affected!

Huge Thank You to everyone at Microsoft that worked hard to resolve this issue. It is super impressive to be able to be able to roll out a complex kernel update in little over a day. It was never my intention to release a fairly potent kernel 0-day publicly. I hope the above timeline explains how this could happen.