18.8.10

Using Scapy to Select a Range of pcap Records

I was reading about Wireshark vulnerabilities that could cause a DoS or buffer overflow. I downloaded the pcap associated with one of the vulnerabilities and read it using a vulnerable Wireshark version 1.2.6. Sure enough, it caused Wireshark to display a gray screen and upon trying to close it, it displayed the following:



I was interested in finding the record(s) that caused the crash by methodically selecting given ranges of the records and narrowing down the problem records(s). At first I thought of using tcpdump to select some records and write only those records to a new output file and reading the new output file into Wireshark. Doing this using tcpdump is not possible because it has a command line switch (-c) that allows you to specify the number of records you want to read, but always begins reading at the first record. So, I wrote a small Python/Scapy program that would allow me to select the starting and ending records from the pcap, write the selected records to a Python list, feed the records into Wireshark and observe whether or not it crashed. It didn’t take me long to find the problem record.

Here is the program I used:

from scapy.all import *
import sys

recs=rdpcap("/tmp/fuzz-2010-06-02-11020.pcap")
lrecs=len(recs)
outrecs=[ ]
start=int(sys.argv[1])-1
end=int(sys.argv[2])-1

if (end >=lrecs):
....print "End value greater than number of pcap records"
....sys.exit()


x=start
while (x <= end):
....outrecs.append(recs[x])
....x=x+1

wireshark(outrecs)


There really isn’t much to it and there is only one statement that uses Scapy. The program starts by importing the Scapy and Python modules required for the program. I stored the downloaded pcap from http://www.wireshark.org/download/automated/captures/fuzz-2010-06-02-11020.pcap in file /tmp/fuzz-06-02-11020.pcap and used the Scapy “rdpcap” to read the pcap into a Python list named “recs”. Next, I created an empty Python list called “outrecs” to stored the user selected records.

The next block of code simply takes two command line arguments and stores the first in an integer named “start” and the second into an integer named “end”. This is the range of records to select and feed into Wireshark. Then there is a check to make sure that the value of the last record selected is less than or equal to the number of records in the pcap. Note, I've used periods to represent spaces since Python requires indentation after the "if" statement and the "while" statement. I did this because the spaces I tried to use were removed by the HTML formatting of the blog software - sorry.

The next block of code creates a loop to read through the list of pcap records and select and append the ones that fall in the user-supplied range into the output list “outrecs”. Scapy has a built-in functionality to invoke Wireshark. The selected records are fed into Wireshark. With a little trial and error, you can discover which record causes the DoS.

My challenge to you is to tell me the record number that causes Wireshark to crash. Make sure you have Scapy installed . Also, make sure that you have a vulnerable version of Wireshark (versions 0.8.20 through 1.2.8) and make sure to supply to “rdpcap” the file location where you stored the downloaded pcap http://www.wireshark.org/download/automated/captures/fuzz-2010-06-02-11020.pcap. There are 704 records in the pcap so that's the upper bounds for the input range.

I stored the Python program in a file named “find-badrec.py”. Now, suppose I want to test to see if the bad record falls in the range of records from 1-10, I’d run it from the command line using:

python find-badrec.py 1 10

The program displays the following Wireshark output and does not crash. I would close Wireshark and press CTRL-C to stop the Scapy program to try again with a different range of values.



Time is running out to sign up for my Scapy course at SANS Network Security in Las Vegas. I cover reading, writing, and altering pcaps using Scapy along with a bunch of other very useful features of Scapy. If you’re already taking a multi-day track at the conference, I’m teaching the one-day course after these end.

This course is a nice complement to SEC503 “Intrusion Detection In-Depth”, SEC502 “Perimeter Detection In-Depth”, SEC504 “Hacker Techniques, Exploits & Incident Handling”, and SEC560 “Network Penetration Testing and Ethical Hacking”. These are just some tracks where you can use Scapy as an additional tool to implement what you’ve learned. I hope to see you there!

16.8.10

Windows LAN Addressing Validation (and a Scapy lesson)

Following the AirTight “hole196” announcement affecting WPA/WPA2-Enterprise networks (see my WillHackForSushi article), I was speaking with a friend who told me how attractive yet naive I am regarding how clients respond to LAN traffic sent to the broadcast address with unicast payloads.  Essentially, I was unaware as to how a client would handle a unicast upper-layer payload (such as a TCP SYN request) when delivered to a broadcast MAC address.

photo

So, mull it over I did, with the help of Scapy.  I wanted to experience first-hand how different clients behaved when they receive unicast IP payloads, sent to a layer 2 broadcast address.  This is a very straightforward test with Scapy, sent in these examples over a wired LAN card.

First, I start Scapy and create an Ethernet header in the variable “p”, using the broadcast destination address, and the source address of my Linux box running Scapy.  The type code 0x0800 indicates that the next payload is an IP packet:

$ sudo scapy
>>> p = Ether(dst="ff:ff:ff:ff:ff:ff", src="00:18:8b:ad:2a:c7", type=0x0800)


Next, I add an IP header, specifying the source of my Linux box and the destination of my Windows 7 box.  I add an ICMP payload as well (using the default type/code of ICMP Echo Request) and display the packet contents:

>>> p /= IP(src="172.16.0.109", dst="172.16.0.113")
>>> p /= ICMP()
>>> p
<Ether dst=ff:ff:ff:ff:ff:ff src=00:18:8b:ad:2a:c7 type=0x800 |<IP
frag=0 proto=icmp src=172.16.0.109 dst=172.16.0.113 |<ICMP |>>>

With my finished packet, I call the srp1() Scapy function to send and receive the packet (using srp1() as opposed to sr1() since I am sending a layer 2 packet) and display the associated packet response:

>>> srp1(p)
Begin emission:
Finished to send 1 packets.
.*
Received 2 packets, got 1 answers, remaining 0 packets
<Ether dst=00:18:8b:ad:2a:c7 src=00:21:5c:7e:70:c3 type=0x800 |<IP
version=4L ihl=5L tos=0x0 len=28 id=31066 flags= frag=0L ttl=128
proto=icmp chksum=0x6888 src=172.16.0.113 dst=172.16.0.109 options=[]
|<ICMP type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |<Padding
load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|>>>>


Much to my surprise, the Windows 7 host responds to my unicast Echo Request sent to the broadcast LAN address with an ICMP Echo Reply.  Next I wondered if the Windows 7 target validated the Ethernet source address.  I created a second packet, similar to the first but this time using the broadcast LAN address as the source:

>>> p = Ether(dst="ff:ff:ff:ff:ff:ff", src="ff:ff:ff:ff:ff:ff", type=0x0800)
>>> p /= IP(src="172.16.0.109", dst="172.16.0.113")
>>> p /= ICMP()
>>> srp1(p)
Begin emission:
Finished to send 1 packets.
...*
Received 4 packets, got 1 answers, remaining 0 packets
<Ether dst=00:18:8b:ad:2a:c7 src=00:21:5c:7e:70:c3 type=0x800 |<IP
version=4L ihl=5L tos=0x0 len=28 id=31603 flags= frag=0L ttl=128
proto=icmp chksum=0x666f src=172.16.0.113 dst=172.16.0.109 options=[]
|<ICMP type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |<Padding
load='\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|>>>>


Again the target responds to the Echo Request.  Note that the response goes to the correct unicast MAC address, as opposed to the source included in the Echo Request packet, indicating that Windows uses the ARP cache to populate the Ethernet header as opposed to just swapping the address fields in the originating packet.


I was surprised to learn this LAN addressing technique also works with TCP:

>>> p = Ether(dst="ff:ff:ff:ff:ff:ff", src="00:18:8b:ad:2a:c7", type=0x0800)
>>> p /= IP(src="172.16.0.109", dst="172.16.0.113")
>>> p /= TCP(sport=65000, dport=135)
>>> srp1(p)
Begin emission:
Finished to send 1 packets.
...........*
Received 12 packets, got 1 answers, remaining 0 packets

<Ether dst=00:18:8b:ad:2a:c7 src=00:21:5c:7e:70:c3 type=0x800 |<IP
version=4L ihl=5L tos=0x0 len=44 id=545 flags=DF frag=0L ttl=128
proto=tcp chksum=0x9fac src=172.16.0.113 dst=172.16.0.109 options=[]
|<TCP sport=loc_srv dport=65000 seq=4159458989L ack=1 dataofs=6L
reserved=0L flags=SA window=8192 chksum=0xda0c urgptr=0 options=[('MSS',
1460)] |<Padding load='\x00\x00' |>>>>


I continued this testing against other clients as well.  Windows Vista systems behave the same as my Windows 7 target; I did not have a Windows XP box handy for testing.  Interestingly, Linux 2.4, Linux 2.6 and OS X 10.6 systems do NOT behave the same as Windows.  These systems reject LAN broadcast traffic sent to unicast IP addresses.


What does this mean?  First, Scapy rocks, and you should check our Judy’s Scapy class with SANS.  Second, leveraging the “hole196” vulnerability, a malicious insider could send traffic to other WLAN clients without going through the LAN, evading wired IDS systems (but otherwise not creating an additional network exposure).  I have a nagging feeling this behavior is not good (tm) otherwise, but I haven’t figured out why yet.  If you have an idea, please share it in the comments below, or drop me a note.  Thanks!


-Josh