CTF | wargame

volgactf 2017 / nested (USB 패킷 분석, 복구, jpg, gz 스테가노그래피 - outguess, gzsteg)

nopdata 2017. 3. 29. 23:50


문제는 패킷 파일 하나가 주어진다. 대강 형태는 다음과 같다.

[ 문제 패킷 일부 ]
USB를 통해서 데이터가 오감을 볼 수 있다. 보면 USBMS 프로토콜을 이용해서 큰 데이터가 오가는 것을 볼 수 있는데, 이 데이터들을 복구 시켜야 한다.
데이터 전송 이전에는 LBA 주소가 전송되는데 이 주소에 맞게 복구를 해야한다. wireshark에서는 패킷의 데이터들을 복구하기가 힘들기때문에 tshark를 이용하였다.


tshark -T fields -e data -r dump.pcap -w output.raw "(usbms && (usb.dst == host || usb.src == host) && scsi_sbc.opcode == 0x28 && !scsi.status) || ppp" | tr -d '\n' > temp
tshark -T fields -e data -r dump.pcap -w output.raw "(usbms && (usb.dst == host || usb.src == host) && scsi_sbc.opcode == 0x2a && !scsi.status)" | tr -d '\n' > temp
[ tshark 추출 명령 ]
전송되는 데이터는 host가 보낸데이터, 받은 데이터로 나뉘며 위 코드상 두 번째 명령으로 받을 수 있는 데이터가 첫 번째 명령으로 받을 수 있는데이터와 동일한 데이터가 있어 굳이 하지 않아도 된다.

추출된 데이터들을 파이썬에서 먼저 조작한 코드는 다음과 같다.

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
= open('output.raw','rb')
 
tmp = f.read(0x70)                                        # tshark prefix 제거
 
data_list = list()
res = list()
 
while True:
    try:
        chk = f.read(0xe0)                                # USBMS 명령 block
        if len(chk) < 1:
            break
        offset = eval("0x"+chk[113:117].encode('hex'))    # LBA 주소
        length = eval("0x"+chk[118:120].encode('hex'))    # Sector 갯수
        if 512*length > 0xf000:                            # 너무 큰 데이터의 경우 조절
            tmp = f.read(512*120)
        else:
            tmp = f.read(512*length)
        data_list.append(tmp)
        res.append(offset)
    except:
        pass
 
f.close()
 
= open('result.raw','wb')
 
for i in range(len(res)):
    f.seek(512*res[i])
    f.write(data_list[i])
 
f.close()
cs

오간 데이터를 봐서는 디스크의 데이터인데 어떻게 처리를 해야할지 몰라서 그냥 아예 이미지를 만들었다. tshark로 추출한 데이터에는 LBA주소와 전송된 데이터 갯수가 있으므로 그에 맞게 조합을 해주면 된다.
조합된 이미지를 ftk로 열어보면 exFAT파일시스템을 가지는 데이터임을 볼 수 있는데 크게 답이 될만한 데이터를 얻을 수는 없었다.
해서 Rstudio를 이용하여 추가적인 데이터를 찾아보았더니 다음과 같이 7z파일이 있음을 알 수 있었다.

[ Rstudio ]

파일 크기가 크지 않아 무료 버전으로도 파일을 획득할 수 있다. 압축을 풀면 each.jpg와 other.zip파일이 존재하는데 other.zip파일에는 비밀번호가 걸려 있다. 제일 마지막에 나온 힌트

'The Other password for your guesses can be found in each picture'

이 말로 보아서는 each.jpg내에 other.zip을 풀 수 있는 비밀번호가 존재하는 것 같은데....

write up을 보니 스테가노 그래피 기법을 적용시켜 풀어야 하는 문제였다. 1번 힌트를 보면 format, our guess에만 글자가 굵게 되어있다. 해서, our guess를 검색 해보긴 했었는데, 이건 each.jpg를 추출하기 전에 해서 대강 해보는 바람에 찾지 못했다. 결론은 jpg의 스테가노 기법 중 outguess을 이용해야 한다. outguess의 메인 페이지는 막혀있는 듯 하고 백업서버를 통해 들어가야 한다.
stegdetect를 이용해서 jpg에 적용된 스테가노 기법들을 탐지할 수 있다고 하는데 이유는 잘 모르겠으나 each.jpg는 탐지를 하지 못하였다.
outguess를 이용해서 each.jpg에 숨겨진 데이터를 찾아야 하는데, 또 여기서 outguess에 적용할 key를 알아야 한다. other.zip과 연관이 있다고 하여 비밀번호는 other라고 한다.

비밀번호를 획득하면 해당 비밀번호를 이용해서 other.zip의 압축을 풀어주면 된다. 압축을 풀면 hide 파일이 나오는데 gz파일이라고 나온다. 2번째 힌트인 gzsteg를 적용시켜 풀어내면 답을 얻을 수 있다.
gzsteg는 gzip 1.2.4버전에서만 사용 가능하며 패치파일이 필요하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@nopdata:/ctf# outguess -k "other" -r each.jpg passcode
Reading each.jpg....
Extracting usable bits:   3944 bits
Steg retrieve: seed: 77, len: 31
root@nopdata:/ctf# cat passcode
enj0y_Ur_z1ppy_c0mplex_phra$e!
root@nopdata:/ctf# unzip other.zip
Archive:  other.zip
[other.zip] hide password: 
  inflating: hide                    
root@nopdata:/ctf# mv hide hide.gz
root@nopdata:/ctf# gunzip -s flag hide.gz
root@nopdata:/ctf# cat flag
VolgaCTF{1t_wa$_s0_embedeasy!}
root@nopdata:/ctf
cs




패킷 파일에서 7z를 추출하는 과정에서 굳이 디스크 전체를 만들어 낼 필요는 없고, 직접 찾아내거나 적절한 카빙 툴을 이용하면 될 듯 하다. Rstudio를 dump.pcap에 적용시켰을 때 카빙이 되지 않았는데, 추출된 데이터에 대해서는 왜 나왔는지는 모르겠다. foremost로는 제대로 되지 않았으나 binwalk로는 찾을 수 있었다.