CTF | wargame

codegate 2013 - forensic 400 (ADS, TrueCrypt, Fuzzy Hash, ssdeep, (subprocess))

nopdata 2017. 4. 11. 17:56
keyword : ADS, TrueCrypt, Fuzzy Hash, ssdeep, (subprocess)

아무런 지문 없이 파일 하나가 주어진다. 주어진 파일은 evidence 파일이며, 이 파일의 구조를 보다 보면 black폴더에 다수의 까만 jpg파일이 들어있고, 그 중 B(115).jpg에 특이한 점이 있었다.

[ B (115).jpg ]
보면 확장자가 jpg인 파일임에도 그 하위에 어떤 데이터가 들어가 있는 것을 파악할 수 있다. 이는 ftk에서 보여주듯 Alternate Data Stream이라고 하며, 윈도우에서 지원하는 추가 스트링이다.


악성 데이터를 숨기기에도 아주 용이하다고 한다. ADS로 숨겨진 black.txt를 추출해서 hxd로 열어 보았으나 어떤 데이터인지 파악을 할 수 없었다.
대회에서 ADS는 TrueCrypt로 암호화된 데이터 숨기기 문제가 많이 출제 된다고 한다. 이 전제는 전체 데이터의 엔트로피가 차이없이 균등하다는 전제가 있다.

[ winhex - AnalyzeFile ]
각 바이너리 값의 빈도를 보면 10,000단위 임에도 큰 차이를 보이지 않는 것을 파악할 수 있다. (다른 파일에 비하면 이정도의 편차는 상당히 적은 편이다.)

비밀번호를 알아야 복호화 진행이 가능한데 여기서 필요한 비밀번호는 black폴더에 있는 jpg파일들과 hole에 있는 jpg파일들을 비교해야 한다.
모두 다 검은색 화면의 .jpg파일인데 hxd값을 보면 조금씩 다르다. 두 가지 write up을 보았는데 하나는 직접 hxd를 보고  특이점을 찾아 진행한 방법이고 다른 하나는 Fuzzy Hash를 이용하여 풀어낸 방법이다.

Fuzzy Hash는 파일의 hash유사도 분석을 하는 방식이다. 전체 바이너리의 hash값을 사용하는 것이 아닌 일정 블록으로 나누고 hash를 진행하는 방식이기 때문에 일반 hash 비교와는 다르다.
사용되는 프로그램으로는 ssdeep이 있다.

[ ssdeep 옵션 ]

먼저 유사도 분석을 위해서 black폴더에 있는 파일들의 해쉬값들을 txt로 저장한다.

[ ssdeep *.jpg >> output.txt ]


이제 Hole폴더의 jpg파일들과 유사도 분석을 하면 유사도가 높은 jpg파일이 검색이 된다.


100% 매치하는 파일도 있으며 대부분 97% 이상의 유사도를 보인다. hexcmp로 99%유사한 0LF~ 와 ~(55).jpg를 비교한 모습은 아래와 같다.

[ hexcmp ]
시작 부분에 데이터와 생성시간, 그리고 footer이후에 들어가는 1byte의 차이 빼고는 모두 동일하다. B (xx).jpg의 이름 순서대로 정렬을 해서 찾는 것이 아니라, B(xx).jpg안에 들어있는 header 부분의 데이터 순서를 맞추어 정렬을 하고 그 순서대로 footer의 데이터를 읽어야 한다. 이 순서에 따라 소스코드를 작성하였다.전제조건은 Black폴더와 Hole폴더가 모두 추출이 되어 있어야 하고 두 폴더의 상위 경로에 ssdeep와 이 소스코드가 존재하여야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import subprocess
 
f=open('black_hash.txt','wb')
subprocess.call(['ssdeep','./Black/*'],stdout=f)
f.close()
 
f=open('matches.txt','wb')
subprocess.call(['ssdeep','-m','black_hash.txt','./Hole/*'],stdout=f)
f.close()
 
f=open('matches.txt','rb')
data=f.read()
data=data.split('\r\r\n')
del data[-1]
 
result=list()
for i in data:
    f=open('./Black/B '+i.split('\\')[-1].split(' ')[1],'rb')
    tmp=f.read()
    f.close()
    try:
        result.append([int(''.join(tmp[0x20:0x22].split('\x00'))),i.split('Hole\\')[1].split(' ')[0]])
    except:
        pass
    
 
result.sort()
for i in result:
    f=open('./Hole/'+i[1],'rb')
    tmp=f.read()
    f.close()
    tmp_d = tmp.split('\xff\xd9\x00\x00\x00\x00')[1]
    print tmp_d[0],
 
cs

[ 동작 결과 ]
동작을 시키면 위와같이 각 위치 값에 따른 데이터 추출의 결과가 나타난다. TrueCrypt의 패스워드 7ru3CyP7_P422w0Rd_57r1n9_K1

[ Truecrypt Mount ]

Truecrypt의 portable버전을 사용하였으며 위 비밀번호를 넣고 마운트를 시키면, key.txt파일이 나온다.

[ key.txt ]
Answer : EyE Am ph33|1n6 |u(ky