들어가며..


안녕하세요! 해킹캠프 CTF에 Easy CAESC, Yeeeeeeeee, Extract Me If You Can, HWP Master 를 출제하였던 Kong(차현수)입니다. 제가 출제하였던 모든 문제의 풀이를 올리려고 하였으나 시간 부족으로 Easy CAESC(풀이세미나 시간에 5조에서 풀이 발표하였습니다.)는 생략하고 Yeeeeeeeee 문제(풀이 세미나 시간에 제가 풀이 발표하였습니다.)는 풀이세미나 시간에 사용했던 PPT 자료만 업로드 할게요. 또 HWP Master 문제는 풀이를 쓰려면 좀 길어서, 참고 링크만 걸어둘테니 잘 모르겠으면 개인적으로 컨택 부탁드립니다.

Yeeeeeeeee


yee-ppt-solve_code-.7z
(서버 설정때문에 특정 확장자만 업로드가 가능해, 부득이하게 png로 올렸습니다. 크롬 혹은 파이어폭스로 접속후 링크를 누른후 Ctrl+S 로 컴퓨터에 저장하셔서 확장자를 7z로 바꿔주시면 정상적으로 열립니다.)

Extract Me If You Can


파일을 열어보시면 아래 그림과 같이 3바이트씩 끊어져 있는 파일을 발견 할 수 있습니다.
3바이트씩 끊어져 있으므로, CRC 컬럼(CRC32)을 이용해 3바이트를 얻을 수 있습니다.

아래 코드는 7z를 이용하여 CRC 컬럼을 파싱, z3로 역산하는 코드입니다.
3바이트이기 때문에 z3를 사용하지 않고 브루트포싱을 이용하여도 (조금은 느릴지도 모르지만) 같은 결과를 얻을 수 있습니다.
5424

import subprocess
from z3 import *


def z3crc32(data,length):
    crc = BitVecVal(0xffffffff,32)
    for block in range(0,length):
        crc ^= ZeroExt(24,data[block])
        for i in range(8):
            crc = If(crc & 1 == BitVecVal(1,32), LShR(crc, 1) ^ 0xedb88320, LShR(crc, 1))
            crc &= BitVecVal(0xffffffff,32)
    return crc ^ 0xffffffff
def Breakcrc32(_crc,length):
    global Output
    s = Solver()
    data = [ BitVec('a%i'%i,8) for i in range(0,length)]
    crc = z3crc32(data,length)
    s.add(crc == _crc)
    s.check()
    Output += chr(s.model()[data[0]].as_long())
    Output += chr(s.model()[data[1]].as_long())
    Output += chr(s.model()[data[2]].as_long())
       
def get_info(fname):
	proc = subprocess.Popen(["7z.exe","l", "-slt",fname] ,stdout=subprocess.PIPE,stdin=subprocess.PIPE)
	stdout = proc.communicate()
	CRCLIst = []
	data = stdout[0].split("\n")
	
	for i in xrange(0,len(data)):
		if data[i][0:6] == "CRC = ":
			CRCLIst.append(eval("0x" + data[i][6:6+8]))
	return  CRCLIst


global Output
Output = ""
CRCList = get_info("extract_me.7z")

for crc in CRCList:
        Breakcrc32(crc,3)
f = open("get_flag.7z","wb")
f.write(Output)
f.close()

print "Finish"

위 코드를 이용해 생성된 get_flag.7z 파일을 압축 풀어주면, exe파일이 나오는데, 실행하여 주면... 짜잔! 플래그가 나옵니다.
542

HWP Master


문제를 열어보시면....


5232

이렇게 나옵니다. 네. 배포용 문서 암호 해제 하는 문제에요!

자세한 설명을 적기에는 너무 길어서, 풀이코드랑 참고 자료 링크만 업로드 합니다,,

https://cdn.hancom.com/link/docs/한글문서파일형식_배포용문서_revision1.2.pdf

https://groups.google.com/forum/#!topic/hwp-foss/d2KL2ypR89Q

위 두 글을 참고하면 배포용 문서로 부터 SHA1 해시를 추출할 수 있습니다.
아래는 배포용 문서로 부터 SHA1 해시를 추출하는 코드입니다.

#include<stdio.h>
#include<wchar.h>
#include<ole2.h>
#include<windows.h>
#define HWPTAG_DISTRIBUTE_DOC_DATA 0x1C
#pragma comment( lib, "ole32.lib" )

int main() {
	/*본 제품은 한글과컴퓨터의 한글 문서 파일(.hwp) 공개 문서를 참고하여 개발하였습니다.*/

	wchar_t Dir[] = L"Z:\\HWP_Master.hwp";
	IStorage *pStg = NULL;
	IEnumSTATSTG *eStg,*eStg2;
	STATSTG stgstruct = { 0 }, stgstruct2 = { 0 };
	IStream* pStream = NULL;
	IStorage *pSubStg;
	BYTE *pBuf;
	DWORD Tmp;
	BYTE A,B;

	if (StgOpenStorage(Dir, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE , NULL, 0, &pStg) == S_OK) {
		pStg->EnumElements(0, NULL, 0, &eStg);
		while (eStg->Next(1, &stgstruct, NULL) == S_OK) {
			if (stgstruct.type == STGTY_STORAGE) {
				if (wcsncmp(stgstruct.pwcsName, L"ViewText", 8) == 0) {
					pStg->OpenStorage(stgstruct.pwcsName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0, &pSubStg);
					pSubStg->EnumElements(0, NULL, 0, &eStg2);
					while (eStg2->Next(1, &stgstruct2, NULL) == S_OK) {
						if (stgstruct2.type == STGTY_STREAM) {
							pSubStg->OpenStream(stgstruct2.pwcsName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
							pBuf = (PBYTE)calloc(1, stgstruct2.cbSize.LowPart);
							pStream->Read(pBuf, stgstruct2.cbSize.LowPart, NULL);

							Tmp = pBuf[3] << 24 | pBuf[2] << 16 | pBuf[1] << 8 | pBuf[0];

							if ((Tmp & 0x3ff) == HWPTAG_DISTRIBUTE_DOC_DATA) {
								//printf("%x", (Tmp >> 20)); //Size... 인데.. HWPTAG_DISTRIBUTE_DOC_DATA 에서는 항상 256. 아니면 혼종.
								//위 주석과 같은 이유로 확장 레코드에 대한 확인 필요 X
								pBuf += 4;
								Tmp = pBuf[3] << 24 | pBuf[2] << 16 | pBuf[1] << 8 | pBuf[0];

								srand(Tmp);
								for (int i = 0, B = 0; i < 256; i++, B--) {
									if (B == 0) {
										A = rand() & 0xff;
										B = (rand() & 0xf) + 1;
									}
									if (i >= 4)
										pBuf[i] ^= A;
								}
								printf("SHA1 Hash : ");
								for (int i = 0; i < 80; i += 2)
									printf("%c", pBuf[4 + (pBuf[0] & 0xF)+i]);
								printf("\n");
							}
						}
					}
				}
			}
		}
	}
	return 0;
}

이렇게 추출한 SHA1 해시를 SHA1 복호화 사이트에 넣어주면 플래그가 나옵니다.
hwp_master