The ongoing breach stories of targeted email campaigns harbouring malicious attachments made me think of writing up a summary of a presentation I gave at the amazing NCC Con held in Dublin in January this year. The talk was based on a pentesting war story that started off exploiting the old (but I believe often overlooked) Dynamic Data Exchange (DDE) trick to go from an Excel export function in a web app to OS code running on users’ workstations. From there, the story takes us on to domain creds via some NTLMv2 theory (without a pentest laptop), a bug in hashcat and a new script “catflap”.
From CSV to CMD
DDE is an old Microsoft technology used to facilitate data transfer between applications, a form of Inter-Process Communication (IPC). The security risks in the context of web applications were, to my knowledge, first published here by James Kettle (I would have sworn this technique was around before 2014 – how time flies).
A brief reminder then: imagine an export function in a web app, where some of the data in the cells comes from user input (so like persistent XSS it is stored and later reflected) e.g. consider an application that stores the following parameter value:
=cmd|'/k ipconfig'!A0
On requesting an export, a CSV file is returned that includes this value in a field. When this file is opened, Excel knows that this DDE reference could be dangerous and issues a couple of warnings:
In this case, an eagle-eyed user might raise an eyebrow at CMD.EXE but, as the original article notes, if the user requested the Excel file and it came from a source they trust, why wouldn’t it be secure? And we know that users click through warnings anyway.
When cmd /k ipconfig runs, the /k persists the command window for a screenshot to use in the report:
In fact, without that persistence, the command seems to run in a minimised window and then of course exits, which is rather nice for our attacker. I should say that export to CSV seems to be cleaner from the attacker’s perspective than export to XLS or XLSX, which often requires extra action by the user to activate the payload such as clicking into the malicious cell, which isn’t desirable. All of this behaviour is subject to the version of Excel running and its configuration.
From CMD to qwerty
The war story concerned an internal application that was vulnerable to the DDE trick, and so I started playing around with payloads that would be more exciting than ipconfig or calc. Here’s a simple one:
cmd /c net use c$
In this case the “attacker_IP” belonged to a locked-down workstation (no pentest laptops allowed) – but it did, curiously enough, have Wireshark installed. So here was the “net use” connection from the “victim” (which was in fact a machine I was using and thus I knew the username and password):
Bear in mind this was an internal test: if the victim was using a vulnerable internet-facing website, their ISP would likely [cough] block the outgoing SMB connection.
I was dealing with NTLMv2 here: a quick reminder showed that I needed to pull out the following for John:
username:$NETNTLMv2$domain$challenge$HMAC-MD5$blob
What’s in green is known at this point (“NETNTLMv2″ is a constant). I could immediately fill in the domain and username fields as they’re given away in cleartext by the NTLMv2 exchange, helpfully pulled out in the (obfuscated) Packet List above (and anonymised below):
smithjer:$NETNTLMv2$domain$challenge$HMAC-MD5$blob
So what about the other bits? They all relate to the NTLM authentication, which is a challenge-handshake protocol:
1. Client > Server (Type 1, negotiation)
Features supported by the client and requested of the server
2. Server > Client (Type 2, challenge)
Features supported and agreed upon by the server + a challenge
3. Client > Server (Type 3, authentication)
More information about the client + response(s)
The essential difference between NTLM and NTLMv2 is how the response is calculated. NTLM uses MD4 and DES in a weak way which is well known (5 NULL bytes yada yada yada); NTLMv2 uses HMAC-MD5 based on more than just the password and challenge, which is where the “blob” comes in. So that’s covered off the “challenge”, “HMAC-MD5″ and “blob” that’s missing from the John hash I’m having to build up from scratch. (Remember, I had no tools, otherwise Responder would have made this easy.) Here’s the challenge:
And here’s the HMAC-MD5 and blob (which is everything after the HMAC in the “NTLM Response tree”):
To get the blob, you can copy the NTLM Response bytes from within Wireshark and remove the first 16 bytes (which is the HMAC). The blob is likely to start 0×01010000…So we now have:
smithjer:$NETNTLMv2$domain$36edff8376e59e18$4f68b56e9ce788d010f58b4f049b5c7f$0101000000000000295779de01bbd001b6f955bf062e...
That’s the hard part done, right? Wrong.
Let’s get cracking
Here was John’s response (bear in mind I knew the password), and, after duly modifying the hash format, hashcat wasn’t happy either:
Note the “skipping line” error. Turns out hashcat has a bug, which (I later found out) oclhashcat doesn’t suffer from. I experimented with it and reported it – hashcat has problems when the blob length is over 224 bytes. For some reason, it’s not been assigned a “bug” label but a “new feature” label. Anyway, back to John: remember the format on the cheatsheet was:
username:$NETNTLMv2$domain$challenge$HMAC-MD5$blob
Turns out the hashcat format worked a treat, i.e.
username::domain:challenge:HMAC-MD5:blob
To be fair to pentestmonkey, “this sheet was originally based on john-1.7.8-jumbo-5. Changes in supported hashes or hash formats since then may not be reflected on this page”, and I was using a later version. Anyway, all’s well that ends well:
catflap
In order to look into the bug I found, I wrote a script (which I called catflap) that serves two purposes:
- It extracts what hashcat needs from the NTLMv2 exchange in a Wireshark capture file.
- It allows you to produce a more realistic test case to check your NTLMv2 cracker is working properly. For this catflap will accept a normal hash format from a file as well as a capture file. catflap will recalculate the NTLMv2 response based on a password you supply. This means all the other variables (username, domain, challenge and, crucially, the blob) in the resulting test hash are exactly the same as the ones in your captured exchange, but in this test case you know the password. In this way you can ensure your cracking tool is working correctly, and acts as a better test than the standard hash examples out there, useful though they are. Of course, you could also play about with other inputs, which is what I did with the blob find the bug.
Usage is simply:
catflap [password]
The following shows catflap changing the HMAC response to set the password to “password”:
You can then run that edited hash through your cracker of choice to ensure it spits out “password”. If not, something’s gone wrong. You can find catflap on github.
Input validation and sanitisation
Whenever I find an Excel export function in a web app, most of the time the DDE trick works because the characters used to launch the payload aren’t the usual suspects. It’s not a well-known attack vector, even among pentesters (or at least it’s been forgotten) so it’s worth remembering to check for this.
What about remediation? The original article on this stated that “the best defence strategy we are aware of is prefixing cells that start with ‘=’ with an apostrophe”. Last month this topic came up in a company-wide discussion at work, and my colleague Cara Marie noted that ‘+’ and ‘-’ could be used to launch a command too. Turns out (thanks to Michael Roberts, another NCC bod) that this had been lurking around for while, for example here). Michael added that you can also use ‘@’ in the format:
@SUM(cmd|'/c calc'!A0)
as well as surrounding the payload with quotes such as:
"=cmd|'/c calc'!A0".
Over this last month the original article has been updated twice to reflect the same findings (except for the use of quotes). So, as far as I know, two independent groups came up with new bypasses for the original recommendation. The point here, then, is that the blacklist approach by itself often fails (which shouldn’t be news to anyone who’s done a re-test). We need to perform validation based on a whitelist of characters and syntax wherever possible. For example, if a field is a phone number, there should be no need for characters such as ‘=’ and ‘@’. Starting off with ‘+’ is reasonable (prefixing an international dialling code) but after that we shouldn’t see any non-numeric characters. We can also restrict the length of that field too. With these restrictions in place, we don’t even need to implement (and maintain) a blacklist.
Red teaming
The story of this article was set around a web application that allowed export but the DDE trick is valuable to red teams trying to get malicious attachments through email. The reason for this is that a DDE payload isn’t a macro and files that use it are often able to fly through perimeter content checks that would stop files with macros.
Published date: 21 April 2016
Written by: Jerome Smith