CTF-D/Network

[Network] 우리는 적군에 대한 첩보를...

Ron Weasley 2022. 1. 24. 19:44

CTF-D 사이트에 있는 네트워크 카테고리에 우리는 적군에 대한 첩보를 풀어보겠습니다.
문제를 한번 보겠습니다.

[그림 - 1]

주어진 첨부파일인 exfil.tar를 다운 받아 압축을 풀어줍니다.
( 리눅스 환경에서 tar파일을 풀려면 tar xvf exfil.tar를 입력하시면 됩니다. )
압축을 풀면 dump.pcap파일과 server.py가 들어 있는것을 볼 수 있습니다.
먼저 와이어샤크를 통해 dump.pcap파일을 보겠습니다.

DNS Packet [그림 - 2]

dump.pcap을 실행하면 다음과 같은 DNS 패킷을 볼 수 있고, 패킷을 좀 더 읽어보면 DNS패킷과 데이터 값처럼 보이는 것들을 확인할 수 있었습니다.

[그림 - 3]

노란색 형광펜으로 되어 있는것을 보겠습니다.
첫번째 패킷은 클라이언트가 DNS를 요청하는 패킷으로 (DNS Query Packet)
두번째 패킷은 DNS서버가 클라이언트에게 요청에 대한 응답을 해주는 패킷으로 (DNS Response Packet) 입니다.
패킷에 대해 상세분석을 보면 다음과 같습니다.

DNS message Header [그림 - 4]

1) Transaction ID
DNS 쿼리와 응답에 연관됩니다. 사용자는 이 필드에서 DNS에 관련된 모든 것을 보기 위한 값을 필터링할 수 있습니다.
이 필드에 대한 필터링은 dns.id입니다.

2) Flags
DNS 패킷의 성격을 제어하는 많은 필드로 구성되어 있습니다.
먼저, Response는 요청하는 패킷(0)인지, 응답하는 패킷(1)인지 표시하는 비트입니다.
 
2-1) Response
지금 분석하고 있는 패킷은 query패킷으로 response값에 0으로 확인이 됩니다.
필터링을 하는 방법은 다음과 같습니다.
dns.flags.response == 1 (응답)
dns.flags.response == 0 (요청)
 
2-2) Opcode
Opcode는 쿼리의 유형을 지정합니다. Opcode 값은 아래와 같습니다.

2-3) Truncated
DNS 응답이 정해진 길이를 초과하여 잘렸는지 표시 하는 필드입니다. 만약, 클라이언트가 잘린 DNS 응답을 보게 되면, TCP를 통해 다시 쿼리를 전송합니다.
 
2-4) Recursion desired
Query를 보낼 때 재귀적인 Query가 필요한지 여부를 정의합니다. 기본적으로 1로 되어 있으며, 불필요 한 경우 0으로 세팅하여 전송하면 된다.
 
2-5) Z
Z 필드는 나중을 위해 예약된 필드로, 항상 0으로 설정되어 있습니다.
 
3) Questions
몇개의 질문인지 질문의 갯수를 표시합니다. 보통 패킷당 한개의 질문을 가집니다.
 
4) Answer RRs, Authority RRs, Additional RRs
각 세션의 갯수를 표시합니다.
 
다음은 Queries 세션입니다.

Name : DNS에 요청한 도메인의 네임, 호스트 네임이 들어갑니다.
Type : 쿼리의 유형을 나타내는 필드이고, 유형은 다음과 같습니다.

가볍게 DNS패킷에 대해서 설명하였고, 본론으로 넘어가 여기서 이상한 점을 찾으셨나요??
DNS에 요청한 도메인의 이름이 암호화된 문자로 보이네요...G4JQAAA....
그래서 우리는 암호화된 문자가 문제를 풀이하는데 쓰인다고 생각을 하고, 이젠 response를 보겠습니다.

패킷을 본 결과, 타입이 CNAME으로 확인할 수 있다.
결국, DNS Query의 Name 부분과 response패킷의 CNAME으로 확인할 수 있습니다.
 

CNAME이란?
  도메인 네임을 다른 이름으로 매핑시키는 것으로, CNAME 레코드는 무조건 다른 도메인 네임을 가리켜야 하며 직접 IP주소를 가리켜서는 안됩니다.

 
문제의 방향성을 보면, dump.pcap파일을 분석하여 DNS패킷에서의 전달된 데이터 값을 추출해서 문제를 해결해야 하는 부분인데, 비슷한 CTF문제들을 생각해봤을 때, 해당 위치에 파일들을 숨겨 파일 시그니처를 통해 파일을 확인하고 추출해 낼 수 있는 문제들이 있습니다. 하지만 해당 문제에는 파일 시그니처 같은 데이터는 보이지 않아서, 같이 매핑되어 있던 server.py파일도 실행시켜 보겠습니다.

server.py 소스코드의 일부분입니다.
이 부분은 데이터의 시작 위치를 나타내며, 6바이트 이후에 데이터가 시작됨을 알 수 있습니다.

이 부분은 도메인 이름을 뜻합니다.
위에 암호화된 문장 끝에 .을 끝으로 eat-sleep-pwn-repeat.de라고 적혀있습니다

이 부분은 데이터가 base32로 인코딩 되었음을 알 수 있고, 각 데이터마다 청크를 구별할 수 있다.

이 부분은 도메인 이름이 base32로 디코딩 되는 부분입니다.

이 부분은 원격 쉘의 실행을 확인할 수 있습니다.
지금까지 잘라 온 소스코드를 이어보면, 데이터를 base32로 인코딩하여 원격 쉘을 통해 DNS패킷의 DNS Query Name부분과 CNAME을 통해 전송되었다는 것으로 의심할 수 있습니다.
 
이제는 윈도우 환경이 아닌 리눅스 환경으로 넘어가서 tshark를 써보겠습니다.
tshark를 통해서 DNS Query Name과 CNAME부분을 추출해 내고 추출한 파일을 Python 코드를 작성하여 base32로 디코딩하여 패킷의 내용을 확인 해보겠습니다.

tshark로 DNS Query Name과 CNAME추출 명령어
tshark -r dump.pcap -Tfields -e dns.qry.name | awk '!a[$0]++' > extracted.txt && tshark -r dump.pcap -Tfields -e dns.cname | awk'!a[$0]++' >> extracted.txt

명령어에 대해서 모르시는분들을 위해 간략하게 설명하겠습니다.
-r : 캡쳐를 한 패킷을 불러옵니다. (즉, dump.pcap이 캡쳐된 패킷이니까 해당 파일을 불러온겁니다.)
-T : 텍스트 출력 형식을 설정합니다.
-e : 표시 하고자 하는 필드를 지정할 수 있으며, Tfields 옵션이 함께 있어야합니다.
(그래서 -e 명령어 앞에 Tfields 옵션이 붙은겁니다.)
dns.qry.name : DNS Query Name필드입니다.
dns.cnama : CNAME 필드입니다.
 
awk : 필드 단위 패턴 처리입니다. 필드 단위로 패턴을 검색하고, 조작하는 것을 주 목적으로 하여 파일 내의 각 라인(레코드)에 대한 필드를 인식할 수 있는 패턴 검색 기능을 보유하고 있으며, awk를 이용해서 조작도 가능합니다.
 
'!a[$0]++' : 식의 값을 무효화합니다. [$0]는 현재 행을 배열의 키로 사용하고 a에 저장된 값을 취합니다. !는 이전 값을 무효화합니다. 그리고 a[$0]이 반환된 경우 ++을 통해 1을 증가 시켜 배열의 키를 1 올려줍니다.
쉽게 말해 $0 배열부터 순서대로 필드 단위로 패턴을 검색하되, 순서를 유지하면서 중복을 제거합니다.
 
이제 명령을 실행 한 후, 나오는 extracted.txt를 보겠습니다.

흠...알아들을수 없는 말로 인코딩 되어 있기 때문에 base32로 디코딩을 하겠습니다.

코드는 다음과 같습니다.
디코딩을 성공하면 다음과 같이 텍스트 파일을 볼 수 있는데, 안에 내용을 살펴보겠습니다.

너무 많아서 간략하게만 가져오겠습니다.
디코딩된 내용을 확인 해보면, GPG라는 부분과 하단에 PUBLIC KEY BLOCK, PRIVATE KEY BLOCK이 존재합니다.
 
일단 간단하게 GPG에 대해서 설명하자면, GPG는 OpenPGP표준을 기반으로 동작하는 파일의 암호화 및 파일의 무결성 점검 도구입니다. GPG를 이용해서 암호화를 위한 키를 생성하고 파일들을 암호화 할 수 있습니다.
쉽게 말해서, 해당 문제에는 GPG를 사용해서 어떠한 파일을 암호화 하여 전송했다는 것인데, 디코딩된 파일을 살펴보면 START_OF_FILE과 END_OF_FILE을 확인할 수 있는데, 해당 문자열 사이에 있는 데이터가 암호화되어 전송된 파일의 데이터 값이라고 의심해 볼 수 있으며, 암호화된 데이터를 복호화 하기 위해서 START_OF_FILE과 END_OF_FILE 사이에 있는 데이터 값을 Hex에디터로 추출하여 decode.txt파일에 존재하는 secret.docx.gpg로 확장자를 바꿔 저장하겠습니다.
 
Hex에디터로 decoded.txt파일을 열어서 카빙을 진행합시다.

여기까지 진행을 하고 secret.docx.gpg로 저장하겠습니다.
gpg로 저장하는 이유는 gpg를 통해서 암호화가 되었기 때문입니다.
gpg를 통해 암호화된 데이터를 복호화 하기 위해서는 public key와 private key가 있어야 하는데, decode.txt에서 아까 BEGIN PGP PUBLIC KEY BLOCK 이부분과 END PGP PUBLIC KEY BLOCK부분 등등 보일텐데 이 부분을 새로운 메모장에다가 복붙해서 저장을 하는데, 파일 이름은 public.key와 private.key로 만들어줍니다.

PRIVATE KEY
PUBLIC KEY

END PGP PRIVATE KEY BLOCK은 생략되어 있어 따로 추가 해줬습니다.
이제 리눅스에서 gpg명령을 이용해서 파일의 복호화를 시켜주겠습니다.

$ gpg --import public.key

$ gpg --import private.key

$ gpg --decrypt secret.docx.gpg > secret.docx

순서대로 명령을 입력하시면 복호화 된 secret.docx파일을 확인할 수 있으며
파일을 열어보면 flag값을 찾을 수 있습니다.

 
롸업을 설명하다보니 길어졌네요...그냥 이정도면 풀이선생님 해도 되겠습니다 ㅋㅋㅋ
 
-reference-
디지털 포렌식 with CTF

'CTF-D > Network' 카테고리의 다른 글

[Network] 파일에서 플래그를 찾아라.  (0) 2022.01.26
[Network] 나는 힉스 입자가 발견되지 않을 것이라고  (0) 2022.01.25
[Network] DefCon#21 #6  (0) 2021.10.17
[Network] DefCon#21 #5  (0) 2021.10.17
[Network] DefCon#21 #4  (0) 2021.10.15