Editor’s Note: Elastic joined forces with Endgame in October 2019, and has migrated some of the Endgame blog content to elastic.co. See Elastic Security to learn more about our integrated security solutions.

Over the last few years, macro-based document attacks have been growing in popularity. With the rising cost of memory corruption based exploitation due to the required level of expertise and resources, attackers understand that they can accomplish similar results by just convincing users to click through a dialog box. This has consequently led to more and more security vendors adding protections and detections against these macro-based document attacks.

Enter Dynamic Data Exchange (DDE).

This newly published technique leveraging a legacy feature was quickly adopted by various groups such as Fin7, and has been employed in malware campaigns such as Vortex ransomware and Hancitor.

The best part about this new DDE attack vector is that it has all of the characteristics of a macro-based document attack, without the macro-based document. In order to successfully launch their attack, an attacker simply needs to convince a user to click through a few dialogs and suddenly they evade all of these recent macro-based document mitigations. Despite this, Microsoft has said they will not address this issue in current releases since it is a feature, and not a bug.

Macro-based exploits continue to persist due to user functionality and the argument that “it’s a feature, not a bug”. However, we wanted to take a deeper look at this new DDE-based attack vector to determine if this quality also held true for DDE-based attacks. We found that although DDE has valid usage as a feature, the aspects that make it a security problem stray a bit from how the documentation describes its intended design. Furthermore, unlike Office macros, this issue could be addressed to resolve the security aspects without impacting the usability of the feature for the end user. We’ll offer our recommendations for both a fix and what can be done to help protect against this issue.

Background

The excellent post by SensePost first brought the DDEAUTO bug to our attention. To understand the bug, it is important to first briefly discuss dynamic data exchange (DDE). DDE is a legacy Inter-Process Communication (IPC) mechanism dating back to 1987. Like all IPC communication, it consists of a protocol designed to pass messages between two applications. In the case of DDE, it is further enhanced by allowing access to shared-memory, either as one-time or continuous data transfers, and event driven callbacks when new data arrives at each end. The MSDN pages contain additional information about the DDE architecture.

The SensePost blog explains how to reproduce the DDEAUTO bug – demonstrating that command execution could be achieved via DDE in a Word document. Microsoft Office provides an extension to leverage DDE inside of documents to communicate with external processes. DDEAUTO is the specific Word field abused by SensePost. This is one of many Word field types defined in MS-DOC, Section 2.9.90 (flt). Moreover, DDEAUTO is a WordprocessingML keyword defined in ECMA-376, Section 3.1.3.2.2 (DDEAUTO).

From Microsoft:

"For information copied from another application, this field (DDEAUTO) links that information to its original source file using DDE and is updated automatically. The application name shall be specified in field-argument-1; this application shall be running".

Let’s break down a canonical example of the DDEAUTO field in a document:

{ DDEAUTO excel "C:\\My Documents\\Profits.xls" "Sheet1!R1C1:R4C4" \p }

This WordprocessingML statement will import part of Sheet1 of the Profits.xls spreadsheet into the current Word document via DDE communication to the process EXCEL.EXE, which is presumed to be open.

SensePost discovered that instead of specifying an application like Excel, they could specify an absolute path to another application as the first parameter to DDEAUTO and quoted arguments as the second parameter.

{DDEAUTO c:\\windows\\system32\\cmd.exe "/k calc.exe"}

An attacker is free to specify arbitrary parameters, with some restrictions we will identify later. One caveat to be mindful of is that when the DDEAUTO statement attempts to load, the user is prompted by multiple (non-security related) popups. At this time, we are unaware of a bypass for this prompt, but have not ruled out the possibility due to the complex nature of document structures. Furthermore, it also is important to note that Microsoft has encouraged developers to abandon DDE for more modern IPC mechanisms due to the fact that DDE is considered a legacy feature and is no longer widely used. In the SensePost blog, it is stated that Microsoft declined to fix the bug for current releases, deeming it a feature, likely due to what they see as excessive user interaction. Given the already growing use in APT campaigns in-the-wild, we believe Microsoft is taking a very conservative approach to this issue, and encourage an additional review of the implications if it is left unaddressed.

Despite this excellent analysis, several questions still remain: what is the cause, how can it be prevented, and is this a feature with nothing really to fix?

Bug or Feature?

Most write-ups of this issue have focused on the way it is triggered maliciously, and the wide variety of commands that can be executed through a malicious document. We wanted to first dive into the underlying code and better understand what the application was capable of with DDE, and what limitations an attacker may encounter when attempting to leverage this issue. Please note, all symbol names have been inferred from the publicly available Word 1.1a source code.

The actual DDE implementation in Word at wwlib!FGetAppTopicItemPffb will begin by setting up the first, second, and third DDEAUTO field arguments into the Global Atom Table (GAT) via wwlib!AtomAddSt, a wrapper for kernel32!GlobalAddAtomW. This means that each DDEAUTO argument is restricted in size to the size of an ATOM, which cannot exceed 255 bytes.

Next, wwlib!DclInitiate is called. This function first grabs a global struct reference for tracking the current state of the DDE negotiation. This struct has been initialized such that the first member at offset 0 is a HWND of a window owned by winword.exe. The second member at offset 4 is initialized to NULL, and should represent the HWND of a window we are negotiating DDE with. This struct reference is acquired by calling wwlib!PdcldDcl.

Next, inside wwlib!DclInitiate, USER32!SendMessageTimeoutW is called to broadcast the WM_DDE_INITIATE message to all top-level windows of programs running on the machine. Each window will be given uTimeout amount of time to respond to this DDE initiate message (0x3e8 or 1000 milliseconds).

If a running application decides to respond to this DDE initiate request, then it sends back a WM_DDE_ACK message. Word listens for this WM_DDE_ACK message while SendMessageTimeoutW is still blocking execution and waiting for the timeout. Upon receiving this WM_DDE_ACK message, the global DDE communications struct is updated, and the second member of the struct is set to the HWND of the responder. Once the second member of the struct has been set by the first WM_DDE_ACK message, subsequent WM_DDE_ACK responses receive a WM_DDE_TERMINATE message.

Once SendMessageTimeoutW returns, it grabs another reference to the DDE communications struct and checks the second member of the struct to see if another process responded to the broadcast. If this value is set, then it is used as the HWND of the other application that DDE will perform the IPC communication with. At this point, execution continues as normal and content is exchanged between the applications over this DDE channel. If no process responded to the WM_DDE_INITIATE message, and the second member of the struct is still not set, then Word determines that the target process for the DDE request is not currently running and is unavailable for the IPC communication.

The documentation on MSDN for DDE states that the target application for a DDE request should already be running when the request is made. At this point in the code, if no running process has responded to the WM_DDE_INITIATE message, then the entire DDE process should stop. To make this more user friendly, Word detects that the user has not started the target process, and attempts to start the process for them. This is where things go awry and deviate from the MSDN documentation.

Upon failing to receive a response from another process, execution falls down to a call to the Wwlib!FLaunchAppTopic function. This function is called with the corresponding Atom ID’s from the broadcasted message. The wrapper wwlib!StFromAtom is called to extract the strings from the specified atoms via the kernel32!GlobalGetAtomNameW API. These atom strings are then combined in the WCHAR buffer var_414_wstr_cmd shown in the code samples.

A space is appended to this WSTR variable before the second field argument is pulled from the atom table and appended to the string.

The second atom is then read out via wwlib!StFromAtom. Note - the third atom we saw registered in wwlib!FGetAppTopicItemPffb is discarded.

After the atoms are both used to construct the var_414_wstr_cmd string, wwlib!FOurRunApp is called with the constructed string passed in register ECX.

FOurRunApp assigns the pointer value to a local variable lpCommandLine, which is automatically named by IDA through type propagation.

Finally, CreateProcessW is called with lpCommanLine, resulting in command execution.

The MSDN DDE examples mention that the target of a DDE request should already be running, which makes sense because this is an IPC mechanism. Deviation from this for usability improvements is what allows this feature to be abused by attackers.

This is a bug specific to the WWLIB implementation of DDE and could be fixed by following the DDE guidelines on MSDN, asking users to start the target process themselves rather than automatically doing it for them. Additionally, the prompts that Word provides could be replaced with more security-oriented wording, such as the dialog that is used by Excel before it starts an application targeted through DDE.

The downside to this is that the key element in macro-based attacks tells us that some users will always click through any dialog, regardless of the wording. The proper fix here would be to ask the users to start the application themselves before clicking through the dialog, and then letting Word try the request again.

Detection

Throughout 2017, we have seen a very common pattern among attacks exploiting Office vulnerabilities and undocumented capabilities. In most of these attacks, the initial attack is used as a stager, with the next step executing powershell.exe or mshta.exe with an encoded download/execute payload on the command line. Based on our previous research and reverse engineering this bug, we can take two different approaches to detection and prevention – a whack-a-mole approach aimed at this specific bug, or a more holistic prevention approach targeting this class of attacks.

The first approach is to instrument Office applications with code to detect specific violations of expected behavior. Since DDE is meant to be used generically for any application to communicate with another, this issue cannot be detected by simply whitelisting what processes Word can talk to. In this case, we could take the easier path of simply adding a protection in the Endgame exploit prevention feature that detects instances where a newly created process name and arguments both come from the DDE window messages above.

That approach is effective for this bug, but is narrowly focused. Instead, the next version of the Endgame platform delivers a more general detection capability that uses behavioral analytics to detect Office products executing suspicious child processes such as mshta.exe, or powershell.exe. We can also determine if those child process make external network connections. We combine these two approaches, offering a robust way to detect different vulnerabilities and attacks across multiple Office products.

CONCLUSION

The rising use of macro-less document attacks is certainly something we’ll be watching for the foreseeable future. It is unfortunate that Microsoft will not work further to resolve this problem, given its increasing implementation among APTs. As we have demonstrated, the DDEAUTO is a specific kind of vulnerability within this broader class of macro-less document attacks. Instead of creating a one-off solution, our research has identified a means to protect not only against the DDEAUTO bug, but against this entire class of attacks.