RE Adventures : Malware.. ugh
Kapil Kapre | Wednesday, Feb 27, 2013

So, the other day I was surfing the web when I almost got infected by some nasty piece of ill written malware. I was hacking on some code in VS and for whatever reason VS always brings up Internet Explorer by default when you look up API docs on MSDN (my default is Google Chrome). I've kept track of IE's numerous improvements over the years, but never found it good enough to use as my primary browser. Mentally with IE I always feel like I'm surfing with a giant red bull's-eye on my back. I don't trust Google either but I know that if anyone is going to keep me from being compromised its the advertising company who wants all of my data. Why would they allow competition? :D I use Chrome for regular surfing but I never sign into any account anywhere that exposes my real identity. I use Firefox for mail, etc. with logons that use my real name and I've configured it such that when FF closes it cleans out all cookies and other cached data. Ofcource its quite possible that Google and everyone else has already figured out what I'm doing - they can co-relate internet traffic via IP packet origin - but hey, what's life without optimism ?

It used to be that simply switching to a more secure browser was enough to stay somewhat safe on the internet but now apart from the browsers themselves the web has been visited upon by two scourges, namely Flash and Java. Like any sane person I have neutered any appendage that Java could attach to any installed browser. Unfortunately I have had to enable Java in one particular browser install that I only use for banking because those idiots decided to use Java. Using something insecure for performing a secure transaction. Does it get any more ironic than that?

Anyway, I opened up a new tab in IE to do a quick google search on something and landed on some random forum that was probably hosting ads via a compromised ad server. In the background an executable was auto-downloaded via yet another flash vulnerability into my %USERPROFILE% folder. I know that IE runs in Low Integrity mode and even the plugins are afforded very limited write access to the file system (supposed to be anyway). Nevertheless the payload was delivered and executed.

Now for many years, I have stopped using any kind of real-time malware/anti-virus/spyware scanning software. I run a customized windows install with all kinds of oddities turned on/off. I obsess to some extent over OS performance and my current OCD target is wasteful I/O. I've profiled the crap out of all my drivers for latency issues and disabled/replaced dozens of drivers which were causing a high ISR & DPC count/execution time among other things. I've also eliminated SuperFetch, Indexing and any other service that would cause unnecessary I/O (I'm well aware of what SuperFetch is and I find that I have zero use for it). OK, so the executable ran - into an AV - and crashed. Heres the OS report.

Windows Event Viewer Error Dialog
AV at offset 0x00019ef0

I submitted the executable to some anti-virus folks and went on with my work. But my OCD yet again managed to mess with my head. Maybe I could just take a peek to see what it did ! Luckily (if you want to call having a malware executable on your HDD 'lucky') I had saved a copy of the executable. The first thing that I thought of doing was checking if the exe was packed and/or held any other interesting stuff. I had some PE unpackers and analyzers that I tested on this executable but all signs including pEiD deep scan analysis pointed to this being a plain unpacked Visual C++ binary. Interestingly, I happened to give a cursory glance at it via a hex editor and saw some digital certificate strings. I hacked together a tool to dump the certificate. Its nothing special, all you do is open the executable, call ImageEnumerateCertificates + ImageGetCertificateData on it and then simply dump the raw data. Windows' certutil.exe can make sense of the dumped data. In case you're interested, here's the output Cert.txt. Hmm.. malware with a valid Authenticode cert from 'Eagle Point Software Corporation'. Great. I loaded up the executable in IDA and got to work. As it turns out, the malware never even got to make any use of that possibly stolen certificate. Here are the program segments.

.text  00401000 0040A000 R . X . L para 0001 public CODE 32 0000 0000 0004 FFFFFFFF FFFFFFFF
.idata 0040A000 0040B000 . W X . L para 0002 public CODE 32 0000 0000 0004 FFFFFFFF FFFFFFFF
.idata 0040B000 0040B104 R . . . L para 0003 public DATA 32 0000 0000 0004 FFFFFFFF FFFFFFFF
.rdata 0040B104 0040E000 R . . . L para 0003 public DATA 32 0000 0000 0004 FFFFFFFF FFFFFFFF
.data  0040E000 0050F000 R W . . L para 0004 public DATA 32 0000 0000 0004 FFFFFFFF FFFFFFFF
.rsrc  0050F000 0051F000 R . . . L para 0005 public DATA 32 0000 0000 0004 FFFFFFFF FFFFFFFF

A cursory glance at the raw strings it contained showed a bunch of time/date strings. I don't know too much about malware but this seemed like it would fit right into what a keylogger or some other kind of logger which produced ASCII time linked data would need.

.rdata:0040B38C 00000009 C HH:mm:ss                                                                                         
.rdata:0040B398 00000014 C dddd, MMMM dd, yyyy                                                                              
.rdata:0040B3AC 00000009 C MM/dd/yy                                                                                         
.rdata:0040B3C0 00000009 C December                                                                                         
.rdata:0040B3CC 00000009 C November                                                                                         
.rdata:0040B3D8 00000008 C October                                                                                          
.rdata:0040B3E0 0000000A C September                                                                                        
.rdata:0040B3EC 00000007 C August                                                                                           
.rdata:0040B3F4 00000005 C July                                                                                             
.rdata:0040B3FC 00000005 C June                                                                                             
.rdata:0040B404 00000006 C April                                                                                            
.rdata:0040B40C 00000006 C March                                                                                            
.rdata:0040B414 00000009 C February                                                                                         
.rdata:0040B420 00000008 C January                                                                                          
.rdata:0040B458 00000009 C Saturday                                                                                         
.rdata:0040B464 00000007 C Friday                                                                                           
.rdata:0040B46C 00000009 C Thursday                                                                                         
.rdata:0040B478 0000000A C Wednesday                                                                                        
.rdata:0040B484 00000008 C Tuesday                                                                                          
.rdata:0040B48C 00000007 C Monday                                                                                           
.rdata:0040B494 00000007 C Sunday                                                                                           
.rdata:0040B4D4 00000008 C FlsFree                                                                                          
.rdata:0040B4DC 0000000C C FlsSetValue                                                                                      
.rdata:0040B4E8 0000000C C FlsGetValue                                                                                      
.rdata:0040B4F4 00000009 C FlsAlloc                                                                                         
.rdata:0040B500 00000006 C e+000                                                                                            
.rdata:0040B508 0000000F C CorExitProcess                                                                                   
.rdata:0040C54F 00000005 C \a\b\t\n\v                                                                                       
.rdata:0040C568 0000005F C  !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~
.rdata:0040C6CF 00000005 C \a\b\t\n\v                                                                                       
.rdata:0040C6E8 0000005F C  !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~
.rdata:0040C7C8 0000001A C  Complete Object Locator'                                                                        
.rdata:0040C7E4 0000001D C  Class Hierarchy Descriptor'                                                                     
.rdata:0040C804 00000013 C  Base Class Array'                                                                               
.rdata:0040C818 0000001C C  Base Class Descriptor at (           

I did some static analysis (i.e. Euphemism for wasted time going through the assembly listing) and it looked like the payload it carried was encrypted. The executable starts up and decodes it using XOR 85h. ( decr[i] = encr[i] - (i+1) ^ 85h ) Heres the function that decrypts the payload.

.text:0040119C loc_40119C:                             ; CODE XREF: sub_401180+4Ej
.text:0040119C                 cmp     [ebp+var_4], 1430h
.text:004011A3                 jz      short loc_4011D0
.text:004011A5                 mov     ecx, [ebp+var_4]
.text:004011A8                 movsx   edx, byte_40E000[ecx]
.text:004011AF                 mov     eax, [ebp+var_4]
.text:004011B2                 add     eax, 1
.text:004011B5                 xor     eax, 85h
.text:004011BA                 sub     edx, eax
.text:004011BC                 mov     ecx, [ebp+var_4]
.text:004011BF                 mov     byte ptr unk_419EF0[ecx], dl    ; Store the decrypted data here (.data:00419EF0)
.text:004011C5                 mov     edx, [ebp+var_4]
.text:004011C8                 add     edx, 1
.text:004011CB                 mov     [ebp+var_4], edx
.text:004011CE                 jmp     short loc_40119C
.text:004011D0 ; ---------------------------------------------------------------------------
.text:004011D0 loc_4011D0: 

So the encrypted payload (initial anyway) is 0x1430 bytes located at 0x0040E000. It is then decoded and stored at 0x00419EF0. Hmm that seems familiar. The first segment is loaded at 00401000, and adding the offset of the AV (0x00019ef0) would make this location (0x00419EF0) the spot where the crashed happened. Furthermore we know that its located in the data segment which is marked as non-executable. Looks like system wide DEP policy that I enabled enforced this and terminated the malware. Win !

The OS prevented the malware from executing, but I was still curious what the malware actually intended to do. I started looking through the decrypted set of instructions. I came across something possibly interesting.

.data:0041A089 rdtsc
.data:0041A08B mov     ebx, eax
.data:0041A08D loc_41A08D:                             
.data:0041A08D rdtsc
.data:0041A08F sub     eax, ebx
.data:0041A091 ja      short loc_41A08D

rdtsc returns the time stamp counter into EDX:EAX. This set of instructions baffled me. It *seemed* like what they wanted to do was make sure the code wasn't being monitored via a debugger. The time stamp counter cannot be halted by the debugger, so the instruction sub eax,ebx should fetch a large number when this program runs inside a debugger versus when it runs outside of it. I didn't quite get what they're doing here though. I started poking around some more.

.data:0041A124 call    far ptr 9A91h:0B5E0A322h
.data:0041A12B call    far ptr 9A91h:0B4E0A300h
.data:0041A132 call    far ptr 9A91h:0B7E0A311h
.data:0041A139 call    far ptr 9A91h:0B6E0A326h
.data:0041A140 call    far ptr 9A91h:0B1E0A30Ah
.data:0041A147 call    far ptr 9A91h:0B0E0A30Bh
.data:0041A14E call    far ptr 9A91h:0B3E0A316h
.data:0041A155 call    far ptr 9A91h:0B2E0A30Ah
.data:0041A15C call    far ptr 9A91h:0BDE0A309h
.data:0041A163 call    far ptr 9A91h:0BCE0A300h
.data:0041A16A call    far ptr 9A91h:0BFE0A324h
.data:0041A171 call    far ptr 9A91h:0BEE0A309h
.data:0041A178 call    far ptr 9A91h:0B9E0A30Ch
.data:0041A17F call    far ptr 9A91h:0B8E0A304h
.data:0041A186 call    far ptr 9A91h:83E0A316h
.data:0041A18D call    far ptr 9A91h:82E0A311h
.data:0041A194 call    far ptr 9A91h:8DE0A30Dh
.data:0041A19B call    far ptr 9A91h:8CE0A332h
.data:0041A1A2 call    far ptr 1A2h:0ECA55665h
.data:0041A251 call    far ptr 9821h:0E0A3C410h
.data:0041A258 call    far ptr 9820h:0E0A3299Ah
.data:0041A25F call    far ptr 9823h:0E0A30A9Ah
.data:0041A266 call    far ptr 9822h:0E0A3049Ah
.data:0041A26D call    far ptr 982Dh:0E0A3019Ah
.data:0041A274 call    far ptr 982Ch:0E0A3299Ah
.data:0041A27B call    far ptr 982Fh:0E0A30C9Ah
.data:0041A282 call    far ptr 982Eh:0E0A3079Ah
.data:0041A289 call    far ptr 9829h:0E0A3179Ah
.data:0041A290 call    far ptr 9828h:0E0A3049Ah
.data:0041A297 call    far ptr 982Bh:0E0A3179Ah

I then stumbled onto a bunch of call-gates. A callgate is an Intel x86 mechanism to change privilege level of the CPU via user mode code. It allows a piece of OS code to be callable by both the OS and user-mode. I didn't quite know which call gates those were, but AFAIK there is no way to install your own call gate w/o help from a kernel mode component, because NT wont let you mess with the GDT without the appropriate privileges. Next I found a little dance that it did to find the memory location of the kernel.

.data:00419F67 mov     edx, large fs:30h            <- take PEB
.data:00419F6E mov     [ebp-4], edx
.data:00419F71 mov     eax, [ebp-4]      
.data:00419F74 mov     eax, [eax+0Ch]               <- take PEB + 0CH = LDR
.data:00419F77 mov     ecx, [eax+0Ch]               <- take LDR + 0ch = linked list pointing to loaded order of modules
.data:00419F7A mov     word ptr [ebp-142h], 79h
.data:00419F83 mov     word ptr [ebp-146h], 70h
.data:00419F8C mov     eax, [ecx+28h]               
.data:00419F8F mov     edx, eax                     
.data:00419F91 add     edx, 8                       
.data:00419F94 mov     dx, [edx]
.data:00419F97 cmp     dx, [ebp-142h]
.data:00419F9E jnz     short near ptr loc_419FC7+1
.data:00419FA0 mov     edx, eax
.data:00419FA2 add     edx, 0Ch
.data:00419FA5 mov     dx, [edx]
.data:00419FA8 cmp     dx, [ebp-146h]
.data:00419FAF jnz     short near ptr loc_419FC7+1
.data:00419FB1 add     eax, 0Eh
.data:00419FB4 mov     ax, [eax]
.data:00419FB7 cmp     ax, [ebp-146h]
.data:00419FBE jnz     short near ptr loc_419FC7+1
.data:00419FC0 and     edi, ebx
.data:00419FC2 fcomp5  st(1)
.data:00419FC4 rcl     cl, cl
.data:00419FC4 ; ---------------------------------------------------------------------------
.data:00419FC6 db 0DEh ; ¦
.data:00419FC7 ; ---------------------------------------------------------------------------
.data:00419FC7 loc_419FC7:                      

Unfortunately for our little guy here, I was running Windows 8 and the PEB structure has changed. At least that's what cdb tells me.

:000> dt nt!_peb
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 BitField         : UChar
   +0x003 ImageUsesLargePages : Pos 0, 1 Bit
   +0x003 IsProtectedProcess : Pos 1, 1 Bit
   +0x003 IsLegacyProcess  : Pos 2, 1 Bit
   +0x003 IsImageDynamicallyRelocated : Pos 3, 1 Bit
   +0x003 SkipPatchingUser32Forwarders : Pos 4, 1 Bit
   +0x003 IsPackagedProcess : Pos 5, 1 Bit
   +0x003 IsAppContainer   : Pos 6, 1 Bit
   +0x003 SpareBits        : Pos 7, 1 Bit
   +0x008 Mutant           : Ptr64 Void
   +0x010 ImageBaseAddress : Ptr64 Void
   +0x018 Ldr              : Ptr64 _PEB_LDR_DATA                   <---------- oops.. its no longer +0xc
   +0x020 ProcessParameters : Ptr64 _RTL_USER_PROCESS_PARAMETERS
   +0x028 SubSystemData    : Ptr64 Void

This was as far as I got. The program had other tiny OS related bugs in it, which meant the only way to test it was to run it inside a VM with a version of Windows compatible with it. I decided to leave that to a later time.....