31.3.11

NBNS Spoofing on your way to World Domination

I few weeks ago I helped on the Red Team at the Midwest CCDC competition in Minneapolis (actually St. Paul, but close enough). There were some talented guys there and it was a lot of fun beating on those poor kids. I highly recommend you volunteer at events like this. You haven't lived until you've slowly ripped the legs off a live domain controller while the owner sits and watches, with no ability to do anything about it (vnc + disabled mouse and keyboard).

Whenever I get together with a group of people like this, I like to exchange techniques and discuss trends. We discussed our paths of least resistance for internal tests, and I mentioned that my favorite are the attacks based on spoofing NetBIOS Name Service (NBNS) Responses. It is the very first tool I fire up on internal penetration tests, and it is very valuable on wireless penetration tests where client laptops are in scope. Before we get into the attack method I'll go over a bit of the background on what NBNS is.

When your Windows box needs to determine the IP address for a given name, it tries, in order, the following lookup methods:

local hosts file - C:\Windows\System32\drivers\etc\hosts

DNS

NBNS

You may wonder, how many queries make it past DNS? The answer is, a lot! Any fat fingered internal server name, a laptop looking for its domain network resources while on another network, or would be web searches will generate this traffic. You may ask, web searches, huh? We open our browser and type in "packetstan" in the address bar, what does the browser do?

Modern browsers allow you to search via the address bar (Chrome doesn't even have a dedicated search box), and the browser needs to decide if the text is a host or a query. If the string has spaces, it is obviously a search request. If there are no spaces the browser doesn't know if we want a server named "packetstan" or we want to search google/yahoo/bing for "packetstan", but it first tries to access a host named "packetstan" and therefor needs to attempt to resolve the IP for the host. i.e. I do a search for "puppies" by browser will attempt to get an IP address for "puppies" (how cute).

The hosts file isn't going to have an entry for "packetstan", and DNS is going to fail to lookup "packetstan" (no .com). So we land on NBNS. Here is a screen shot of the transaction:

Since the look up is just a hostname, windows adds the local DNS suffix to the query and asks its DNS server(s). The suffix picked up my the Windows box usually comes from the DHCP server. As you can see, the DNS server replied that it had no idea on how to lookup that name. Next, you'll see the NBNS Request. The beautiful thing is, the NBNS Request is a broadcast, so anyone can reply easily and redirect traffic.

If we wanted to poison a DNS lookup we would have to somehow get in the middle of the traffic, and that takes some additional effort. NBNS is so easy I can even do it before I have my first cup of coffee, as I usually save MITM attacks until after a few cups of joe.

Wesley McGrew wrote a tool to forge NBNS replies. He also has a great threepartseries on the protocol and the tool writing process. Fans of this blog should really check out those posts. Now that we have better understanding of NBNS, how do we spoof a reply?

To spoof an NBNS reply, we need to craft a reply packet that has the same Transaction ID as the request. The Transaction ID is similar to the one used by DNS (BTW, the numbers sequential and are reset to \x80\x00 after each reboot). We also need to specify the IP address to send in the reply. For all intents and purposes, the rest of each response packet can be static.

Mr. McGrew's tool does what we described above, but I wanted it in Metasploit, which means rewriting it in ruby. Unfortunately, Racket, the packet crafting tool for Ruby, doesn't have a module for NetBIOS, so we'll manually dissassemble the packet and create a raw reply packet.

Using WireShark as a guide, we can pull apart the packet, piece by piece.

As previously mentioned, all we really need is the Transaction ID and the Name. The Transaction ID is really simple as it is just two bytes, but what if we want to read the name? The raw name looks like this:

\x20FAEBEDELEFFEFDFEEBEOCACACACACAAA\00

The leading space and trailing null byte are apparent, but what kind of encoding is used here? Master McGrew describes it in the second portion of his blog post. Each character in our query ("PACKETSTAN") is split into nibbles (half bytes) and each nibble is added to \x41 (A), like this:

P = \x50

1st nibble = 5, second nibble is 0

41 + 5 = 46 and \x46 = F

41 + 0 = 41 and \x41 = A

P is then encoded as FA

We can decode and encode our name with this bit of ruby (remember hex 41 equals decimal 65):

We skip the first (space) and last (null) bytes and subtract 65 from each so we end up with "5041434B45545354414E20202020". We convert that into ASCII characters and strip the trailing spaces to get "PACKETSTAN".

Fortunately, we don't actually need to re-encode the string since we can just use the original in our reply. However, I use this string to match a regular expression so we only reply to the in-scope clients.

if (nbnsq_decodedname =~ /#{datastore['REGEX']}/i) ...

The raw response packet is then created. You'll notice that most of our packet is static or taken directly from the Request packet.

The default regular expression will match any query, and when one is found you will see a message like this:

[*] Regex matched PACKETSTAN from 192.168.1.51. Sending reply...

Ok, great, so we sent a reply and have poisoned a name lookup. So what?

We can use Metasploit to setup a few fake services. That way, if a host lookup was for a web server or a file server (smb) the user will be directed to our Metasploit instance. The Metasploit modules in question will request authentication, and the client will happily send it credentials (sort of).

Name Current Setting Required Description ---- --------------- -------- ----------- CAINPWFILE no The local filename to store the hashes in Cain&Abel format CHALLENGE 1122334455667788 yes The 8 byte challenge JOHNPWFILE /home/tm/johnsmb no The prefix to the local filename to store the hashes in JOHN format LOGFILE no The local filename to store the captured hashes SRVHOST 0.0.0.0 yes The local host to listen on. This must be an address on the local machine or 0.0.0.0 SRVPORT 445 yes The local port to listen on. SSL false no Negotiate SSL for incoming connections SSLVersion SSL3 no Specify the version of SSL that should be used (accepted: SSL2, SSL3, TLS1)

Name Current Setting Required Description ---- --------------- -------- ----------- LOGFILE /home/tm/httplog no The local filename to store the captured hashes PWFILE no The local filename to store the hashes in Cain&Abel format SRVHOST 0.0.0.0 yes The local host to listen on. This must be an address on the local machine or 0.0.0.0 SRVPORT 80 yes The local port to listen on. SSL false no Negotiate SSL for incoming connections SSLVersion SSL3 no Specify the version of SSL that should be used (accepted: SSL2, SSL3, TLS1) URIPATH / no The URI to use for this exploit (default is random)

Any Windows box that access our SMB share will automatically try to authenticate. The web browser is a different story, and depends on the browser and if the client machine is joined to a domain. It will either send credentials automatically or prompt the user for credentials. The table below describes when credentials are automatically sent, and when they aren't.

Browser

Domain Joined

Standalone (Non-Domain)

Chrome

Automatic

Performs Google Search

IE

Automatic

User Prompted

FireFox

User Prompted

User Prompted

Most of the machines in scope for my test are joined to the corporate domain, so this attack works great! A Windows box that is part of a domain treats hosts, not FQDN hostnames, as part of its "Local Intranet Zone". This zone has all sorts of settings to make single sign-on easier. IE and Chrome both use this "Local Intranet Zone", while Firefox does not.

The SMB capture module will output a John the Ripper compatible file. However, the http_ntlm module doesn't give us a the file format we want (I'm working on a fix), so we'll have to change the file to look like this:<user>:::<LMHASH>:<NTLMHASH>:<CHALLENGE> (wrapped for space, the actually file won't be wrapped):

Note: Windows 7 and Windows 2008 will not provide a LM Hash and any further steps will not work as these steps rely on the weak LM Hash. You can still brute force the NetNTLMv2 hash, but there are no shortcuts.

The hashes sent via SMB or HTTP aren't your straight LH and NTLM hashes. While the full details requires a separate blog post, the short version is they are salted with a challenge to "ensure" they can't be reused. However, Metasploit uses a static challenge (1122334455667788) so we can use rainbow tables to crack the password. The tables can be used with rcracki and the halflmchall_alpha-numeric (smaller) or halflmchall_all-space (larger) tables are available from freerainbowtables.com. You can download both of them (28GB) via wget like this:

In 3.4 short seconds we have the first seven characters of the password. The latest version (at the time of writing) of John the Ripper (1.7.6-jumbo12) has a perl file named netntlm.pl that will bruteforce the second portion of the password. We just need to give it the first portion of the password and a john compatible file (output trimmed).

$ perl netntlm.pl --seed MYPASSW --file ~/john-http

###########################################################################################The following LM responses have been previously cracked:

The following NTLM responses have been previously cracked:

###########################################################################################Isolating accounts which have only had their LM response cracked.Account MYDOM\\tim LM response added to cracking list.

We can see that my password is MYPASSWORD1. However, LM passwords are case insensitive and are always presented upper case. To get the case sensitive password we run the *same* command again. The second pass will use "MYPASSWORD1" against the case sensitive NTLM hash.

$ perl netntlm.pl --seed MYPASSW --file ~/john-http

###########################################################################################The following LM responses have been previously cracked: MYDOM\tim:MYPASSWORD1::4f717259791a8a3d6a11ae4050ed5c72ee125c7119e2a20f:44(trimmed)

This method will usually yield credentials pretty quickly, but to get some higher privileged accounts just wait a while.

Once you have a cracked password you can (usually) connect to boxes all over the place by using Metasploit's exploit/windows/smb/psexec module.

The prevent this attack from being as effective, you need to modify the LAN Manager authentication level to refuse LM via GPO. It isn't enough to use "Send NTLM response only" or "Send NTLMv2 response only".