TFC CTF 2024 - vspm
๋ชฉ์ฐจ
0x00. Introduction
Structure
์์ ๊ฐ์ด ๊ตฌ์ฑ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ์ต๋ code_base + 0x4060 ์์ญ์ 10๊ฐ๊น์ง ์ ์ฅํ ์ ์๋ค.
0x01. Vulnerability
unsigned __int64
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
Exploit์ ์งํํ๊ธฐ์ ์์ ๋ณดํธ๊ธฐ๋ฒ์ ํ์ธํด๋ณด๋ฉด code, stack, libc ๋ชจ๋ ์์ญ์ด ๋ณ๋๋๋ ์ํ์ด๋ค. ๋ฐ๋ผ์ ํ์ฌ ๊ฐ์ง๊ณ ์๋ credential overwrite ์ทจ์ฝ์ ์ ๊ฐ์ง๊ณ ์ต์ ํ ์์ญ์ memory leak์ ํด์ผ๊ฒ ๋ค๋ ์๊ฐ์ด ๋ค์๋ค.
Libc Leak
Heap์์ ํ์ฉํ ์ ์๋ memory leak ํ
ํฌ๋ ์ค unsorted bin์ ์ด์ฉํ main_arena leak์ด ๋ง์ด ์ฌ์ฉ๋๋ค. main_arena๋ libc ์์ญ์ด๊ธฐ ๋๋ฌธ์ offset ๊ณ์ฐ๋ง ํด์ฃผ๋ฉด libc base๋ฅผ ํ๋ํ ์ ์๋ค.
๋ฌธ์ ๋ ํฌ๊ธฐ๊ฐ 0x80 ์ด์์ธ chunk๋ฅผ free์์ผ์ผ unsorted bin์ ์ถ๊ฐ๋๋๋ฐ ์
๋ ฅํ ์ ์๋ len์ 0x79๊ฐ ์ต๋์ด๋ค. Heap์ chunk๋ค์ด ์ฐจ๋ก๋๋ก ์์ด๊ณ , ์ฃผ์์์ 0xXXXXXXXXXXXXX000 ๋ถ๋ถ๋ง ๋ณ๋๋๋ฏ๋ก fake chunk๋ฅผ ๊ตฌ์ฑํ๊ณ credential overwrite ์ทจ์ฝ์ ์ ์ด์ฉํ์ฌ ๋ค์ password ๊ตฌ์กฐ์ฒด์ credential์ด fake chunk๋ฅผ ๊ฐ๋ฆฌํค๊ฒ ํ ์ ์๋ค.
= # fake chunk -> prev_size
+= # fake chunk -> size
๋จผ์ 0000, 4444, top chunk์ offset ์ฐจ์ด๋ฅผ ๊ณ ๋ คํด์ chunk๋ค์ ๋ฐฐ์นํ๋ค.
์ฒ์์๋ 0000, 4444์ offset ์ฐจ์ด๋ง ๊ณ ๋ คํ๋ฉด ๋๋ ์ค ์์๋๋ฐ, top chunk์๋ offset ์ฐจ์ด๊ฐ ๋ง์์ผ fake chunk์ free๊ฐ ์ฑ๊ณตํ๋ค.
# first password structure
# first fake chunk header
# second fake chunk header
# top chunk
์ด์ 2222์ credential์ด fake chunk๋ฅผ ๊ฐ๋ฆฌํค๊ฒ ๋ง๋ค๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ด payload๋ฅผ ์์ฑํ๋ค.
# free "1111"
# alloc "1111" and overwrite next pointer
# free fake chunk -> unsorted bin
1111์ ์ด์ฉํด์ 2222์ credential์ด 0x55555555d020์ ๊ฐ๋ฆฌํค๊ฒ ๋ง๋ค๋ฉด 0x55555555d010์ ๋ฃ์ ๊ฐ์ด chunk header๊ฐ ๋๋ค. ์ด์ 2222๋ฅผ freeํ๋ฉด 0x100์ง๋ฆฌ chunk๋ฅผ freeํ ๊ฒ์ผ๋ก ๋์ด unsorted bin์ผ๋ก ์ด๋ํ๋ค.
| | )
์ด ๊ณผ์ ์์ free๋ chunk์ fd, bk์ main_arena ์ฃผ์๊ฐ ์ฐ์ฌ์ง๋ค. ์ดํ malloc์ผ๋ก ํด๋น ์์ญ์ ๋ฐํ๋ฐ์๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ด๊ธฐํํ์ง ์๊ธฐ ๋๋ฌธ์ check()๋ฅผ ํตํด leak์ด ๊ฐ๋ฅํ๋ค.
๋คํํ 0x100๋ณด๋ค ์์ chunk๋ฅผ ์์ฒญํด๋ unsorted bin์ ๋ถํ ํด์ ํ ๋นํด์ฃผ๊ธฐ ๋๋ฌธ์ ๋์ถฉ 0x30์ง๋ฆฌ chunk๋ฅผ ์์ฒญํ๋ค.
# alloc from unsorted bin
=
= 0x3b4cc0
= -
์ด ๋ ์
๋ ฅํ \xc0์ ๋๋ฒ๊ฑฐ๋ก ํ์ธํ main_arena์ ์ฒซ ๋ฐ์ดํธ์ธ๋ฐ ์ด์ฐจํผ offset์ ๊ณ์ฐํ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ๋ง์ถฐ์ค ํ์๋ ์์ง๋ง ์
๋ ฅ์ ์์ ์ฃผ์ง ์์ ์๋ ์๊ธฐ ๋๋ฌธ์ ๋ง์ถฐ์ฃผ์๋ค.
Stack Leak
Libc leak์ ์ฑ๊ณตํ์ผ๋ credential overwrite์ check()๋ฅผ ์ด์ฉํ์ฌ libc์ ๋ชจ๋ ์์ญ์ ์ถ๋ ฅํ ์ ์๋ค. Libc ์์ญ ์ค environ ๋ณ์์ stack ์ฃผ์๊ฐ ์ ์ฅ๋์ด์์ผ๋ฏ๋ก ์ด๋ฅผ ์ด์ฉํด stack leak์ ์งํํ์๋ค.
= 0x3b75d8
= b * 0x20
+=
๋จผ์ 0000์ freeํ๊ณ 1111์ credential์ด environ์ ๊ฐ๋ฆฌํค๋๋ก overwriteํ๋ค.
=
= - 0x110
1111์ด environ์ ๊ฐ๋ฆฌํค๊ณ ์๊ณ check()์์ credential ์ ๋ณด๋ฅผ ์ถ๋ ฅํด์ฃผ๋ ๊ฒ์ ์ด์ฉํด์ environ์ ์ ์ฅ๋ stack ์ฃผ์๋ฅผ ํ๋ํ ์ ์๋ค.
Fastbin Dup into Stack
์์ credential overwrite ์ทจ์ฝ์ ์ ์ด์ฉํด์ credential์ด fake chunk๋ฅผ ๊ฐ๋ฆฌํค๊ฒ ํ ๊ฒ๊ณผ ๋น์ทํ๊ฒ ๋ค๋ฅธ credential์ ๊ฐ๋ฆฌํค๊ฒ ํด์ double free ์ทจ์ฝ์ ์ ํธ๋ฆฌ๊ฑฐํ ์ ์๋ค.
๋จผ์ ์ payload๋ฅผ ์คํํ๋ฉด password ๊ตฌ์กฐ์ฒด๋ ๋ค์๊ณผ ๊ฐ์ ๊ฐ์ ๊ฐ์ง๊ฒ ๋๋ค.
Double free ์ทจ์ฝ์ ์ ํธ๋ฆฌ๊ฑฐํ๊ธฐ ์ํด 7777์ 0x55555555d240์ 5555์ 0x55555555d1d0์ผ๋ก ๋ฎ์ด์ผํ๋ค.
์ฌ๊ธฐ์์ credential์ ์ฌ์ด์ฆ๊ฐ 0x60์ธ ์ด์ ๋ ํ์ ํ๋๋ก ํ๊ณ , 0x60์ง๋ฆฌ chunk๋ฅผ 3๊ฐ ํ ๋น๋ฐ๋ค๋ณด๋ chunk ์ฃผ์์ ๋ ๋ฒ์งธ ๋ฐ์ดํธ๊ฐ ๋ฌ๋ผ์ง๋ค. 0xd1d0 ์ค 0x1d0์ ๊ณ ์ ์ด๊ณ 0xd000๋ถ๋ถ๋ง ๋ณ๋๋ ๊ฒ์ด๋ฏ๋ก ์ฌ๊ธฐ์์ 1/16 ํ๋ฅ ๋ก exploit์ ์ฑ๊ณตํ๋ค.
Fastbin์ ์ด๋ป๊ฒ ์ ์กฐ์ํ๊ฑฐ๋ heap leak์ ํ๋ฉด ๊ฐ๋ฅํ ๊ฒ ๊ฐ๊ธด ํ๋ฐ ํ๋ฅ ์ด ๊ทธ๋ฆฌ ๋ฎ์ง ์์์ ๊ทธ๋ฅ ์งํํ๊ธฐ๋ก ํ๋ค.
์ payload์ ๊ฐ์ด 7777์ credential์ 5555์ credential๊ณผ ์ผ์น์ํค๊ณ 5555, 6666, 7777์์ผ๋ก free๋ฅผ ํ๊ฒ ๋๋ฉด ๋ค์๊ณผ ๊ฐ์ด fastbin์ด ๊ตฌ์ฑ๋๋ค.
0x55555555d1d0->0x55555555d160->0x55555555d1d0
์ด์ malloc์ ํตํด 0x60 ์ฌ์ด์ฆ์ chunk๋ฅผ ์์ฒญํ๋ฉด 0x55555555d1d0์ ํ ๋น๋ฐ์ ์ ์๊ณ , ์ฌ๊ธฐ์ stack์ ์ฃผ์๋ฅผ ์ด ๋ค ํด๋น ์ฃผ์ ์์ fake chunk header๋ฅผ ์ธ ์ ์๋ค๋ฉด fastbin list์ ์ถ๊ฐ๋๋ค.
๋ฐ๋ผ์ ๊ฑฐ๊พธ๋ก fake chunk header๋ฅผ ๊ตฌ์ฑํ ์ ์๋ stack์ ์์น๋ฅผ ์ฐพ์๋ณด์๋๋ฐ, ์ฒ์์๋ save()์ stack์ ํ์ฉํ๋ ค๊ณ ํ๋ค.
unsigned __int64
len, i๋ฅผ ์ด์ฉํ์ฌ fake chunk header๋ฅผ ๊ตฌ์ฑํ๋ฉด return address๊น์ง 0x70๋งํผ ์ฐจ์ด๊ฐ ๋๋ฏ๋ก ์ต๋ ํ ๋น ํฌ๊ธฐ์ธ 0x78๋ณด๋ค ์์ ๊ฐ๋ฅํ ๊ฒ ๊ฐ์๋ค.
๊ทธ๋ฐ๋ฐ ๋ฌธ์ ๋ 0x70 ํฌ๊ธฐ์ chunk๋ฅผ ํ ๋น๋ฐ์ผ๋ ค๋ฉด len์ 0x70์ ์
๋ ฅํด์ผ ํ๋๋ฐ, fake chunk header๋ฅผ ๊ตฌ์ฑํ ๋๋ len์ 0x80์ ์
๋ ฅํด์ผ size๊ฐ ๋ง๊ธฐ ๋๋ฌธ์ ๋ชจ์์ด ์๊ธด๋ค.
๊ทธ๋์ ๋ค๋ฅธ ์์ญ์ ์ฐพ์์ผํ๋๋ฐ return์ ํด์ ์ข
๋ฃํ๋ ํจ์๊ฐ ์์ด์ ๊ณ ๋ฏผํ๋ค๊ฐ, save()์์ ์์ญ์ ํ ๋น๋ฐ๊ณ read๋ฅผ ํ ๋ read()์ return address๋ฅผ ๋ฎ์ผ๋ฉด ๊ฐ๋ฅํ ๊ฒ ๊ฐ์๋ค.
unsigned __int64
๋ฌธ์ ๋ ๋ค์ fake chunk header์ธ๋ฐ, read()์ return address๋ฅผ ๋ฎ์ ์ ์๋ ์์ญ์ malloc() ์ง์ ์ ํ์ธํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
0x7fffffffdbf8์ 0x60์ด ์ ์ฅ๋์ด์์ง๋ง ์ด ๊ฐ์ malloc()์ ์ธ์๋ก ์ ๋ฌ๋ ๊ฐ์ด ๋ด๋ถ ๋ก์ง์ ์คํํ๋ค๊ฐ ์ ์ฅ๋ ๊ฐ์ผ๋ก ์ด์ ์ ๋ชจ์์ด ๋๊ฐ์ด ๋ฐ์ํ๋ค.
์ฌ๊ธฐ์์ ํธ๋ฆญ์ด ํ๋ ์๋๋ฐ, chunk header๋ ๊ตณ์ด alignment๊ฐ ๋ง์ง ์์๋ ๋๊ธฐ ๋๋ฌธ์ stack ์ฃผ์์ ๊ฐ์ฅ ์์ ๋ฐ์ดํธ๊ฐ 0x7f์ธ ์ ์ ์๊ฐํด์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ค์ ์ถ๋ ฅํด๋ณด์๋ค.
์ด๋ ๊ฒ fake chunk header๋ก ํ์ฉํ ์ ์๋ ์์ญ์ด 0x7fffffffdc15์ 0x7fffffffdc25 ๋ ๊ตฐ๋ฐ๊ฐ ์๋๋ฐ, fd๋ฅผ 0x7fffffffdc15๋ก ์ค์ ํ ๊ฒฝ์ฐ๋ malloc()์ ์คํจํ๊ณ , 0x7fffffffdc25๋ก ์ค์ ํ ๊ฒฝ์ฐ๋ malloc()์ ์ฑ๊ณตํ๋ค.
์์๋๋ ์ด์ ๋ ํด๋น ์์ญ์ด ํ์ฌ ํจ์์ stack ๋ฐ๋ก ์ ์์ญ์ด๋ผ์ malloc()์ stack ์์ญ๊ณผ ๊ฒน์น๊ฒ ๋๊ณ , malloc() ๋ด๋ถ์ ์ผ๋ก stack์ ์ฌ์ฉํ๋ค๊ฐ 0x7fffffffdc15์ ์ ์ฅ๋ ๊ฐ์ด overwrite๋๋ ๊ฒ์ผ๋ก ์ถ์ ๋๋ค.
์ด์จ๊ฑฐ๋ 0x7fffffffdc25๋ฅผ fd๋ก ์ค์ ํ๊ธฐ ์ํด ํ๋ํ stack ์ฃผ์์์ offset์ ๊ณ์ฐํด์ 0x55555555d1d0์ ํ ๋น๋ฐ์ ํ ์
๋ ฅํด์ฃผ๋ฉด fastbin์ ๋ค์๊ณผ ๊ฐ์ด ๊ตฌ์ฑ๋๋ค.
0x55555555d160->0x55555555d1d0->0x7fffffffdc35
๋ฐ๋ผ์ ์ดํ 3๋ฒ์งธ malloc()์์ stack์ ์ฃผ์๊ฐ ๋ฐํ๋๊ณ , offset์ ๊ณ์ฐํด์ read()์ return address๋ฅผ one shot ๊ฐ์ ฏ์ผ๋ก ๋ฎ์ด์ฃผ๋ฉด ๋๋ค.
=
= 0xe1fa1
= b * 0x13
+=
0x03. Payload
=
= 0x0000555555554000
=
= f
=
return
return
return
return
=
=
=
= # fake chunk -> prev_size
+= # fake chunk -> size
# libc leak
# free "1111"
# alloc "1111" and overwrite next pointer
# free fake chunk -> unsorted bin
# alloc from unsorted bin
=
= 0x3b4cc0
= -
# flush unsorted bin
# stack leak
= 0x3b75d8
= b * 0x20
+=
=
= - 0x110
# fastbin dup
=
= 0xe1fa1
= b * 0x13
+=
=
=