CTF | wargame

codegate 2011 / forensic 200 (정보 엔트로피, 카빙, 클러스터, jpg 복구)

nopdata 2017. 4. 11. 17:33
keyword : 정보 엔트로피, 카빙, 클러스터, jpg 복구,

문제는 파일이 하나 제공된다. 지문을 보면 용의자의 시스템에서 할당되지 않은 영역을 가져온 데이터라고 한다.

할당되지 않은 영역이라 함은 디스크에서의 데이터를 그대로 가져온 것인데 통상적으로 하나의 데이터를 저장하는데에 있어서 512bytes의 n배수를 할당 한다.
따라서 최소 단위인 512bytes단위로 파일을 보아야 한다.

하지만 write up을 보니 512bytes단위로 보지 않고, 시스템의 클러스터 단위로 보았다. 여기서 클러스터란 디스크의 파일 시스템이 정한 디스크 사용 최소 단위이다.
풀이를 보니 최소 클러스터 단위는 제대로 카빙된 데이터의 크기를 보고 맞추면 된다고 한다.
즉, 파일시스템의 클러스터가 2048bytes가 할당되어 있따면 a.exe파일이 1bytes짜리 프로그램이라도 512bytes의 4배인 2048bytes가 할당된다.


카빙 도구를 통해 보면 연속적으로 추출되는 3360.gif와 3368.gif의 크기는 각각 1kb이다. 하지만 추출되는 데이터의 섹터를 보면 3360, 3368로 8섹터 차이이다.

즉, 제공된 파일의 클러스터 단위는 512bytes x 8 sector인 4096bytes이다. 따라서 8개단위로 잘라서 데이터를 보아야 한다.


전체 데이터를 카빙해보면 그 중에서 위와 같은 jpg파일이 추출된다. 오른쪽은 썸네일인데 jpg파일을 열면 왼쪽과 같이 깨져있다.
따라서 깨져있는 부분을 복구해야 한다. 카빙된 파일의 크기는 83섹터이다. 제공된 데이터가 8섹터씩 끊어져 있으므로, 복구해야 할 데이터는 88섹터 이상이라는 것을 알 수 있다.
문제는 위 사진을 보았을 때, 사진이 어디서부터 깨졌는지 알 수 없다. 88섹터임을 감안 해 보았을 때, 위에서 끊어진 지점을 보면 중간 데이터 부분도 깨져 있어, footer만을 제대로 찾아서 복구 하였을 경우, 사진이 제대로 나오지 않을 가능성이 있다.

그래도 일단, 먼저 footer를 찾아보도록 한다.
jpg의 footer는 보통 알다시피 ff d9로 끝이 난다. 여기서 생각해야할 점은 footer 뒤에 00 00 00... 이게 반복 되어야 한다는 점이다. 그 이유는 8섹터 단위로 저장이 되어 있었으므로, 8개의 섹터를 모두 채우지 못한 나머지 영역의 경우 쓰이지 않은 00 으로 채워지는 것이 마땅하기 때문이다.
해서 ff d9를 조회하되, 이후 부분이 00으로 끝나는 부분을 찾아야 한다.

이를 찾는 소스코드를 파이썬으로 작성하는데 루틴은 다음과 같다.

file open -> read(4096) -> if "ff d9 00 00 00 ..." in read data? yes:break, no:loop

파일을 설정된 클러스터 단위인 8섹터 단위로 읽어들이고 그 값이 ff d9 00 00 00 ...이 맞다면 조회를 할 수 있도록 한다.

단, 하나만 찾으면 안된다. 다른 jpg파일의 footer일 수 도 있기 때문이다.

f=open('test.jpg','rb')
res=list()
for i in range(0,10):
     data=f.read(4096)
     res.append(data)

f.close()

result=''

for i in range(len(res)):
     f=open(str(i)+'.jpg','wb')
     result+=res[i]
     f.write(result)
     f.close()

먼저 깨진 사진의 시작점 부터 어디까지가 정상적인 파일인지 알아야 한다. 위 소스는 그 부분을 찾기 위한 소스코드이다.

8섹터씩 끊어가며 저장을 한 뒤, 데이터가 정상적으로 나오는 지점을 파악해야 한다. 위 소스코드를 이용하여 확인을 해 보면 생성된 8.jpg를 보면 딱 아래와 같이 나온다.


처음 사진에서 처럼 깨진 부분이 명확하게 나온다. 이제 여기부터 이어줄 다음 데이터들을 찾아내야 한다.
위에서 말한 footer찾기 방식으로 먼저 footer를 찾았다. (본래라면 8.jpg도 이름을 바꾸어 주어야 하지만, 8번이 영향이 없으므로 그냥 썼다)

f=open('unallocated','rb')
res=list()
for i in range(0,6586):
    data=f.read(4096)
    res.append(data)


f.close()

f=open('8.jpg','rb')
result=f.read()
f.close()

for i in range(len(res)):
    if "\xff\xd9\x00\x00\x00" in res[i]:
        f=open(str(i)+'.jpg','wb')
        f.write(result+res[i])
        f.close()

footer가 맞는지를 먼저 확인하기 위해서 시작부터 정확한 데이터로 확인된 8.jpg에 footer의 \xff\xd9\x00\x00\x00 ... 가 확인된 footer의 8섹터를 붙여서 출력을 해 보면 5165.jpg를 보았을 때 아래와 같이 중간 부분이 비어 보이지만 깨지지 않은 정확한 그림이 나온다.



footer를 찾았으니 footer로부터 몇 섹터가 정확한 데이터인지 파악해야 한다.
전체 데이터에서 위 footer위치를 찾고 그 위로 이어진 데이터를 쭉 보면 특별히 차이나는 지점이 있다.

write up 에서는 데이터의 엔트로피 차이를 보고 파악하라고 하였는데 이를 의미한다.


이처럼 섹터 단위로 보았을 때, 뭔가 끊어진듯한 느낌을 받을 수 있다. 이 부분이 footer의 시작점이라고 보면 된다.

해서, 8.jpg에 이 데이터를 추가해주면 아래의 사진을 얻을 수 있다.


섬네일을 보았을 때, 이 주소가 답임을 알 수 있다. www.youtube.com/watch?v=uLQokDgcivU
하지만, 아직 정확한 사진을 복구하지는 못하였다. 나머지 중간부분은 데이터의 엔트로피의 차이를 가지고 판단을 해야 한다고 한다.

엔트로피란 확률 분포에 따른 불확실성의 한 척도... 라고 하는데 보면 x크기의 데이터에 \x00 ~ \xff의 분포를 가지고 나타내는 지표이다.
어떠한 압축의 경우 압뒤 분할이 끊긴 경우에는 이 분포도가 분명 비슷할 것이다. 위와 같은 사진의 경우에도 같다. 인근 픽셀값은 비슷하기 때문에 결과로 나타나는 바이너리 값도 비슷할 수 밖에 없다.
해서, 데이터의 엔트로피를 파악한다.

먼저 제대로 8.jpg와 footer를 연결한 위 사진을 tmp1.jpg라고 칭하고 이 tmp1.jpg에 대한 엔트로피 값을 계산 하면 아래와 같다.

계산 소스코드는 http://yc0345.tistory.com/173를 참고하였다.



tmp1.jpg는 총 128섹터이다. 클러스터 단위인 8섹터로 나누면 16이 나온다. 따라서 위와 같이 16개로 구분된 엔트로피 값을 얻을 수 있다.
보면 엔트로피 값이 7.x대에서 비슷하다. 단, 시작점인 첫 클러스터의 엔트로피값과 마지막 footer의 엔트로피 값이 현저히 낮은 것을 알 수 있다.
그 이유는 첫 클러스터의 경우 jpg의 시그니쳐, 파일정보 등을 들어가 있어 실제 데이터가 아니기 때문이며, footer역시 그와 비슷한 이유이다.(footer의 경우 램슬랙으로 인해 0x00이 계산에서 누락된다.)

해서 범위를 좁게 잠으면 7.70~7.90 일 것이고, 크게 잡는다면 7.0 ~ 8.0으로 잡으면 된다. 이 가설이 확실한지에 대해서는 제공된 파일 unallocated의 전체 엔트로피값을 보면 된다.


이 그림은 unallocated 데이터의 엔트로피 값의 일부를 가져온 것이다. 보면 다양한 엔트로피 값이 있다. 때문에 위 가설을 이용하여 풀이를 하면 된다.

write up 에서는 제대로 카빙된 데이터는 제하고 하는 것이라고 하였지만, 카빙툴을 직접 사용하지 않았기 때문에, 따로 제외하는 소스코드를 작성해야 했다.
때문에 그냥 제하지 않고 전체 대입 방식을 이용하여 하기로 하였다.

범위는 7.0 ~ 8.0으로 잡고 시작을 하였다. unallocated에서 이 엔트로피 값에 해당되는 클러스터의 번호들은 다음과 같다.


범위를 넓게 잡아서 인지 많이 생기긴 했다. 하지만 생각을 해 보면 무조건 연속되어 있어야 하는 전제 조건이 있다.
때문에 붙은 값들을 추리면 많지 않다. 해서, 붙은 값들을 추려서 계산을 하면 45번째에 아래와 같은 사진을 얻을 수 있다.


중간에 잃어버린 데이터를 찾았다. 이제 이전에 찾아 놓았던 footer를 이 밑에 붙여주면 최종 복구된 사진을 획득할 수 있다.




(이 문제에서 나온 엔트로피는 다른 문제에서도 주요하게 사용될 것으로 생각되므로 정리 및 사용이 간편한 더미 소스코드 제작이 필요함)

Answer : www.youtube.com/watch?v=uLQokDjcivU