Handling Stomps

Streetfight Research
4 min readDec 8, 2020

Intro

One of the document related malware issues that we had to think about when developing the Streetfight sandbox is the so called p-code stomp attack.

This attack involves exploiting the fact that Office documents can contain multiple representations of the same macro which can be manipulated individually without corrupting the entire document.

For an awesome writeup on the subject, you can check out Carrie Roberts blog.

Discussion

There have been a few attempts to detect this technique, many of which rely on performing a comparison of the two representations, be it by disassembling the embedded p-code or trying to compare artifacts like string constants.

These approaches are sensible, but didn’t exactly fit our use case when building the sandbox — additionally, the disassembler has a few known issues at the time of writing and the p-code specification has not been made public by Microsoft.

When searching for an answer, a piece of Carrie’s blog stuck out:

Note that this is an important caveat. A VBA stomped maldoc can only be executed using the same VBA version used to create the document. This limitation can be addressed by doing reconnaissance on a target prior to maldoc generation to determine the appropriate Office version to use or by generating maldocs with multiple Office versions and spraying them against the target.

Digging deeper, it seems that Microsoft used the compiled p-code representation to improve speed of loading and executing the macro on matching VBA versions. It appears that all we need to do is set the version of VBA used to compile the project to some illegal value and see which representation it decides to run.

If we can ensure that the code is not stomped, we can simply run the document through the sandbox twice, once with the version modified and once without.

Implementation

After a bit of digging, we determined that in order to change the VBA version, we had to modify the compound file element defined in the VBA_PROJECT stream.

Carrie Roberts was kind enough to provide some benign stomped samples for us to play with, so we decided to simply use a hex editor to set the Version field to a few values (like 0x0000) to see what would happen to our document under Word 2016.

As a first pass, we simply searched the binary file for the Reserved1 member and parsed around it, but soon realized that a tool from Outflank called EvilClippy can be modified to do the job.

EvilClippy is an offensive tool which will perform the p-code attack for us, even targeting specific versions of Office. The function ReplaceOfficeVersionInVBAProject is responsible for modifying the _VBA_PROJECT_STREAM of the input document to match the configuration of the target. Its prototype can be seen below:

private static byte[] 
ReplaceOfficeVersionInVBAProject(
byte[] moduleStream,
string officeVersion
)

The patch we used simply modified the switch statement to add a case for neuter which would set the project version to 0xFFFF (we also tried 0x0000 which empirically didn’t work).

case "neuter_ff":
version[0] = 0xFF;
version[1] = 0xFF;
break;

We expect that this will simply invalidate the p-code since it wont match any existing version of Office, causing the VBA code to run instead.

Result

Compiling and running EvilClippy is well documented in the Github repository. After applying our patch, we ran it with the 2016x32 sample from the examples repo above to match the Streetfight sensor configuration target.

We used the following syntax, which produced a new “neutralized” file 2016x32_word_msgbox_stomped_fakecode_EvilClippy.doc

EvilClippy.exe -t neuter_ff 2016x32_word_msgbox_stomped_fakecode.doc

Uploading both to the Streetfight cloud we can see that they differ in output, as hoped:

Conclusion

With some extra overhead, the modification of the _VBA_PROJECT stream is a viable way to handle mismatched p-code in a sandbox across all Office versions we tested. Sign up for the Office Sandbox waitlist or hire us to hack your stuff.

--

--