본문 바로가기

드림핵

[드림핵 워게임] Return Address Overwrite

<문제>

 

<풀이>

먼저 문제를 다운로드했다.

rao.c부터 살펴본다.

main 함수부터 보면

char 배열 변수 buf를 0x28만큼의 크기로 선언한다.

그리고 25행에서 scanf()를 이용해 buf에 사용자 입력값을 저장하는데

scanf()에 입력값을 "%s"로만 받으면 입력값 길이를 검증하지 않기 때문에 stack buffer overflow가 발생할 수 있다.

 

다음으로 get_shell()함수가 눈에 띠는데,

execve()함수를 이용해 "/bin/sh"를 실행한다.

즉 get_shell()를 실행하면 shell을 획득할 수 있다.

 

 

여기까지 봤을 때 공격 시나리오를 다음과 같이 짤 수 있다.

stack buffer overflow 취약점을 이용해서

return address를 get_shell()함수로 overwrite하고

쉘을 획득하는 것이다.

 

 

이제 gdb를 이용해서 바이너리를 분석해보자.

main함수를 disassemble 해보면

<+4>에서 sub rsp, 0x30으로 스택 프레임에 공간을 마련하는 것을 볼 수 있다.

앞서 c코드에서 살펴본 buf변수의 공간이다.

<+35>에서 lea rax, [rbp-0x0]을 하는데, 이는 rax가 buf의 주소를 가리키도록 설정하는 것이다.

 

이 상태로 <+54>의 scanf함수를 호출하면

scanf함수의 반환 값이 rax에 저장되는 데(*rax는 함수의 반환값을 받는 레지스터), 그 주소에는 buf가 있으므로,

결과적으로 scanf의 반환값이 buf에 저장될 것이다.

 

그래서 main의 스택 프레임 구조를 그려보면 다음과 같다.

따라서 buf에 0x30+0x8bytes의 임의의 문자와 0x8bytes의 return address를 입력하면

<+65>의 ret 실행 시 입력한 return address로 jump할 것이다.

 

 

return address는 get_shell() 함수의 주소여야 하므로 다음과 같이 주소를 알아낸다.

get_shell의 주소는 0x4006aa 인 것을 알았다.

 

파이썬으로 페이로드를 구성해서 rao를 실행했더니 셸을 획득할 수 있었다.

페이로드는

(python3 -c "import sys;sys.stdout.buffer.write(b'A'*0x30 + b'B'*0x8 + b'\xaa\x06\x40\x00\x00\x00\x00\x00')";cat)

이렇게 구성했는데 주소에 해당하는 b'\xaa\x06\x40\x00\x00\x00\x00\x00'

get_shell의 주소인 0x4006aa과 조금 순서가 다른이유는

리틀 엔디안 방식으로 주소를 전달해야 하기 때문이다.

 

이렇게 페이로드를 전달했을 때 셸을 획득할 수 있는 것을 확인했다.

이제 pwntools를 이용해 실제 서버를 대상으로 익스플로잇하는 파이썬 코드를 짜서 flag를 얻어보자.

 

파이썬 코드는 이렇게 짰다.

remote에 들어가는 데이터는

드림핵 문제페이지에서 Request VM을 클릭한 후

이렇게 출력되는 Host와 Port를 입력하면 된다.

 

 

FLAG가 출력되었다.