Back in January, I put out two blog posts on using DCOM for lateral movement; one using MMC20.Application and the other outlining two other DCOM applications that expose “ShellExecute” methods. While most techniques have one execution method (WMI has the Create() method, psexec creates a service with a custom binpath, etc.), DCOM allows you to use different objects that expose various methods. This allows an operator to pick and choose what they look like when they land on the remote host from a parent-child process relationship perspective.
In this post, I’m going to walk through abusing the Excel.Application DCOM application to execute arbitrary code on a remote host. This same DCOM application was recently talked about for lateral movement by using the RegisterXLL method, which you can read about here. In this post, I’m going to focus on the “Run()” method. In short, this method allows you to execute a named macro in a specified Excel document. You can probably see where I’m going with this 🙂
As you all may know, VBA macros have long been a favorite technique for attackers. Normally, VBA abuse involves a phishing email with an Office document containing a macro, along with enticing text to trick the user into enabling that malicious macro. The difference here is that we are using macros for pivoting and not initial access. Due to this, Office Macro security settings are not something we need to worry about. Our malicious macro will execute regardless.
At this point, we know that Excel.Application is exposed via DCOM. By using OLEViewDotNet by James Forshaw (@tiraniddo), we can see that there are no explicit launch or access permissions set:
If a DCOM application has no explicit Launch or Access permissions, Windows allows users of the Local Administrator group to Launch and Access the application remotely. This is because DCOM applications have a “Default” set of Launch and Access permissions. If no explicit permissions are assigned, the Default set is used. This can be found in dcomcnfg.exe and will look like this:
Since Local Administrators are able to remotely interface with Excel.Application, we can then remotely instantiate it via PowerShell using [Activator]::CreateInstance():
As you can see, remote instantiation succeeded. We now have the ability to interact with Excel remotely. Next, we need to move our payload over to the remote host. This will be an Excel document that contains our malicious macro. Since VBA allows Win32 API access, the possibilities are endless for various shellcode runners. For this example, we will just use shellcode that starts calc.exe. If you are curious, you can find an example here.
Just create a new macro, name it whatever you want, add in your code and then save it. In this instance, my macro name is “MyMacro” and I am saving the file in the .xls format.
With the actual payload created, the next step is to copy that file over to the target host. Since we are using this technique for Lateral Movement, we need Local Admin rights on the target host. Since we have that, we can just copy the file over:
With the payload on target, we just need to execute it. This can be done using the Run() method of the Excel.Application DCOM application that was instantiated earlier. Before we can actually call that method, the application needs to know what Excel file the macro resides in. This can be accomplished using the “Workbooks.Open()” method. This method just takes the local path of the file. So, what happens if we invoke the method and pass the location of the file we just copied?
Well, isn’t that interesting. The file exists, but Excel.Application is stating that it doesn’t. Why might this be? When Excel.Application is instantiated via DCOM, it is actually instantiated via the Local System identity. The Local System user, by default, does not have a profile. Since Excel assumes that it is in an interactive user session, it fails in a less than graceful way. How can we fix this? There are better ways to do this, but a quick solution is to remotely create the Local System profile.
The path for this profile will be: C:\Windows\System32\config\systemprofile\Desktop and C:\Windows\SysWOW64\config\systemprofile\Desktop.
Now that the Local System profile is created, we need to re-instantiate the Excel.Application object and then call “Workbooks.Open()” again:
As you can see, we were now able to open the workbook containing our malicious macro. At this point, all we need to do is call the “Run()” method and pass it the name of our malicious macro. If you remember from above, I named mine “MyMacro”
Calling “Run(myMacro)” will cause the VBA in that macro to execute. To verify this, we can open Process Explorer on the remote host and verify. As you can see below, this particular host has the “Disable VBA for Office Applications” GPO set. Regardless of that security setting, the macro is permitted to execute:
For this example, I just used calc spawning shellcode, which resulted in a child process being spawned under Excel.exe. Keep in mind that since VBA offers a lot in terms of interaction with the OS, it is possible to not spawn a child process and just inject into another process instead. The final steps would be to remotely cleanup the Excel object and delete the payload off the target host.
I have automated this technique via PowerShell, which you can find here: https://gist.github.com/enigma0x3/8d0cabdb8d49084cdcf03ad89454798b
To assist in mitigating this vector, you could manually apply remote Launch and Access permissions to the Excel.Application object…but don’t forget to look at all the other Office applications. Another option would be to change the default remote Launch/Access DACLs via dcomcnfg.exe. Keep in mind that any DACL changes should be tested as such modifications could potentially impact legitimate usage. In addition to that, enabling the Windows Firewall and reducing the number of Local Administrators on a host are also valid mitigation steps.
What stands out the most with this technique is that Excel and the child process will spawn as the invoking user. This will often be process creations from user accounts that different than the user that is currently logged on. If those are the only two processes and the user account being used doesn’t normally logon to that host, that might be a red flag.