I didn’t get to spend a lot of time on the FLARE-On challenges this year, but I worked through the first 2 and used it as an opportunity to learn Radare2 a little better.
Here’s my solution to challenge 2.
$ md5 DudeLocker.exe BusinessPapers.doc MD5 (DudeLocker.exe) = 4c262d5ab4bf8586d303bfa91a05b42b MD5 (BusinessPapers.doc) = 857da52bdcaec6da473a59e2fe66df0a
Here is my r2 project file if you want to follow along with my notes. It should be unzipped in “.config/radare2/projects”, edit the challenge2 file and change the line “e file.path = /Users/yourname/challenge02/DudeLocker.exe” to the correct path for DudeLocker.exe, then open it with “r2 -p challenge2”.
Challenge 2 provides two files: an executable and a DOC based on the file extension. However, it appears the DOC has been encrypted. The challenge should be to decrypt this document file by reversing the method of encryption used by DudeLocker.exe.
$ file DudeLocker.exe BusinessPapers.doc DudeLocker.exe: PE32 executable for MS Windows (console) Intel 80386 32-bit BusinessPapers.doc: data $ xxd BusinessPapers.doc | head 00000000: 1bb9 6ebb 82c9 3d9a e290 fd8d c59a d40f ..n...=......... 00000010: adb8 9376 511e 5f3a 37b5 d08a b5b8 02ae ...vQ._:7....... 00000020: 503c 1926 676e 5ecd 48c4 3def d8ed 8ef1 P<.&gn^.H.=..... 00000030: 24af 5f4d 1c0c 7281 d583 a1a5 f729 5c2b $._M..r......)\+
Running strings on the binary found a few interesting things. Obviously, it’s using a lot of Crypt* functions, which confirms its capabilities. There’s also JFIF, which could indicate a JPEG file, and there is a .JPG filename in the unicode strings. The unicode strings also show some things that may indicate anti-RE elements. Since OutputDebugString is imported, it could be that these are debug messages, rather than output directly to the console.
$ gstrings DudeLocker.exe | more ...snip... OutputDebugStringW FindResourceW ...snip... CryptAcquireContextW CryptReleaseContext CryptDeriveKey CryptDestroyKey CryptSetKeyParam CryptGetKeyParam CryptGetHashParam CryptEncrypt CryptCreateHash CryptHashData CryptDestroyHash ...snip... JFIF ...snip... $ gstrings -el DudeLocker.exe | more Briefcase \ve_vant_ze_money.jpg Obviously you're not a reverse engineer... I'm out of my element
Looking at some info about the file in radare2, it looks like JFIF is in the .rsrc section, which would go along with the theory that it’s a JPEG stored in a resource.
$ r2 DudeLocker.exe [0x00401b80]> aaaa [0x00401b80]> izz~JFIF vaddr=0x00404066 paddr=0x00001866 ordinal=075 sz=5 len=4 section=.rsrc type=ascii string=JFIF [0x00401b80]> iS [Sections] idx=00 vaddr=0x00401000 paddr=0x00000400 sz=3072 vsz=2956 perm=m-r-x name=.text idx=01 vaddr=0x00402000 paddr=0x00001000 sz=1536 vsz=1390 perm=m-r-- name=.rdata idx=02 vaddr=0x00403000 paddr=0x00001600 sz=512 vsz=250 perm=m-rw- name=.data idx=03 vaddr=0x00404000 paddr=0x00001800 sz=136192 vsz=135776 perm=m-r-- name=.rsrc 4 sections
Running the file, doesn’t produce any output. But if we run it with the Sysinternals DebugView open, we can see one of the output strings.
Radare puts us in the entry0 function, which is a small stub with a call to sub.SHELL32.dll_SHGetFolderPathW_9a0 based on the auto-analysis. We can go into thus function and get a quick overview of what it’s doing by looking at all the "calls" it makes.
[0x00401b80]> s sub.SHELL32.dll_SHGetFolderPathW_9a0 [0x004019a0]> pdf~call │ 0x004019e3 ff1594204000 call dword [sym.imp.SHELL32.dll_SHGetFolderPathW] ; "N%" @ 0x402094 ; get User Desktop path │ │ 0x004019f4 ff1538204000 call dword [sym.imp.KERNEL32.dll_lstrlenW] ; "x#" @ 0x402038 │ │ 0x00401a1a e8e1f5ffff call sym.buildPathToBriefcase │ │ 0x00401a3b ff1588204000 call dword [sym.imp.KERNEL32.dll_CreateFileW] ; sym.imp.KERNEL32.dll_CreateFileW │ ││ 0x00401a4e ff1560204000 call dword [sym.imp.KERNEL32.dll_CloseHandle] ; "F#" @ 0x402060 │ │ │ 0x00401a61 ff1540204000 call dword [sym.imp.KERNEL32.dll_OutputDebugStringW] ; sym.imp.KERNEL32.dll_OutputDebugStringW │ └───> 0x00401a6e e8cdf5ffff call sym.checkVolumeSerialNumber ; compares SN to 0x7dab1d35 │ │││ 0x00401a87 ff1540204000 call dword [sym.imp.KERNEL32.dll_OutputDebugStringW] ; sym.imp.KERNEL32.dll_OutputDebugStringW │ │ ││ 0x00401aa1 ff157c204000 call dword [sym.imp.KERNEL32.dll_GetProcessHeap] ; sym.imp.KERNEL32.dll_GetProcessHeap │ │ ││ 0x00401aa8 ff1584204000 call dword [sym.imp.KERNEL32.dll_HeapAlloc] ; sym.imp.KERNEL32.dll_HeapAlloc │ │ ││ 0x00401ac2 e879feffff call sym.decryptKeyString ; fills seedData │ │ ││ 0x00401ada e8a1f5ffff call sym.generateEncryptionKey │ ││ ││ 0x00401aff e8fcf7ffff call sym.encryptFilesInFolder │ │││││ 0x00401b1a ff1534204000 call dword [sym.imp.KERNEL32.dll_lstrcatW] ; "l#" @ 0x402034 │ │││││ 0x00401b27 e8f4f6ffff call sym.writeRansomImage │ │││││ 0x00401b2f e8bcfbffff call sym.getVersion │ ││││││ 0x00401b44 e8d7fdffff call sym.setParameterInfo │ ││ ││ 0x00401b52 ff157c204000 call dword [sym.imp.KERNEL32.dll_GetProcessHeap] ; sym.imp.KERNEL32.dll_GetProcessHeap │ ││ ││ 0x00401b59 ff1580204000 call dword [sym.imp.KERNEL32.dll_HeapFree] ; sym.imp.KERNEL32.dll_HeapFree │ ││ ││ 0x00401b67 e8e4f5ffff call sym.cleanUpCrypto
This is after I had done my analysis on the file. The steps of the ransomware are as follows:
- Determine path to %USERPROFILE%\Desktop\Briefcase.
- If this folder doesn’t exist, print debug message “Obviously you’re not a reverse engineer…” and exit.
- Compare the hard drive serial number to 0x7dab1d35
- If the serial number doesn’t match, print debug message “I’m out of my element” and exit.
- Using the hard drive serial number, decrypt 37 bytes of data at 0x403000.
- Generate an AES encryption key using the above decrypted string.
- Encrypt each file in the folder.
- Write the JPEG resource to the “Briefcase” folder and set it as the Desktop wallpaper.
Step 5, where the byte array is decrypted does the following. It basically divides the counter by 4 and uses the remainder to choose one of the bytes of the volume serial number, then XORs the current byte in the string with that value.
│ ││ 0x0040195f 8b5508 mov edx, dword [ebp + encryptedKeyString] ; [0x8:4]=4 │ ││ 0x00401962 0355fc add edx, dword [ebp - counter] │ ││ 0x00401965 0fb60a movzx ecx, byte [edx] ; get current byte │ ││ 0x00401968 8b45fc mov eax, dword [ebp - counter] │ ││ 0x0040196b 33d2 xor edx, edx │ ││ 0x0040196d be04000000 mov esi, 4 │ ││ 0x00401972 f7f6 div esi ; divide counter by 4 │ ││ 0x00401974 8b4510 mov eax, dword [ebp + volumeSerialNumber] ; [0x10:4]=184 │ ││ 0x00401977 0fb61410 movzx edx, byte [eax + edx] ; select a byte from VSN based on remainder or division │ ││ 0x0040197b 33ca xor ecx, edx ; XOR current byte with above value │ ││ 0x0040197d 8b450c mov eax, dword [ebp + seedData] ; [0xc:4]=0xffff │ ││ 0x00401980 0345fc add eax, dword [ebp - counter] │ ││ 0x00401983 8808 mov byte [eax], cl ; store result in seedData │ └──< 0x00401985 ebc7 jmp sym.incrementCounter
The byte array below gets decrypted to “thosefilesreallytiedthefoldertogether” and is stored in “seedData”. I was going to write a Python version of this, but figured it would be much easier to just run DudeLocker.exe in a debugger and break right after the decryption function was called.
[0x00401940]> px 37 @ 0x403000 - offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF 0x00403000, 4175 c40e 507b c211 506e d918 5471 c704 Au..P{..Pn..Tq.. 0x00403010, 4174 ce19 4175 ce1b 5a71 cf18 4769 c41a At..Au..Zq..Gi.. 0x00403020, 5069 c318 47 Pi..G
Before sym.decryptKeyString:
After sym.decryptKeyString:
In Step 6, the CBC mode AES key object is generated. This uses the SHA1 hash of the decrypted string to generate the key.
[0x00401580]> s sym.generateEncryptionKey [0x00401080]> pdf~call │ 0x00401097 ff151c204000 call dword [sym.imp.ADVAPI32.dll_CryptAcquireContextW] ; "l$" @ 0x40201c │ │ 0x004010a1 ff1578204000 call dword [sym.imp.KERNEL32.dll_GetLastError] ; sym.imp.KERNEL32.dll_GetLastError │ ││ 0x004010ba ff151c204000 call dword [sym.imp.ADVAPI32.dll_CryptAcquireContextW] ; "l$" @ 0x40201c │ │ │ 0x004010e5 e896000000 call sym.deriveAESKey │ ││││ 0x00401104 ff1518204000 call dword [sym.imp.ADVAPI32.dll_CryptReleaseContext] ; sym.imp.ADVAPI32.dll_CryptReleaseContext │ │││ 0x0040111c ff150c204000 call dword [sym.imp.ADVAPI32.dll_CryptSetKeyParam] ; sym.imp.ADVAPI32.dll_CryptSetKeyParam │ ││││ 0x00401132 e819000000 call sym.cleanUpCrypto [0x00401080]> s sym.generateEncryptionKey [0x00401080]> pdf ╒ (fcn) sym.generateEncryptionKey 196 │ sym.generateEncryptionKey (int phKey, int phProv, int seedData, int seedDataLength); │ ; var int cipher_mode_CBC @ ebp-0x4 │ ; arg int phKey @ ebp+0x8 │ ; arg int phProv @ ebp+0xc │ ; arg int seedData @ ebp+0x10 │ ; arg int seedDataLength @ ebp+0x14 │ ; CALL XREF from 0x00401ada (sym.main_start) │ 0x00401080 55 push ebp │ 0x00401081 8bec mov ebp, esp │ 0x00401083 51 push ecx │ 0x00401084 c745fc010000. mov dword [ebp - cipher_mode_CBC], 1 │ 0x0040108b 6a00 push 0 │ 0x0040108d 6a18 push 0x18 ; "@" ; PROV_RSA_AES │ 0x0040108f 6a00 push 0 │ 0x00401091 6a00 push 0 │ 0x00401093 8b450c mov eax, dword [ebp + phProv] ; [0xc:4]=0xffff │ 0x00401096 50 push eax │ 0x00401097 ff151c204000 call dword [sym.imp.ADVAPI32.dll_CryptAcquireContextW] ; "l$" @ 0x40201c ...snip... │ └───└─> 0x004010ce 8b5514 mov edx, dword [ebp + seedDataLength] ; [0x14:4]=0 │ │ │ 0x004010d1 52 push edx │ │ │ 0x004010d2 8b4510 mov eax, dword [ebp + seedData] ; [0x10:4]=184 │ │ │ 0x004010d5 50 push eax │ │ │ 0x004010d6 6810660000 push 0x6610 ; CALG_AES_256 │ │ │ 0x004010db 8b4d0c mov ecx, dword [ebp + phProv] ; [0xc:4]=0xffff │ │ │ 0x004010de 8b11 mov edx, dword [ecx] │ │ │ 0x004010e0 52 push edx │ │ │ 0x004010e1 8b4508 mov eax, dword [ebp + phKey] ; [0x8:4]=4 │ │ │ 0x004010e4 50 push eax │ │ │ 0x004010e5 e896000000 call sym.deriveAESKey ...snip... │ │││└─> 0x0040110e 6a00 push 0 │ │││ 0x00401110 8d55fc lea edx, [ebp - cipher_mode_CBC] │ │││ 0x00401113 52 push edx │ │││ 0x00401114 6a04 push 4 ; KP_MODE │ │││ 0x00401116 8b4508 mov eax, dword [ebp + phKey] ; [0x8:4]=4 │ │││ 0x00401119 8b08 mov ecx, dword [eax] │ │││ 0x0040111b 51 push ecx │ │││ 0x0040111c ff150c204000 call dword [sym.imp.ADVAPI32.dll_CryptSetKeyParam] ; sym.imp.ADVAPI32.dll_CryptSetKeyParam ...snip... [0x00401080]> s sym.deriveAESKey [0x00401180]> pdf~call │ │ 0x004011ac ff1500204000 call dword [sym.imp.ADVAPI32.dll_CryptCreateHash] ; sym.imp.ADVAPI32.dll_CryptCreateHash │ ││ │ 0x004011d4 ff1520204000 call dword [sym.imp.ADVAPI32.dll_CryptHashData] ; sym.imp.ADVAPI32.dll_CryptHashData │ ││││ 0x004011e2 ff1524204000 call dword [sym.imp.ADVAPI32.dll_CryptDestroyHash] ; ",%" @ 0x402024 │ │││ │ 0x004011ff ff1514204000 call dword [sym.imp.ADVAPI32.dll_CryptDeriveKey] ; sym.imp.ADVAPI32.dll_CryptDeriveKey │ │││ │ 0x00401211 ff1524204000 call dword [sym.imp.ADVAPI32.dll_CryptDestroyHash] ; ",%" @ 0x402024 [0x00401180]> pdf ╒ (fcn) sym.deriveAESKey 158 │ sym.deriveAESKey (int encryptionKey, int pointerToCryptoServProv, int algId_AES256, int seedData, int seedDataLength); │ ; var int hHash @ ebp-0x8 │ ; var int deriveSuccess @ ebp-0x1 │ ; var int local_0h @ ebp-0x0 │ ; arg int encryptionKey @ ebp+0x8 │ ; arg int pointerToCryptoServProv @ ebp+0xc │ ; arg int algId_AES256 @ ebp+0x10 │ ; arg int seedData @ ebp+0x14 │ ; arg int seedDataLength @ ebp+0x18 │ ; CALL XREF from 0x004010e5 (sym.generateEncryptionKey) │ 0x00401180 55 push ebp │ 0x00401181 8bec mov ebp, esp │ 0x00401183 83ec08 sub esp, 8 ...snip... | └──> ;-- sym.createHash: │ └──> 0x0040119b 8d45f8 lea eax, [ebp - hHash] │ │ 0x0040119e 50 push eax │ │ 0x0040119f 6a00 push 0 │ │ 0x004011a1 6a00 push 0 │ │ 0x004011a3 6804800000 push 0x8004 ; CALG_SHA1 │ │ 0x004011a8 8b4d0c mov ecx, dword [ebp + pointerToCryptoServProv] ; [0xc:4]=0xffff │ │ 0x004011ab 51 push ecx │ │ 0x004011ac ff1500204000 call dword [sym.imp.ADVAPI32.dll_CryptCreateHash] ; sym.imp.ADVAPI32.dll_CryptCreateHash ...snip... | ││└──> ;-- sym.hashSeedData: │ ││└──> 0x004011c6 6a00 push 0 │ ││ │ 0x004011c8 8b5518 mov edx, dword [ebp + seedDataLength] ; [0x18:4]=64 ; "@" │ ││ │ 0x004011cb 52 push edx │ ││ │ 0x004011cc 8b4514 mov eax, dword [ebp + seedData] ; [0x14:4]=0 │ ││ │ 0x004011cf 50 push eax │ ││ │ 0x004011d0 8b4df8 mov ecx, dword [ebp - hHash] │ ││ │ 0x004011d3 51 push ecx │ ││ │ 0x004011d4 ff1520204000 call dword [sym.imp.ADVAPI32.dll_CryptHashData] ; sym.imp.ADVAPI32.dll_CryptHashData ...snip... | │││└──> ;-- sym.deriveKeyUsingHash: │ │││└──> 0x004011ed 8b4508 mov eax, dword [ebp + encryptionKey] ; [0x8:4]=4 │ │││ │ 0x004011f0 50 push eax │ │││ │ 0x004011f1 6a01 push 1 ; CRYPT_EXPORTABLE │ │││ │ 0x004011f3 8b4df8 mov ecx, dword [ebp - hHash] │ │││ │ 0x004011f6 51 push ecx │ │││ │ 0x004011f7 8b5510 mov edx, dword [ebp + algId_AES256] ; [0x10:4]=184 │ │││ │ 0x004011fa 52 push edx │ │││ │ 0x004011fb 8b450c mov eax, dword [ebp + pointerToCryptoServProv] ; [0xc:4]=0xffff │ │││ │ 0x004011fe 50 push eax │ │││ │ 0x004011ff ff1514204000 call dword [sym.imp.ADVAPI32.dll_CryptDeriveKey] ; sym.imp.ADVAPI32.dll_CryptDeriveKey
In Step 7, each file in the “Briefcase” folder is iterated over, and folders are entered recursively. For each file, the filename is converted to lowercase then from WideChar to MultiByte, then the MD5 of this format of the filename is taken. This MD5 hash is used as the initialization vector for encrypting the file.
│ ││└───> 0x00401af0 8d95c0fdffff lea edx, [ebp - str_pathToBriefcase] │ ││ ││ 0x00401af6 52 push edx │ ││ ││ 0x00401af7 8d45ec lea eax, [ebp - ptr_CryptoServiceProvider] │ ││ ││ 0x00401afa 50 push eax │ ││ ││ 0x00401afb 8d4de8 lea ecx, [ebp - encryptionKey] │ ││ ││ 0x00401afe 51 push ecx │ ││ ││ 0x00401aff e8fcf7ffff call sym.encryptFilesInFolder [0x00401aa0]> s sym.encryptFilesInFolder [0x00401300]> pdf~call │ 0x0040132b ff1530204000 call dword [sym.imp.KERNEL32.dll_lstrcpyW] ; "`#" @ 0x402030 │ 0x0040133c ff1534204000 call dword [sym.imp.KERNEL32.dll_lstrcatW] ; "l#" @ 0x402034 │ 0x00401350 ff154c204000 call dword [sym.imp.KERNEL32.dll_FindFirstFileW] ; sym.imp.KERNEL32.dll_FindFirstFileW │ ││ 0x00401379 ff1548204000 call dword [sym.imp.KERNEL32.dll_lstrcpynW] ; "T#" @ 0x402048 │ ││ 0x00401394 e8f7050000 call sym.checkIfFolder ; checks dwAttributes of FindFileData to indicate a directory │ ││││ 0x004013ca e831fcffff call sym.buildPathToBriefcase │ ││││ 0x004013e1 e81affffff call sym.encryptFilesInFolder ; recursively encrypt files in subdirs │ │ ││ 0x00401403 e888050000 call sym.checkIfFolder │ │ │││ 0x00401428 e8d3fbffff call sym.buildPathToBriefcase │ │ │││ 0x00401437 ff1538204000 call dword [sym.imp.KERNEL32.dll_lstrlenW] ; "x#" @ 0x402038 │ │ │││ 0x0040144a ff159c204000 call dword [sym.imp.USER32.dll_CharLowerW] ; ":$" @ 0x40209c │ │ │││ 0x00401456 ff157c204000 call dword [sym.imp.KERNEL32.dll_GetProcessHeap] ; sym.imp.KERNEL32.dll_GetProcessHeap │ │ │││ 0x0040145d ff1584204000 call dword [sym.imp.KERNEL32.dll_HeapAlloc] ; sym.imp.KERNEL32.dll_HeapAlloc │ │││││ 0x00401474 e8b7020000 call sym.zeroOutBuffer │ │ │││ 0x00401497 ff155c204000 call dword [sym.imp.KERNEL32.dll_WideCharToMultiByte] ; sym.imp.KERNEL32.dll_WideCharToMultiByte │ │ │││ 0x004014ad e8be020000 call sym.setIVToFilenameMD5 │ │││││ 0x004014cb e830000000 call sym.encryptFile │ │ ││ 0x004014e9 ff1550204000 call dword [sym.imp.KERNEL32.dll_FindNextFileW] ; sym.imp.KERNEL32.dll_FindNextFileW [0x00401500]> s sym.setIVToFilenameMD5 [0x00401770]> pdf~call │ 0x0040179d ff1508204000 call dword [sym.imp.ADVAPI32.dll_CryptGetKeyParam] ; sym.imp.ADVAPI32.dll_CryptGetKeyParam ; get block length in bits │ │ 0x004017b3 e898f9ffff call sym.cleanUpCrypto │ │ 0x004017d1 ff157c204000 call dword [sym.imp.KERNEL32.dll_GetProcessHeap] ; sym.imp.KERNEL32.dll_GetProcessHeap │ │ 0x004017d8 ff1584204000 call dword [sym.imp.KERNEL32.dll_HeapAlloc] ; sym.imp.KERNEL32.dll_HeapAlloc │ ││ 0x004017f7 e854f9ffff call sym.cleanUpCrypto │ ││ 0x00401810 e81bffffff call sym.zeroOutBuffer │ ││ 0x0040182b ff1500204000 call dword [sym.imp.ADVAPI32.dll_CryptCreateHash] ; sym.imp.ADVAPI32.dll_CryptCreateHash │ ││││ 0x00401887 ff1520204000 call dword [sym.imp.ADVAPI32.dll_CryptHashData] ; sym.imp.ADVAPI32.dll_CryptHashData │ │││││ 0x00401895 ff1524204000 call dword [sym.imp.ADVAPI32.dll_CryptDestroyHash] ; ",%" @ 0x402024 │ │││││ 0x004018b8 ff1504204000 call dword [sym.imp.ADVAPI32.dll_CryptGetHashParam] ; sym.imp.ADVAPI32.dll_CryptGetHashParam │ ││││││ 0x004018d6 ff150c204000 call dword [sym.imp.ADVAPI32.dll_CryptSetKeyParam] ; sym.imp.ADVAPI32.dll_CryptSetKeyParam │ │││││││ 0x004018e8 ff157c204000 call dword [sym.imp.KERNEL32.dll_GetProcessHeap] ; sym.imp.KERNEL32.dll_GetProcessHeap │ │││││││ 0x004018ef ff1580204000 call dword [sym.imp.KERNEL32.dll_HeapFree] ; sym.imp.KERNEL32.dll_HeapFree │ │││││││ 0x00401901 e84af8ffff call sym.cleanUpCrypto │ ││││││ 0x00401911 ff1524204000 call dword [sym.imp.ADVAPI32.dll_CryptDestroyHash] ; ",%" @ 0x402024 [0x00401770]> s sym.encryptFile [0x00401500]> pdf~call │ 0x0040153f ff1508204000 call dword [sym.imp.ADVAPI32.dll_CryptGetKeyParam] ; sym.imp.ADVAPI32.dll_CryptGetKeyParam ; getBlockLength │ │ 0x00401555 e8f6fbffff call sym.cleanUpCrypto │ │ 0x004015ae ff1588204000 call dword [sym.imp.KERNEL32.dll_CreateFileW] ; sym.imp.KERNEL32.dll_CreateFileW │ ││ 0x004015ca ff156c204000 call dword [sym.imp.KERNEL32.dll_GetFileSize] ; " #" @ 0x40206c │ │││ 0x004015f4 ff1588204000 call dword [sym.imp.KERNEL32.dll_CreateFileW] ; sym.imp.KERNEL32.dll_CreateFileW │ ││││ 0x00401611 ff157c204000 call dword [sym.imp.KERNEL32.dll_GetProcessHeap] ; sym.imp.KERNEL32.dll_GetProcessHeap │ ││││ 0x00401618 ff1584204000 call dword [sym.imp.KERNEL32.dll_HeapAlloc] ; sym.imp.KERNEL32.dll_HeapAlloc │ ││││││ 0x0040163f ff1564204000 call dword [sym.imp.KERNEL32.dll_ReadFile] ; ":#" @ 0x402064 │ ││││││ 0x00401672 ff1528204000 call dword [sym.imp.ADVAPI32.dll_CryptEncrypt] ; sym.imp.ADVAPI32.dll_CryptEncrypt │ ││││││ 0x00401690 ff1568204000 call dword [sym.imp.KERNEL32.dll_WriteFile] ; ".#" @ 0x402068 │ │││ 0x004016b2 ff1560204000 call dword [sym.imp.KERNEL32.dll_CloseHandle] ; "F#" @ 0x402060 │ │││ 0x004016c2 ff1560204000 call dword [sym.imp.KERNEL32.dll_CloseHandle] ; "F#" @ 0x402060 │ │││ 0x004016d4 ff157c204000 call dword [sym.imp.KERNEL32.dll_GetProcessHeap] ; sym.imp.KERNEL32.dll_GetProcessHeap │ │││ 0x004016db ff1580204000 call dword [sym.imp.KERNEL32.dll_HeapFree] ; sym.imp.KERNEL32.dll_HeapFree
Step 8 writes the following image to “Briefcase” and sets it as the Desktop wallpaper:
This level of analysis was overkill for this challenge, but I wanted to get more practice with Radare2, so I tried to document everything I could in the EXE since it was pretty straightforward. In the end, to do the decryption, we need to create an AES key using the SHA1 hash of “thosefilesreallytiedthefoldertogether” as the shared secret and the MD5 hash of the lowercase of the filename as the initialization vector.
I tried using the built in python Cipher libraries, but they only support a key length of 16, 24, and 32 bytes, and a SHA1 hash is 20 bytes. I found the wincrypto library. The builtin decrypt function uses an IV = ‘\0’ * 16 so I had to call it manually. This was the python script I ended up using.
#!/usr/bin/python from Crypto.Cipher import AES from wincrypto import CryptCreateHash, CryptHashData, CryptDeriveKey from wincrypto.constants import CALG_SHA1, CALG_AES_256 import hashlib import binascii import sys # read in the file filename = sys.argv[1] f = open(filename, 'r') data_enc = f.read() # decrypt the secret used for the AES key key_str_enc = "4175c40e507bc211506ed9185471c7044174ce194175ce1b5a71cf184769c41a5069c31847" key_str_enc = binascii.a2b_hex(key_str_enc) vsn = "351dab7d" vsn = binascii.a2b_hex(vsn) key_str_dec = "" # divide the counter by 4 and use the remainder to determine which byte of the VSN to use for XOR for i in range(0, len(key_str_enc)): remain = (i % 4) key_str_dec += chr(ord(key_str_enc[i]) ^ ord(vsn[remain])) # hash secret and create the key sha1_hasher = CryptCreateHash(CALG_SHA1) CryptHashData(sha1_hasher, key_str_dec) aes_key = CryptDeriveKey(sha1_hasher, CALG_AES_256) # get the hash of the filename to create the IV m = hashlib.md5() m.update(filename.lower()) iv = m.hexdigest() iv = binascii.a2b_hex(iv) # decrypt the file data data_dec = AES.new(aes_key.key, mode=AES.MODE_CBC, IV=iv).decrypt(data_enc) # write out the file fo = open(filename + ".out", 'wb') fo.write(data_dec) fo.close()
$ python DudeUnlocker.py BusinessPapers.doc $ file BusinessPapers.doc* BusinessPapers.doc: data BusinessPapers.doc.out: JPEG image data, JFIF standard 1.01
I was going to share my radare2 project file, but somehow it got corrupted and lost all my work. Luckily, I had r2 open in another window and was able to save a working copy of the project from that one. Definitely learned to make backup copies based on this experience. Also, don’t hit Ctrl-D to exit out. It wrecks the project file.