prettify code

2017年3月21日 星期二

[Write-up] 0ctf 2017 qual - pwn647 pages


Introduction

binary

This is not a normal but very interesting pwn challenge. The target of this chal is to "guess" random 64 bits.

When running binary, it'll fetch 64 random bits from /dev/urandom, and mmap 64 pages according to the following rule:
// bits = random 64 bits
void* base = 0x200000000;
for(int i=0;i<64;i++) {
  mmap(base+(2*i+bits[i]) * 0x1000, 0x1000, ...);
}



And the result of maps looks like:
$ cat /proc/27859/maps
00400000-00402000 r-xp 00000000 fc:00 17992369   /home/0ctf2017/pages/pages
00601000-00602000 r--p 00001000 fc:00 17992369   /home/0ctf2017/pages/pages
00602000-00603000 rw-p 00002000 fc:00 17992369   /home/0ctf2017/pages/pages
0063a000-0066c000 rw-p 00000000 00:00 0          [heap]
200000000-200001000 rw-s 00000000 00:05 12606831 /dev/zero (deleted)
200002000-200003000 rw-s 00000000 00:05 12606832 /dev/zero (deleted)
200004000-200005000 rw-s 00000000 00:05 12606833 /dev/zero (deleted)
200007000-200008000 rw-s 00000000 00:05 12606834 /dev/zero (deleted)
200008000-200009000 rw-s 00000000 00:05 12606835 /dev/zero (deleted)
20000a000-20000b000 rw-s 00000000 00:05 12606836 /dev/zero (deleted)
20000d000-20000e000 rw-s 00000000 00:05 12606837 /dev/zero (deleted)
20000f000-200010000 rw-s 00000000 00:05 12606838 /dev/zero (deleted)
200010000-200011000 rw-s 00000000 00:05 12606839 /dev/zero (deleted)
200012000-200013000 rw-s 00000000 00:05 12606840 /dev/zero (deleted)
200015000-200016000 rw-s 00000000 00:05 12606841 /dev/zero (deleted)
200016000-200017000 rw-s 00000000 00:05 12606842 /dev/zero (deleted)
200018000-200019000 rw-s 00000000 00:05 12606843 /dev/zero (deleted)
20001a000-20001b000 rw-s 00000000 00:05 12606844 /dev/zero (deleted)
20001c000-20001d000 rw-s 00000000 00:05 12606845 /dev/zero (deleted)
20001f000-200020000 rw-s 00000000 00:05 12606846 /dev/zero (deleted)
200020000-200021000 rw-s 00000000 00:05 12606847 /dev/zero (deleted)
200023000-200024000 rw-s 00000000 00:05 12606848 /dev/zero (deleted)
200024000-200025000 rw-s 00000000 00:05 12606849 /dev/zero (deleted)
200026000-200027000 rw-s 00000000 00:05 12606850 /dev/zero (deleted)
...

After mmap-ed these pages, we can send our shellcode, the shellcode will be executed and it has to answer what the 64 random bits are. The flag will be printed out if answered correctly.

Exploit Method

The first thought come to mind is the famous ASLR breaking attack by VUSec - AnC attack.

However, the scenario is different between AnC attack and this challenge.
Basic idea of AnC is accessing an unknown address pointer and the accessing time will be different according to the CPU cache.
While in this chal, we cannot access any address starts with 0x200000000 because it will crash directly if the address doesn't be mmap-ed.

Thanks to +劉育全 's googling ability, he found there's a PREFETCHT0 instruction in x86. This instruction takes an address as argument and asks CPU to bring the address into cache. And according to the reference, this instruction works as no-op if the specific address is not exists.

So we experimented how many CPU ticks need to take when the address exists or not. And the result is exciting - it take much longer time when the address is not mmap-ed!

So the exploit is easy, just run PREFETCHT0 many times over two addresses, the one needs shorter time is the mmap-ed one.

Exploit Script: https://github.com/david942j/ctf-writeups/blob/master/0ctf-2017-qual/pages/pages.rb

Flag: flag{rand0mPage_randomFl4g}