This is the second part in a series of posts showing how we can use my unpack.py script to find quite a bit of useful information about a CryptoLocker variant. This post will analyse the unpacked payload that we found in part one.
The previous post in this series, Analysing CryptoLocker with unpack.py: Initial Analysis (part 1), used unpack.py and found that the initial malware sample created another, suspended instance of itself which it then injected with sections from an uncompressed PE file before running it. This post picks up from where part one left off and shows how we can analyse the PE file that was injected in to the new process.
We can start off with the usual static analysis on the uncompressed PE file, by running strings(1) and the like. Running strings didn’t show anything overly amazing — mainly just Win32 API function names. Running strings -el (to show strings consisting of 16-bit little endian characters) however, showed the random-looking domain name that was in DNS queries in the packet capture. It also showed the following:
S:(ML;;NRNWNX;;;LW) EnableLUA SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System InstallDate SOFTWARE\Microsoft\Windows NT\CurrentVersion MachineGuid SOFTWARE\Microsoft\Cryptography DigitalProductId Global\ 4runas
That first line looks like an ACE (Access Control Entry — found in access control lists) string, and from looking at ACE Strings (Windows), it looks like a mandatory label (ML) entry, with no read (NR), no write (NW) and no execute (NX) mandatory label rights.
The second line looks like a registry key, and indeed it is (see EnableLUA):
EnableLUA
specifies whether Windows® User Account Controls (UAC) notifies the user when programs try to make changes to the computer. UAC was formerly known as Limited User Account (LUA).
It also looks like it might be gathering some system information (InstallDate, MachineGuid, and DigitalProductId) to report back.
It gets better though — since that unpacked PE file was used to overwrite a new process, let’s try taking that uncompressed PE file and running it with unpack.py and see what we get. First, we’ll take a copy of the uncompressed memory on the shared drive:
[code autolinks=”false”]
$ mkdir 1300
$ cp -a filename.exe.memblk0xb60000.decomp 1300/1300.exe
[/code]
Now let’s run it:
[code autolinks=”false”]
e:
cd 1300
..\unpack.py 1300.exe > inf.log 2> inf-stderr.log
[/code]
Now this was the code used to overwrite the second instance of filename.exe, so we’ll expect this to start another explorer.exe instance and then exit. Checking the Windows security event log:
[code autolinks=”false”]
A new process has been created: New Process ID: 3896 Image File Name: C:\Python27\python.exe Creator Process ID: 3476
A new process has been created: New Process ID: 3904 Image File Name: \Device\VBoxMiniRdr\vboxsrv\cuckoo1\1300\1300.exe Creator Process ID: 3896
A new process has been created: New Process ID: 3912 Image File Name: C:\WINDOWS\explorer.exe Creator Process ID: 3904
A process has exited: Process ID: 3904 Image File Name: \Device\VBoxMiniRdr\vboxsrv\cuckoo1\1300\1300.exe
A process has exited: Process ID: 3896 Image File Name: C:\Python27\python.exe
A new process has been created: New Process ID: 1280 Image File Name: C:\WINDOWS\system32\vssadmin.exe Creator Process ID: 3912
A process has exited: Process ID: 1280 Image File Name: C:\WINDOWS\system32\vssadmin.exe
[/code]
That’s good — that is exactly what we expected to see, in that it is exactly the same as when we ran the original filename.exe file, only without the initial process. What we’ve essentially done, is just ran the second filename.exe process ourselves.
Checking unpack.py‘s log output for this process, we see another little gem:
[code autolinks=”false”]
[-] Dumping 38928 bytes of encrypted memory at 0xf73688 to 1300.exe.memblk0xf73688.enc
[*] <3904:3908> 0x4014ba: CryptDecrypt(0x15c9a0,0x0,0x1,0x0,0xf73688,0x9800 (38912)) = 1
[-] Dumping 38912 bytes of decrypted memory at 0xf73688 to 1300.exe.memblk0xf73688.dec
[/code]
The process decrypted some memory. It then goes on to create the explorer.exe process in a suspended state, allocate some memory inside it, write to that memory, and then resume the main thread in the new process (the unpack.py log output for the CreateProcess() call shows, after the ‘:’, the return value (1 for ‘true’), then the handle of the new process (0x80), the handle of its main thread (0x84), the process id of the new process (3912) and the thread id of its main thread (3916)):
[code autolinks=”false”]
[*] <3904:3908> 0x404035: CreateProcess("C:\WINDOWS\explorer.exe","",0x4): 1 (0x80, 0x84, <3912:3916>)
[-] CREATE_SUSPENDED. Hooking ResumeThread() (1)
[*] <3904:3908> 0x401d2d: VirtualAllocEx(0x80,0x0,0x17000 (94208),0x3000,0x004) = 0x90000
[*] <3904:3908> 0x401daa: WriteProcessMemory(0x80,0x90000,0xf8fac8,0x17000,0x12fb88): 1
[-] Dumping 94208 bytes of memory at 3904:0xf8fac8 written to 3912:0x90000 to 1300.exe.memblk0x90000-3912.wpm
[*] <3904:3908> 0x7c83290f: ResumeThread(0x84)
[-] New suspended process (pid 3912) resumed
[*] <3904:3908> Exit process event for C:\vboxsrv\cuckoo1\1300\1300.exe: 0x0
[*] Terminating
[D] Number of created processes: 1
[/code]
This time, it isn’t process hollowing. It creates a new explorer.exe and allocates new memory within it, which it then writes to — it doesn’t overwrite explorer‘s own PE sections like it did with the second filename.exe process that it started. Consequently the memory that was written to explorer.exe isn’t necessarily going to be a PE file. I would, however, expect it to be executable code, as its intention is presumably to run malicious code inside an innocent looking explorer.exe process. Let’s see if either the decrypted data or the written memory are recognisable:
[code autolinks=”false”]
$ file *.dec *.wpm
1300.exe.memblk0xf73688.dec: data
1300.exe.memblk0x90000-3912.wpm: PE32 executable (GUI) Intel 80386, for MS Windows
[/code]
Right. So the decrypted data wasn’t, but the memory that is written to explorer.exe is a PE file. The file command didn’t recognise the decrypted memory, but we’ll take a peek at it and see if we can recognise it, or if it contains anything interesting:
[code autolinks=”false”]
$ hexdump -C 1300.exe.memblk0xf73688.dec |head
00000000 41 50 33 32 18 00 00 00 e8 97 00 00 8b 88 ee 21 |AP32………..!|
00000010 00 2c 01 00 d9 65 e2 12 4d 38 5a 90 38 03 66 02 |.,…e..M8Z.8.f.|
00000020 04 09 71 ff 81 b8 c2 91 01 40 c2 15 e6 01 06 1c |..q……@……|
00000030 0e 1f ba f8 03 b4 09 cd 21 b8 eb 4c 00 0a 54 68 |……..!..L..Th|
00000040 69 73 20 70 72 39 6f 67 9e 61 6d 1d 63 1c 6e 7d |is pr9og.am.c.n}|
00000050 3f 74 9e 62 65 bf 3d 75 7e 61 69 06 44 4f 53 f8 |?t.be.=u~ai.DOS.|
00000060 6d 6f 0e 64 65 2e 0d 25 0a 24 30 43 d0 43 25 05 |mo.de..%.$0C.C%.|
00000070 5e 94 22 4b 0d 60 04 8f bf e0 f8 82 a9 08 d5 f1 |^."K.`……….|
00000080 9a 0c e1 f8 d1 88 9d 5a 7c c8 43 91 3e d8 22 80 |…….Z|.C.>.".|
00000090 1f b3 e4 30 15 97 34 3e 4a 2a 33 28 7c e5 54 b8 |…0..4>J*3(|.T.|
[/code]
The block of memory isn’t a PE file, but there are strings in there that make it look awfully similar to one. If we look at the parameters to the CryptDecrypt() call, we can see that the memory buffer that is decrypted is 0x9800 (38,912) bytes at address 0xf73688. The WriteProcessMemory() call was writing memory from address 0xf8fac8. I’d be guessing that something is manipulating that decrypted data slightly to produce the PE file that is written to the block of newly allocated memory in the new explorer.exe process.
If we wanted to investigate this further, we could open the 1300.exe file in a debugger, set a breakpoint at 0x4014b4 (the 0x4014ba reported by unpack.py is the return address from CryptDecrypt() — looking at it in a debugger you can see that the CryptDecrypt() call is at 0x4014b4), run it, make a note of the memory address and size passed to CryptDecrypt(), step over the call, then create a memory breakpoint on the decrypted memory block and continue execution.
That memory breakpoint will show us where the decrypted memory is being accessed from, which will give us a starting point to investigate how it gets from that block of decrypted data in to a block of data suitable for injecting in to explorer.exe. I’m not going to go much further with this as the point of this exercise is to see how much analysis we can do with unpack.py, but it does demonstrate how unpack.py provides useful information that will give you a head start when it comes to analysing the malware sample with a debugger.
Also, that AP32 at the start looks like it could be some sort of magic number / identifier, and after doing an Internet search for it, I found Jaromir Horejsi (and only just short of three years after it was written!).
‘, byJaromir is describing some interesting behaviour and while reading that, I was pondering whether the self-debugging behaviour could be responsible for the considerable number of unhandled exceptions that unpack.py was logging. This would be something that would need further investigation, and is a potential candidate for extra unpack.py functionality.
As far as observable behaviour goes, we notice that the pop-up window is missing, so it’s probably fair to say (although not for certain because there could be things like command line arguments and the like dictating the behaviour of this second process that we’ve just run manually) that it is the initial filename.exe process that is responsible for displaying that.
The DNS queries for random looking hosts in that weird domain are still happening though, and they continue to happen after our process has exited, so I’d be guessing that that functionality is implemented in the new explorer.exe process. There is a Sysinternals tool — Process Monitor — that we could use if we wanted to confirm that.
Right, let’s have a look at the PE file that was injected in to the new explorer.exe process. If we run that with unpack.py, we get the following:
If we use objdump to dump the PE file’s header information, then we can see that the PE file obviously wasn’t intended to be launched directly:
…
Export Address Table — Ordinal Base 5046364
Invalid Export Address Table rva (0x6f0073) or entry count (0x630069)[Ordinal/Name Pointer] Table
Invalid Name Pointer Table rva (0x740066) or entry count (0x6f0072)
…
In fact, those RVA addresses and entry counts look remarkably like 16-bit ASCII characters. Let’s see what that ordinal base looks like in hex:
$ bc bc 1.06.95 Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc. This is free software with ABSOLUTELY NO WARRANTY. For details type `warranty'. obase=16 5046364 4D005C
More 16-bit ASCII character look-alikes.
Since we can’t investigate the code injected in to explorer.exe by running it, let’s make a tweak to unpack.py so that WinAppDbg will attach to any child processes that get created — the second explorer.exe in this case — and then rerun the second filename.exe process.
Open unpack.py in a text editor and change the following line (toward the end):
debug.execl(filename,bFollow = False)
to:
debug.execl(filename,bFollow = True)
Now we’ll run 1300.exe through unpack.py again. This time unpack.py will stay running because the WinAppDbg debugger will attach to the explorer.exe process which 1300.exe creates, and that explorer.exe process doesn’t exit (at least not voluntarily).
Right, so with unpack.py sitting there, and something now spitting out DNS requests for that weird domain name, let’s have a look at what unpack.py has discovered so far:
[code autolinks=”false”]
[*] <3880:3656> Create process event for pid 3880 (C:\vboxsrv\cuckoo1\1300\1300\1300.exe)
[/code]
We have the process id of the 1300.exe process that we just started (3880). Make a note of this because WinAppDbg will now attach to child processes, so we will start to see output pertaining to the explorer.exe process which 1300.exe starts. We can tell which process is doing what because unpack.py logs the process id in its messages.
First up, we see what we saw last time we ran 1300.exe — it decrypting memory, creating a new suspended explorer.exe process, grabbing some memory inside explorer.exe, writing that odd PE file to it, and then resuming the new explorer.exe process.
[code autolinks=”false”]
[-] Dumping 38928 bytes of encrypted memory at 0xf73688 to 1300.exe.memblk0xf73688.enc
[*] <3880:3656> 0x4014ba: CryptDecrypt(0x15c9a0,0x0,0x1,0x0,0xf73688,0x9800 (38912)) = 1
[-] Dumping 38912 bytes of decrypted memory at 0xf73688 to 1300.exe.memblk0xf73688.dec
…
[*] <3880:3656> 0x404035: CreateProcess("C:\WINDOWS\explorer.exe","",0x4): 1 (0x80, 0x84, <3884:3888>)
[-] CREATE_SUSPENDED. Hooking ResumeThread() (1)
[*] <3880:3656> 0x401d2d: VirtualAllocEx(0x80,0x0,0x17000 (94208),0x3000,0x004) = 0x90000
[*] <3880:3656> 0x401daa: WriteProcessMemory(0x80,0x90000,0xf8fac8,0x17000,0x12fb88): 1
[-] Dumping 94208 bytes of memory at 3880:0xf8fac8 written to 3884:0x90000 to 1300.exe.memblk0x90000-3884.wpm
[*] <3880:3656> 0x7c83290f: ResumeThread(0x84)
[-] New suspended process (pid 3884) resumed
[/code]
Except that because WinAppDbg is now attaching to child processes, we also see the following (note the pid of 3884):
[code autolinks=”false”]
[*] <3884:3888> Create process event for pid 3884 (C:\WINDOWS\explorer.exe)
[/code]
We then see another load of ‘Load DLL’ messages, which I’d be guessing are the DLLs required for explorer.exe‘s imports.
Next, 1300.exe exits, which we were expecting, as that is behaviour that we’ve seen before. It was also logged in the Windows security event log when we first ran filename.exe.
[code autolinks=”false”]
[*] <3880:3656> Exit process event for C:\vboxsrv\cuckoo1\1300\1300\1300.exe: 0x0
[/code]
Then we see something a bit more exciting:
[code autolinks=”false”]
[-] Dumping 95776 bytes of encrypted memory at 0x1809cc0 to 1300.exe.memblk0x1809cc0.enc
[*] <3884:3888> 0x9149a: CryptDecrypt(0xc6aa8,0x0,0x1,0x0,0x1809cc0,0x17610 (95760)) = 1
[-] Dumping 95760 bytes of decrypted memory at 0x1809cc0 to 1300.exe.memblk0x1809cc0.dec
[/code]
Note the process id of 3884 — that is the new explorer.exe process.
We then see a call to IsDebuggerPresent(), which matches what Jaromir was saying when describing the self-debugging behaviour, in which case it could be interesting to stop unpack.py from changing IsDebuggerPresent()s return value to 0.
Remember before when the Windows event log was telling us that explorer.exe started vssadmin.exe? Well now that we’ve told WinAppDbg to attach to child processes (bFollow = True), we can actually see explorer.exe starting vssadmin.exe in unpack.py‘s output. unpack.py logs the arguments passed to the CreateProcess() call, which means that we can see the command line passed to vssadmin.exe:
[code autolinks=”false”]
[*] <3884:3892> 0x98bed: CreateProcess("","vssadmin.exe Delete Shadows /All /Quiet",0x8000000): 1 (0x184, 0x190, <3904:3908>)
[*] <3904:3908> Create process event for pid 3904 (C:\WINDOWS\system32\vssadmin.exe)
[/code]
That confirms what we suspected — that vssadmin.exe is being used to delete shadow copies.
We then see schannel.dll being loaded, which I believe is used for things like HTTPS connections:
[code autolinks=”false”]
[*] <3884:3888> Load DLL event: C:\WINDOWS\system32\schannel.dll
[/code]
Back to the decrypted memory — let’s have a look and see what that looks like:
[code autolinks=”false”]
$ file 1300.exe.memblk0x1809cc0.dec
1300.exe.memblk0x1809cc0.dec: data
[/code]
Hmm — nicht gut. The size of the file, 95,760 bytes, is awfully similar to that of 1300.exe too, at 95,744 bytes.
Again, let’s dump the first part of the file and see if it looks interesting:
[code autolinks=”false”]
$ hexdump -C 1300.exe.memblk0x1809cc0.dec |head
00000000 00 76 01 00 4d 5a 90 00 03 00 00 00 04 00 00 00 |.v..MZ……….|
00000010 ff ff 00 00 b8 00 00 00 00 00 00 00 40 00 00 00 |…………@…|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |…………….|
*
00000040 f0 00 00 00 0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c |…………!..L|
00000050 cd 21 54 68 69 73 20 70 72 6f 67 72 61 6d 20 63 |.!This program c|
00000060 61 6e 6e 6f 74 20 62 65 20 72 75 6e 20 69 6e 20 |annot be run in |
00000070 44 4f 53 20 6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 |DOS mode….$…|
00000080 00 00 00 00 07 4f 99 d3 43 2e f7 80 43 2e f7 80 |…..O..C…C…|
00000090 43 2e f7 80 58 b3 5c 80 56 2e f7 80 58 b3 69 80 |C…X.\.V…X.i.|
[/code]
It certainly does. That looks like a standard PE header, only with four bytes immediately before it. Let’s remove those four bytes and see if it then looks like a PE file. Although before we do that, we have four bytes (a 32-bit value) at the start of some data. What type of 32-bit value often precedes some data? A length field. Let’s take 0x00017600 (which is 0x00760100 byte-swapped from little endian to big endian) and convert it to decimal, and see what it looks like:
$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty’.
ibase=16
00017600
95744
95744 hey — recognise that? It’s the size of 1300.exe, and 16 bytes short of our decrypted memory block (95,760 bytes). Could that 32-bit value be there to tell the receiving code that the PE file is 95,744? The decryption algorithm likely works with blocks of data, which will explain why 95,760 bytes were decrypted, given that 95,760 is divisible by 16 bytes, which is 128 bits — a common block size for encryption algorithms.
Let’s crop the four bytes from the start of that file and see if we then end up with a PE file:
[code autolinks=”false”]
$ mkdir 3884
$ dd if=1300.exe.memblk0x1809cc0.dec of=3884/3884.exe bs=1 skip=4
95756+0 records in
95756+0 records out
95756 bytes (96 kB, 94 KiB) copied, 0.191101 s, 501 kB/s
$ file 3884/3884.exe
3884/3884.exe: PE32 executable (GUI) Intel 80386, for MS Windows
[/code]
Smashing. If we run 3884.exe through unpack.py too, we see pretty much the same behaviour as we saw for 1300.exe. Note, however, that the same behaviour was only repeated after reverting the virtual machine to the saved, clean, snapshot. If the second explorer.exe process was still running, then 3884.exe behaved differently. So, did this 3884.exe process spit out the same files as 1300.exe?:
[code autolinks=”false”]
$ md5sum -b *.dec *.wpm 3884/*.dec 3884/*.wpm |sort
5394465287700c5a8f46292fafd78b33 *3884/3884.exe.memblk0x1809cc0.dec
75705201c288fee7db592752cc4d51df *1300.exe.memblk0x1809cc0.dec
e7a863210d3a6c3ec83a123c49d23f4d *1300.exe.memblk0x90000-3884.wpm
e7a863210d3a6c3ec83a123c49d23f4d *3884/3884.exe.memblk0x90000-3688.wpm
f2346e1c66df58c6b2931597777346d3 *1300.exe.memblk0xf73688.dec
f2346e1c66df58c6b2931597777346d3 *3884/3884.exe.memblk0xf73688.dec
[/code]
Interesting. One of the decrypted blocks is the same, and the PE file that was written to explorer.exe is the same. The other decrypted block is different. Let’s see how different:
[code autolinks=”false”]
$ od -tx1 1300.exe.memblk0x1809cc0.dec > hexdump
$ od -tx1 3884/3884.exe.memblk0x1809cc0.dec > 3884/hexdump
$ diff hexdump 3884/hexdump1c1
< 0000000 00 76 01 00 4d 5a 90 00 03 00 00 00 04 00 00 00
—
> 0000000 0c 76 01 00 4d 5a 90 00 03 00 00 00 04 00 00 00
[/code]
The only difference is the first byte, being the least significant byte of the 32-bit length value before the start of the PE header (the 0x4d 0x5a bytes starting at offset 4). That different first byte is adding twelve on to the length, which could be 16 bytes (an extra encryption block) minus a 4 byte length field.
Okay, so it looks like 1300.exe, being the second filename.exe process that the original exe file started, is where most of the functionality is — it starts a new process, explorer.exe, and injects decrypted code in to it. The initial executable file, filename.exe, looks like an unpacking stub which pops up a window to make it look like it had a function other than that of creating a malicious explorer.exe process — that is, that original exe file is a trojan designed to hide the unpacking code. The unpacked code then injects decrypted code in to a new explorer.exe process in an attempt to make it look more legitimate, before exiting. At this stage it is looking like the code injected in to explorer.exe is responsible for DNS queries for A records in a strange .org domain.
If you enjoyed this, then join me for part three where we’ll look at some of the network communications and introduce a new feature to unpack.py.