워게임/Root Me

[Root Me] ELF C++ - 0 protection - 리버싱 / IDA / GDB

SecurityMan 2022. 6. 30. 11:00


C++ 리버싱 문제

C++ 리버싱은 처음 해본거 같은데 생각보다 어려워서 놀랐다.

C 랑 큰 차이 없지 않을까 생각하고 시작했다가 큰코다쳤다..

반응형


문제의 목표는 올바른 password를 찾는것이다.


문제파일로 주어지는 것은 ch25.bin 파일이다.


먼저 프로그램을 실행시켜 보았다.

터미널에서 ./ch25.bin 이라고 입력하면 실행이 되는데

그냥 실행시켰더니 usage : ./ch25.bin password 라면서 사용법이 출력된다.

./ch25.bin hello 라고 password를 인자로 넣어서 실행시켜 주니

비밀번호가 틀렸다는 안내 문구가 출력된다.


IDA 라는 디스어셈블러를 이용해서 ch25.bin 파일을 열어보았다.

c로 만들어진 프로그램이라면 오른쪽에 깔끔하게 어셈블리어가 잘 보이는데

c++로 만들어서 그런지 알아볼 수 없는 복잡한 내용들이 보였다.


그렇다고 아예 알아볼 수 없는 것은 아니다.

주목한 부분인 이 부분인데

_ZSteqIcSt11char_traitsIcESaIcEEbRKSbIT_T0_T1_EPKS3_ 를 call 한다음에

그 결과값에 따라서 password incorrect 를 출력하거나, Bravo 를 출력하고 있는 모습이 보인다.

저 부분을 자세히 보기로 했다.


_ZSteqIcSt11char_traitsIcESaIcEEbRKSbIT_T0_T1_EPKS3_ 를 더블클릭하면 이런 화면으로 넘어간다.

아래쪽에 compare 가 있는걸로 보아서 비밀번호를 검증하는 루틴인 듯 하다.

시작 주소인 08048CF7 을 잘 기억해 둔다.


여기부터는 Kali 리눅스에서 gdb 라는 디버깅 도구를 이용해 분석했다.

gdb는 기본적으로 설치되어 있고,

gdb ./ch25.bin 을 입력하면 해당 파일을 디버깅 할 수 있다.


info func 라고 입력하면 ch25.bin 내에 있는 함수들의 목록을 보여준다.

아까 기억해뒀던 08048CF7 을 찾아보면

bool std::operator==<char, std::char_traits<char>, std::allocator<char> (std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*) 라고 적혀있는데

이것이 아까 IDA 에서 봤던 _ZSteqIcSt11char_traitsIcESaIcEEbRKSbIT_T0_T1_EPKS3_ 이랑 똑같은 것이다.


b bool std::operator==<char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, char const*)

라고 입력해서 해당 함수에 breakpoint를 걸어준다.


r(run) 을 입력하면 프로그램을 실행시킬 수 있다.

주의할 것은 password를 인자로 넣어줘야한다는 것이다.

그래서 r hello 같은 방식으로 입력을 해줘야 한다.

프로그램을 실행시키면 아까 브레이크포인트를 걸어둔 곳에서 프로그램이 멈추게 된다.


다시한번 IDA 에서 08048CF7 의 어셈블리를 살펴본다.

eax 에 ebp + arg_4 (0x0c) 를 저장하고, 다시 그값을 esp +4 가 가리키는 주소로 옮기고 있다.

그다음 다시 eax 에 ebp + arg_0 (0x08) 을 저장하고, 그 값을 esp 가 가리키는 주소로 옮긴다.

그리고 나서 compare 를 수행하는데

위에서 가리키고 있는 두 주소중 어느곳에 비밀번호가 숨어있는듯 하다.

일단 compare를 하는 부분의 주소가 08048D0A 이니 거기까지 gdb에서 프로그램을 진행시킨다.


breakpoint 로 부터 ni 를 입력하면 프로그램을 한 칸씩 진행시킬 수 있다.

ni 를 네 번 입력해면 08048D0A 에서 멈추는것을 볼 수 있다.


여기서 info reg 명령어를 이용하면 레지스터의 상태를 확인할 수 있다.

아까 사용되었던 eax, esp 레지스터 값만 살펴본다.

eax 에는 0xffffd104, esp 에는 0xffffd0d0 가 들어있다.


스택의 내용을 보려면 x/20wx <주소> 이런식으로 입력하면 된다.

esp 에 저장되어있던 0xffffd0d0 주소의 스택을 살펴봤더니

eax에 저장되어있던 0xffffd104 가 들어있는것이 보인다.


이번엔 eax 저장된 주소인 0xffffd104 의 스택을 살펴본다.

여기에는 0x0805ccc 라는 값이 저장되어 있는데,

이것도 뭔가 주소처럼 보인다.


마지막으로 0x08050ccc 주소로 찾아가보면

뭔가 데이터가 저장되어 있는것처럼 보인다.

여기를 읽을 때 주의해야 하는데 리틀 엔디안 방식으로 저장이 되어있기 때문이다.

예를들어 맨 처음있는 0x65726548 의 경우 이걸 그대로 읽는 것이 아니라

48 65 72 65 처럼 두개씩 잘라서 맨 뒤부터 읽어야 한다는 것이다.


메모장에서 리틀 엔디안으로 적혀있는 16진수 값들을 순서대로 써 주었다.


CyberChef(https://gchq.github.io/CyberChef)에서 hex 값을 디코딩해주면 숨겨진 password를 찾을 수 있다.


ch25.bin 프로그램을 실행시키면서 찾은 비밀번호를 넣어보면

Bravo 라고 뜨는것을 볼 수 있다.

반응형