31번 문제
문제 :
[ down ] 버튼을 누르면 pdf파일이 다운 받아진다.
Hello_SuNiNaTaS.pdf 파일을 열면 "Hello, Nice to meet you. Do you wanna get a Key?"라는 문구가 나온다.
그 외에는 특별한 것이 없다.
문제가 'Forensic'의 한 종류이고, 제목이 'PDF Analysis'인 것을 보니,
문제를 풀기 위해서는 pdf를 분석해야 하나 보다.
이 문제를 풀기 위해 pdf 구조에 대해서 공부한 내용이다.
링크 주소 : 2023.01.27 - [포렌식] - [포렌식] PDF 파일 구조
공부한 내용을 간단히 요약하자면,
PDF는 Header, Body, Cross Reference Table, Trailer로 구성되어 있다.
Header | 8 bytes (%PDF-버전정보) 25 50 44 46 2D 31 2E 33 |
Body | 실제 문서의 정보를 포함하는 오브젝트와 그 객체들을 구성하는 트리 형태. 오브젝트 시작 : n1 n2 obj, 끝 : endobj이다. |
Cross Reference Table | 각 객체를 참조할 때 사용되는 테이블. 키워드 : xref |
Trailer | 파일 끝에 위치하며. Trailer 시작은 trailer, 끝은 %%EOF로 끝난다. |
문제로 돌아와서 해당 PDF를 HxD 프로그램으로 열면
중간 부분에
프로그래밍 언어가 보인다.
복사를 해서 정리를 했더니, 아래와 같은 javascript 코드가 나왔다.
var Base64 = {
keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
decode : function (input) {
for (var ah = 0; ah < (input.length); ah++){
input=input.replace("'+'", "");
}
var rlLwarzv = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
rlLwarzv = rlLwarzv + String.fromCharCode(chr1);
if (enc3 != 64) {
rlLwarzv = rlLwarzv + String.fromCharCode(chr2);
}
if (enc4 != 64) {
rlLwarzv = rlLwarzv + String.fromCharCode(chr3);
}
}
eval(rlLwarzv);
}
}
Base64.decode("'Vm0'+'wd2Qy'+'UXlW'+'a1pP'+'VldS'+'WFYw'+'ZG9WV'+'ll3W'+'kc5V'+'01Wb'+'DNXa2'+'M1VjF'+'Kc2JET'+'lhhMU'+'pUV'+'mpGS'+'2RHVk'+'dX'+'bFpOY'+'WtFe'+'FZtc'+'EdZV'+'1JIV'+'mtsa'+'QpSb'+'VJPW'+'W14R'+'00x'+'WnR'+'NWH'+'BsU'+'m1S'+'SVZ'+'tdF'+'dVZ'+'3Bp'+'Umx'+'wd1'+'ZXM'+'TRkM'+'VZX'+'WkZ'+'kYV'+'JGS'+'lVU'+'V3N'+'4Tk'+'ZaS'+'E5V'+'OVhR'+'WEJ'+'wVW'+'01Q'+'1dW'+'ZHNa'+'RFJa'+'ClYx'+'WlhWM'+'jVLVm1'+'FeVVtR'+'ldh'+'a1p'+'MVj'+'BaV'+'2RF'+'NVZ'+'PV2'+'hSV'+'0VK'+'VVd'+'XeG'+'FTM'+'VpX'+'V2t'+'kVm'+'EwN'+'VVD'+'azF'+'XV2'+'xoV'+'01X'+'aHZ'+'WMG'+'RLU'+'jJO'+'SVR'+'sWm'+'kKV'+'0do'+'NlZ'+'HeG'+'FZV'+'k5I'+'VWt'+'oU2'+'JXa'+'FdW'+'MFZ'+'LVl'+'ZkW'+'E1U'+'QlR'+'NV1'+'JYV'+'jI1'+'U2Fs'+'SllV'+'bkJEY'+'XpGV1'+'kwWm'+'9XR0'+'V4Y'+'0hK'+'V01'+'uTjN'+'aVmR'+'HUjJ'+'GRwp'+'WbGN'+'LWW'+'toQm'+'VsZH'+'NaR'+'FJa'+'Vms1'+'R1R'+'sWm'+'tZV'+'kp1U'+'WxkV'+'01GW'+'kxWb'+'FprV'+'0Ux'+'VVF'+'sUk'+'5WbH'+'BJVm'+'pKMG'+'ExZH'+'RWbk'+'pYYm'+'tKRV'+'lYcE'+'dWMW'+'t3Cl'+'dtOV'+'hSMF'+'Y1WV'+'VWN'+'FYw'+'MUh'+'Va3'+'hXT'+'VZw'+'WFl'+'6Rm'+'Fjd3'+'BqUj'+'J0T'+'FZXM'+'DFRM'+'kl4W'+'khOY'+'VJGS'+'mFWa'+'kZLU'+'1ZadG'+'RHOV'+'ZSbH'+'AxV'+'Vd4'+'a1Y'+'wMU'+'cKV'+'2t4'+'V2J'+'GcH'+'JWMG'+'RTU'+'jFw'+'SGR'+'FNV'+'diS'+'EJK'+'Vmp'+'KMF'+'lXS'+'XlS'+'WGh'+'UV0'+'dSW'+'Vlt'+'dGF'+'SVm'+'xzV'+'m5k'+'WFJ'+'sbD'+'VDb'+'VJI'+'T1Z'+'oU0'+'1GW'+'TFX'+'VlZ'+'hVT'+'FZeA'+'pTWH'+'BoU0'+'VwV1'+'lsaE'+'5lRl'+'pxUm'+'xkam'+'QzQn'+'FVak'+'owVE'+'ZaWE'+'1UUm'+'tNa'+'2w0'+'VjJ'+'4a1'+'ZtR'+'XlV'+'bGh'+'VVm'+'xae'+'lRr'+'WmF'+'kR1'+'ZJV'+'Gxw'+'V2E'+'zQj'+'VWa'+'ko0'+'CmE'+'xWX'+'lTb'+'lVL'+'VVc'+'1V1'+'ZXS'+'kZW'+'VFZ'+'WUm'+'tVN'+'VVG'+'RTl'+'QUT'+'09'");
코드를 보면 아래의 글자들을 Base64로 디코딩하는 것이다.
Vm0wd2QyUXlWa1pPVldSWFYwZG9WVll3Wkc5V01WbDNXa2M1VjFKc2JETlhhMUpUVmpGS2RHVkdXbFpOYWtFeFZtcEdZV1JIVmtsaQpSbVJPWW14R00xWnRNWHBsUm1SSVZtdFdVZ3BpUmxwd1ZXMTRkMVZXWkZkYVJGSlVUV3N4TkZaSE5VOVhRWEJwVW01Q1dWZHNaRFJaClYxWlhWMjVLVm1FeVVtRldha1pMVjBaV2RFNVZPV2hSV0VKVVdXeGFTMVpXV2tkVmEwNVVDazFXV2xoV01XaHZWMGRLUjJOSVRsWmkKV0doNlZHeGFZVk5IVWtoU2JXaFdWMFZLVlZkWE1UQlRNV1JYVjI1U2FsSllVbkJEYXpGV1kwWm9XR0V4Y0hKV01uTjNaVmRHUjJGRwpWbGNLWWtoQmVsZHNaRFJaVms1R1RsWmtZVkp1UWxkV01GWkxWbFprV0UxVVFsUk5WbHBJVmpKMGExZHRWbkpYYmtKRVlYcEdWMWt3CldtOVhSMFY1WVVWNFYwMUhVa3hXTVZwWFl6RmFjd3BqUjJ0TFZXMDFRMkl4WkhOYVJGSmFWakZLU1ZadGRHOVZSbHAxVVd4a1YwMUcKV2t4V2JGcHJWMGRTUjFwSGRFNVdiSEJKVmpKMFlXSXlSWGhUV0dSWVltdGFSVmxzVm5kWFJsbDVDbVJIT1ZoU01GWTFXVlZhVTFZeApTWHBoU0VwV1lsaE5lRlpxUmxkamQzQnFVakowVEZaWE1UUmtNa2w0VjJ4a1ZtRXlVbGhVVmxaelRrWmFkR1ZJVGxwV2EzQjVWako0CmExWXlTblVLVVc1V1ZXSkZWVFZWUmtVNVVGRTlQUT09
해당 문자열을 Base64 디코딩해 주는 사이트에서 돌려 보았다.
결괏값을 여러 번 재디코딩하는 과정을 하게 되면 최종적으로 아래와 같은 문구가 뜨게 된다.
헛발질을 한 거다... 😢
다시 다른 방법을 찾기위해서 HxD에서 해당 pdf에서 키워드를 검색해 보면,
✔ Head는 맨 앞 %PDF (25 50 44 46) 이고
✔ Body : 0 0 obj ... endobj 이런 식으로 오브젝트들 구성
✔ Cross Reference Table : xref (78 72 65 66) 는 3DB5에 위치
✔ Trailer : trailer(74 72 61 69 6C 65 72) - %%EOF(25 25 45 4F 46) 는 4027~40AF에 위치
근데 이걸로 끝나는 게 아니라
그 뒤에 2 0 obj 로 새로 오브젝트가 시작되고 뒤에도 xref table, trailer가 온다.
✔ Cross Reference Table : xref (78 72 65 66)
✔ Trailer : trailer(74 72 61 69 6C 65 72) - %%EOF(25 25 45 4F 46)
PDF가 한 개더 들어 있는 거 같은데
HxD로 따로 저장도 해보았는데 잘 되지 않았다.
이 두 PDF를 분리하는 것은 혼자 힘으로 어려워서 PDF 분석 툴인 PDFStreamDumper를 이용하기로 했다.
PDFStreamDumper로 해당 PDF를 열었을 때 모습이다.
무엇인가 분석이 되었다.
아까 보았던 자바스크립도 코드도 따로 분석이 되어있다.
그리고 쭉 하나씩 보개 되면, 중간에 %PDF-1.7로 시작하는 부분이 있다.
HxD에서는 안 나왔던 건데... 신기하다.
해당 부분을 저장하기 위해서는 오른쪽 버튼을 클릭한뒤 [ Save Decoompressed Stream ] 버튼을 눌러서 해당 파일을 저장한다.
저장한 파일을 열면 빈화면이 나온다.
HxD로도 열어도 특별한 것이 없어서,
PDFStreamDumper로 다시 열어보았다.
[예]를 누르면 아래와 같이 파일이 분석된다.
뭔가 제대로 안 되는 거 같아서
인터넷에서 PDF를 무료로 잠금해제 하는 사이트에서 파일 암호를 해제해 보았다.
사이트 주소 : https://smallpdf.com/kr/unlock-pdf
해당 파일 분석을 다시 해보았더니
이렇게 Flag 값이 나왔다!
해당 플래그를 md5로 변환해서 제출하면
드디어 해결! 😆