Skip to Main Content
July 13, 2023

Modeling Malicious Code: Hacking in 3D

Written by Zach Bevilacqua
Penetration Testing Purple Team Adversarial Detection & Countermeasures Red Team Adversarial Attack Simulation Research


Attackers are always looking for new ways to deliver or evade detection of their malicious code, scripts, executables, and other tools that will allow them to access a target. We on the Tactical Awareness and Countermeasures (TAC) team at TrustedSec strive to keep up with attacker techniques and look ahead to develop potential evolutions in tactics and behavior. This is especially useful when we perform Purple Team engagements—we can keep our actions fresh and push the envelope on emulating attacker behavior.

In following this ideology, I set out to look for some novel, first-stage techniques that might allow initial access or execution without raising too much suspicion. A colleague of mine, Andrew Schwartz (@4ndr3w6S), suggested to look for file types that could be leveraged for malicious purposes that also have default associations with Windows applications. This led us down a path of many interesting file types. Amazingly, countless file extensions end up being some sort of archive, such as .cab or .zip files. There were several interesting file types, but I decided to dig into the .3mf file format. This file type is a common 3D model format that's natively supported by Windows. By default, it associates with the Paint 3D app but is widely used in a variety of 3D modeling software.


Initially, I needed to understand the file structure. The .3mf file type has a pre-existing association in Windows with the Paint 3D viewer. In order to examine and understand the file type, I decided to expertly craft a 3D model in Paint 3D and save it as a .3mf file.

Figure 1 - Expertly Crafted 3D Model

Using a hex editor to view the model above shows the magic bytes 50 4B or PK at the beginning of the file. Alternately, the file command will identify the model as a ZIP archive.

Figure 2 - Hex Editor View of .3mf File Header
Figure 3 - File Command Output for .3mf File

The following example is the directory tree for the amazing 3D model I created. We have some textures here because I colorized some of the objects and added a text layer. However, at the bare minimum, a .3mf file must have the .rels, [Content_Types].xml, and 3dmodel.model files.

Figure 4 - Directory Structure of .3mf File

For reference, a simple cube model has the following directory and file structure:

Figure 5 - Directory Structure of Simple Cube Model .3mf File

The elite move would be to get execution straight from the Paint 3D app as it parses the file. Unfortunately, I couldn't find any way to abuse the loading logic to execute code. However, this format is great for smuggling data as there is no real file structure or data integrity for the archive. This means that you can just drop any file into it and ship it off. The best part is that the file will still load in Paint 3D!

Figure 6 - 3D Cube Renders With Nonstandard Directory and Files

Though this doesn't offer much in the way of protection from an analyst, if you're going to smuggle some malicious code into an environment, code stored using this method may bypass off-the-shelf detection signatures.

But wait, there's more we can do to increase our OPSEC! A great option is to hide our data with the existing coordinates that define the 3D shapes of the model. This technique is steganography. In short, steganography is hiding information inside of another medium. A common example of steganography is storing a file inside of an image. Our method is going to be very similar, but instead of embedding information in each pixel, we can use the spatial coordinates within the .3mf file to store our data. Using one of the required files in the archive, the 3dmodel.model file, we can simply hide our payload as decimal values. Let's take a look at the basic cube version of this file.

Figure 7 - File Contents of 3dmodel.model File

In the highlighted section above, we have floating point numbers representing the X, Y, and Z coordinates of the cube. We could use the triangles section, though the values aren't consistent enough to easily hold the data we want.


Enough of that, let's get to the hacker stuff.

Ultimately, an attacker is going to want to execute code on a target system. While there are a multitude of tactics and techniques to choose from, we will opt to embed some shellcode that, when executed, will beacon back to our command and control (C2) server. This is a fairly standard activity that grants an attacker access to the compromised machine. I'll be using the Havoc Framework as my C2 for testing purposes. Once you have the Havoc teamserver set up and a listener started, generating the shellcode is fairly simple. Go to the Attack menu, then click Payload. On the next screen, choose the options you'd like, and make sure to select Windows Shellcode in the dropdown menu.

Figure 8 - Havoc Payload Generation Menu

I've made a Python script to help with the process of creating all of the properties and converting bytes from my shellcode into model coordinate values to blend in with the other values.

Figure 9 - Helper Script Inserting Code to .3mf File

The hard part will come when we want to extract the embedded data. Since we added all of the coordinate tags, we need to make sure we don't pull them in when putting the shellcode back together. When adding the shellcode to the model, the .3mf file is zipped using no compression so that the XML can be read as plaintext directly from the file itself. There is also a tag added to the beginning and ending of the added section to make finding the desired data easier. An example of this added data can be found below: the data is the basic cube model from before with a boot tag added for the first stage loader to find.

Figure 10 - Added Values

The first stage loader reads the .3mf file until it encounters the given tag, then reads that section as XML and converts each coordinate value into bytes until the full shellcode is reassembled. At this point, the shellcode can be loaded with any generic shellcode loader. One of my current favorite execution methods is a .lnk file from inside a mounted .iso container. This technique has been recently used by major threat groups to evade Mark-of-the-Web and deliver malware via email.

Figure 11 - Example Execution

Prevention and Detection

This type of ingress and execution is tough to detect before the execution stage. Due to the nature of reading the contents of a file and then executing code in memory, it doesn't leave many logs. However, there are several detection opportunities for attacks such as this.

  • Starting from the outside, organizations should make sure they are filtering attachments from email. This will vary depending on the organization's business requirements, but most likely there is no business need to receive executable file types (.exe, .dll, etc.) and scripting files (.ps1, .bat, .com, etc.).
    • For example, organizations in the healthcare sector probably don't need to receive .3mf files, but manufacturing or fabrication organizations may need to accept that format.
    • A good approach is to create an allowlist of known file types that have a business requirement and filter out all other file types. You can always update the list of allowed types should business requirements change.
  • Depending on the method of delivery, organizations would want to have detections for execution techniques such as scripting (.bat, .ps1, .py, .js, etc.) and containerized execution (.zip, .iso, .one, etc.).

The example above uses a delivery method of an ISO archive and launching the shellcode loader with a Windows shortcut, ultimately spawning a Havoc agent. While not the focus here, the execution stage offers additional detection opportunities for defenders.