Problem description:
The row-hammer attack is a method,
where the physical properties of the DRAM chip are exploited:
Flushing one row frequently may trigger bit flips in adjecent
rows (see here). One Problem is, that
each row contains many pages belonging to various processes or
the operating system. Hammering might cause also bit flips in
any of those, thus causing system instability.
The following article demonstrates an approach to nail down
a memory page from a SUID-binary or ld-linux itself to a suitable
physical memory location and then hammer it without any risks.
The main idea behind the tool is to use two features of Linux
to get zero-risk row-hammer exploitation:
- Pagemap reading: Knowing the exact number of a physical
page behind a given virtual address might be useful when writing
high performace software or similar. But in the following POC
the only purpose is to verify that a process really holds at least
3 adjacent rows of physical memory pages. Hence hammering on the
middle row will never cause adverse events for other running processes
or the kernel.
- Disk read page cache: When a file is read, e.g. for
execution or other sort of processing, the pages remain in memory
even after use while memory is not needed elsewhere. When a process
maps a file into its virtual address space, it will first get
the page from the cache. When attempting to write to it, the page
is copied on write. By building up memory pressure, a page can
also be evicted from the cache. This is more likely when free
memory is sparse and the page is not used by too many processes.
See here for more information.
So the main feature of the POC is to
- reserve complete rows of physical pages,
- remove the cached page of a file suitable for privilege escalation,
e.g. a SUID binary or ld-linux, from read page cache,
- trigger a timed read to get it mapped to the only free position
in a contiguous block of physical pages and
- hammer the middle of the block with complete buffer rows on
each side, one of them containing the target page. This eliminates
the risk of unintended modifications of unrelated pages.
- When hammering produces just a corrupt page not suitable
for privilege escalation, it can be evicted from page cache again
to start procedure anew.
The POC currently does not support too many command line arguments
or automatic adaption to the target machine. Hence the parameters
have to be changed in code. As I do not have any row-hammer affected
test equipment, the tool will currently only print out the message
Put your rowhammer code here when mapping was set up correctly.
Functionality for that might be included in later releases.
Parameters:
- --DiskInputFile: This file has to be created before
start of POC to consume pages in disk cache when reading from
it. The size should be sufficiently large to provide enough pages
to fill up all remaining free memory with read file cached pages
and hence evict the target file page from dcache over and over
again for repositioning.
- --DiskPagesLoadRounds: This is the number of page load
rounds from DiskInputFile to fill up the disk page cache
and probably evict the target disk page page. After that number
of rounds try to remap the target page. If too small, the target
file page will always stay at the same position (check
lastRoundTargetPagePos).
- --HogDataSize: This is the main parameter to control
memory use. Together with already used memory (minus cached data),
it should amount to 90-105% of the machine's total physical RAM.
- --TargetFile: User readable file to map to memory and try
to hammer on, e.g. /usr/bin/chfn.
- --TargetFilePage: Page of TargetFile to hammer on.
For example /usr/bin/chfn contains code run early in SUID
binary with high privileges which is located on page 3 (offset
3*4096).
Source code controls:
- physicalPagePosInRow: This is the position of the target
file page within the block of contiguous physicial rows for hammering.
The idea is, that if e.g. the 5th page in a physical row has a
higher probability for bit-flips, the target file page should
be mapped to this position. This should be optimized by calibration
unit but currently only manual analysis and setting is supported.
- hammerSpanSlots: Number of full 5-row physical
page blocks to reserve before attemting to read target file.
This number might have to be increased on busy systems.
To start, the POC should be configured to consume nearly all
available memory on the machine (see hogDataSize). To perform
IO activity, create file some-file so that hogDataSize
and the file consume 2-20% more RAM than available in the machine.
After invocation the tool fills up memory and performs disk
IO to cause page cache pertubations.
$ ./D-RamPage --DiskInputFile DiskInput --HogDataSize 1073741824 --TargetFile /usr/bin/chfn --TargetFilePage 3
Longest span is 0x686c at 0x2ff94
Using page range 0x27f80-0x37f80 at 0x600000602000-0x600010602000
Got 0xebd1 pages of 0x10000 in range, will be used in 255 cycles
Searched pages, got 128 spans
Mapped one in-range page in run 186
Target page map successful at 0x29b00+0x0 (rowLength 0x40)!
Got target address 0x600002182000
Ready for hammering: pagemap is:
0x6000c0000000: page 0xa600000000029b00
...
...
0x6000c00bf000: page 0x8600000000029bbf
Put your rowhammer code here!
The tool can also be used to evict pages from ld-linux from
page cache. This worked on a 2-core machine with desktop and virtual
machine running but might fail on really busy server systems.
Interesting bit-flip targets for escalation:
As the
flipping of bits does not give root privileges per se, one of
the modified files has to be used for escalation.
- /usr/bin/chfn: Advantages: file is rarely used, so
it is easier to evict from disk page cache. On page number 3
the quite large sanitize_env function is located which
will run before dropping privileges, processes user controlled
environment variables input and contains many machine instructions
manipulating stack data (a bit flip in the lower address bits
is still likely to point to stack). For testing, the program can
be run ptraced: this will cause loss of SUID-functions but the code
in sanitize_env will react the same. Another target
are the forbidden environment variable strings (forbid
from libmisc/env.c), which are quite large in memory.
- /lib/ld-linux.so.2: Little harder to map as used
more frequently making page cache evict harder. Good targets
are environment variable security checks (page 25 here) where
e.g. LD_PRELOAD is not accepted when containing slashes
(see man ld.so). Hitting that gives you trivial escalation
on any SUID binary using a small
helper library.
For evaluation of good bit-flip target pages, the tool
BitFlipEmulate.c can be used to
create modified variants for system files. Then one can attempt
to exploit a number of such files to get a feeling for the success
rate.
Future improvements:
Currently the POC does not include
the row-hammer attack code itself as it is highly likely to be
dysfunctional without testing (at the moment I have no test machine).
When available, implementation of 3 functions might be useful:
- Automatic rowLength detection: currently 64 pages per row is
used as safe default. This might cause hammering to affect the
affect only some of the pages of the row with the target page
but not the target page itself.
- Page flip calibration statistics: See which pages are easier
to affect, how much hammering is needed to flip only some bits.
- Appropriate restarting of whole process with new row-detection
and calibration values.
Privilege escalation within virtual machine:
To avoid crashing
of the guest or host, /proc/[self]/pagemap cannot be used as it
does not yield the real physical page addresses. An alternative
crash risk mitigation procedure could be to allocate as many pages
as possible. Afterwards start row-hammering a random page and try
to infer the adjecent pages from monitoring the remaining allocated
pages. As soon as linked pages are known, try to fix interesting
target content to the vulnerable pages.
Privilege escalation from virtual machine to host:
There may be hypervisor calls, where guest
pages are passed on to host
directly and vice versa. Interference with such a page might
allow targeted host influence from within guest.