catching pink dolphins using libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · then, i...

15
Catching Pink Dolphins using Libpcap via 802.11 Douglas Berdeaux [email protected] 2013 © GNU – www.WeakNetLabs.com

Upload: others

Post on 21-Jul-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

Catching Pink Dolphins using Libpcap via 802.11

Douglas Berdeaux

[email protected]

2013 © GNU – www.WeakNetLabs.com

Page 2: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

Abstract

During my recent development of WARCARRIER, I thought that I had to write my own 802.11

protocol analyzer. So, I tried what came naturally to me – Perl – using the Net::PCAP libraries. This

proved to be fruitless since I desired more from the actual packet processing functions. I moved on to

SCAPY and LORCON with Python, but I realized the dependencies where starting to pile up and I wanted

to make my code as small as possible. Then, I decided to dive right into C programming and using the

Libpcap libraries for packet capture. This was incredibly easy with basic Ethernet packets on a switched

network, but for 802.11 (WiFi) packets, I was coming upon dead ends in documentation, I couldn’t

understand the syntax used in code snippets, and I was just plain lost. When I finally finished the

protocol analyzer code application, I just decided that Airodump-NG wasn’t too big of a dependency and

left it in the production code.

C is an amazing language, but its syntax is far different from what I was used to with Perl. Now, I

can capture the packets that I want and pull any data that I want from their headers in only a few easy

steps! This paper explores libpcap with the DLT_IEEE802_11_RADIO link layer type of packet. The

examples you read have been tested with the following hardware: ALFA AWUS036H rt8187 with the

latest driver from Compat-Drivers and the GNU Linux kernel. I realize that the code has a lot of room for

optimization, and I left out all of the functions that report errors for brevity. This is a very basic

introduction to the libpcap library for the purpose of sniffing beacon frames only – which should be

enough to get someone (that’s stuck in the same position I was in) onto the ground and running.

Page 3: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

Frame Dissection

Our examples will deal solely with the beacon frames. In 802.11 wireless networking, An AP, or

Access point, can broadcast a type of packet called a management frame with a subtype of beacon. The

management frame is pretty much just a large header, there’s no data portion sent with it. All of its data

is in the header itself. The beacon is used by an AP for station synchronization and to express to stations

and potential stations what its attributes and technical capabilities are. By default, many AP

manufacturers set the beacon rate to about 100ms – which is ten times per second. This is what makes

wardriving possible. When you are traveling with your station computer with an 802.11 adapter in

promiscuous mode, it can easily sense these beacons and the software used for wardriving pulls the

relevant information out of them. There are many levels the beacon passes as it makes its way from the

physical layer of the antenna aperture up to the application layer where our software handles the

beacon frame.

There are many subtypes of management frames, not just beacons. So to keep track of these

types, we use numbers. As each frame is sent through the air, the header information is encoded as bits

in 1’s OR 0’s in the radio signal. These bits are grouped into bytes (8 bits to a byte). These bytes are then

grouped again into section of the header frame. One of these sections is just to relay the information as

to which type the frame is that is being sent. As you may have guessed, there are also more types that

just management frames since we need some way to send actual application data to and from the

stations! There are a total of three different types of 802.11 frames: management, control, and data

which are represented in the bytes as 00, 01, and 02 respectively. Our frame is a management frame of

subtype beacon. Beacons are represented as 08. So, if we are to dissect our packet at the application

layer when it is received, we can step through it starting at the beginning of the frame using what is

called an offset and see the 00 08 value for management:beacon. Let’s go deep and take a look at a

beacon frame in Wireshark, the packet analyzer.

Page 4: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

Figure 0: the beacon frame dissected using Wireshark.

In figure 0, we can see the highlighted byte 80 that corresponds to “Subtype: 8” or simply

“beacon.” If you look even higher above, we see that the destination of the packet is to every address or

“Broadcast.” Beacons are multicast frames and are not sent to machines individually unless the 802.11

network is an iBSS or Ad-Hoc network. See the null byte 00 just before the 80? That’s how we know this

is a management packet. Everything in a frame header can be accessed by its hexadecimal form.

ESSID

See the ESSID “PinkDolphin” in the right side of the frame data output in the bottom portion of

the window in figure 0? That is represented in its ASCII form which we will need to convert from the

hexadecimal form. Let’s next highlight the ESSID in Wireshark and find its offset and see its hexadecimal

form.

Page 5: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

Figure 1: The ESSID or “name” of the service set (802.11 network) in a Wireshark packet dissection.

In figure 1 above, we see the hexadecimal form on the bottom left and the ASCII version on the

right. If we count the bytes until we get to the 50 in the ESSID (the first highlighted value in the field on

the left side), we get 4 rows of 16 or 64 bytes.

If we convert all hexadecimal values starting at the offset + 1 (65)1, into ASCII values we get:

0x50 = ‘P’ 0x69 = ‘i’ 0x6e = ‘n’ 0x6b = ‘k’ 0x44 = ‘D’ 0x6f = ‘o’ 0x6c = ‘l’ 0x70 = ‘p’ 0x68 = ‘h’ 0x69 = ‘i’ 0x6e = ‘n’

Listing 0: hexadecimal values represented as ASCII for the ESSID.

1 When assigning a character pointer to the offset, we assign it to the 64the byte in the frame header. It’s first element, say

essid[0] will be the 65th

byte in the frame header – the second (essid[1]) will be the 66th

byte and so on.

Page 6: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

Since the ESSID is of variable length, it is terminated by a byte value of 0x1 or simply 1 that we

can see just after the last character 6e (n) as 01. In C, we can make a pointer *essid start at the 0x50 value

or the ‘P’ and then use a simple while loop that increments n and continues until our essid[n] = 0x1. Then

we will have our ESSID from the 802.11 header! Well, it’s not really “ours.”2 This is actually a neighbor’s

protected AP. Why is this legal or okay to sniff the packets then? Well, we are just sniffing beacon

frames and not the user’s data. Remember the Google fiasco when they were wardriving for the

location of BSSIDs for global positioning purposes but they accidentally sniffed passwords and private

data too? Well, someone at Google decided it was unimportant enough to make their own protocol

sniffer for just the beacons alone – which is actually all they needed. And then they got in trouble. The

beacons again, are just public frames. That’s why they are not encrypted. In fact, no management

frames are encrypted in the 802.11 network standard as of the writing of this paper.

This is the only case in which the value of a field will be of a variable length that I can think of at

the moment. None of the fields are separated in any way, but we know by the protocol how long in

bytes they are and we can easily analyze the packets to find their offset from the beginning of the

header. The ESSID can range from 0 to 32 characters, or bytes as we saw in listing 0. This would be

represented as two whole rows in the Wireshark display on the lower left side. As a false sense of

security, these bytes can be hidden when administrators decide to “hide” the ESSID. There are three

packets which include the ESSID even when it is hidden, and those are Association Request, Probe

Request, and Probe Response. All of which are unencrypted management frames - and as a side note,

can be generated using the libpcap libraries.

2 Yet.

Page 7: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

The Code

Now that we have a grasp as to what we need to do to parse out specific values of the 802.11

header, let’s dive into the C code. This will be a fair amount of intermediate code, but don’t let this deter

you from coding your own implementation or following along! I promise I will make this as easy as

possible and explain all that I can. First and foremost, we include the libpcap header file pcap.h in a

preprocessor directive:

#include <pcap.h>

Code listing 0: including the libpcap header file

Remember if we want to use the printf(); stdout function, we need the stdio.h file also.

#include <stdio.h>

Code listing 1: including the standard input/output header file

The packet capture functions use a data type of uint8_t which comes with the netinet/in.h

header files which we need to include: (my machine through errors without the header file)

#include <netinet/in.h> // needed for the uint8_t in

Code listing 2: including the net-internet header file

Now, we use a few global variables of special types from the pcap.h header for the creation and

writing of the packet capture (.pcap) file. Remember the packets are binary data to the computer. We

can’t just open them in a basic text editor and read them or write them so we use specific packet

capture handling functions from pcap.h to do this work for us.

pcap_t *capturehandle; /* packet capture handle */pcap_dumper_t *pcapfile; /* output file */pcap_t *filehandle; /* output file handle */char *filename = "output.cap"; /* output file name */

Code listing 3: packet capture file management from libpcap

Now, we can prototype the function we want to call when we receive a packet. Prototyping just

allows us to declare that a function exists elsewhere in the C code outside of main(); that will be called by

another function. The arguments to this function: u_char *args, const struct pcap_pkthdr *header, const

u_char *packet are simply for the packet that we will pass to it which another function3 will fill in for us.

// prototype the function to call per packetvoid sniff(u_char *args, const struct pcap_pkthdr *header, const u_char *packet); Code listing 4: prototyping the function that pcap_loop(); or pcap_dispatch(); will call per packet.

We also need a data type of struct to gather the uint8_t value of it_len.

3 pcap_dispatch();

Page 8: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

// This is for the it_len value:struct radiotap_header { uint8_t it_rev; uint8_t it_pad; uint16_t it_len; };

Code listing 5: struct using the uint8_t data type for the length of the radiotap header using in calculating the offset

for other values within the header.

Now we can begin our main(); C function. The arguments for main(); which are int argc, char *argv[] are

the standard arguments for accepting command line input from the terminal. I will use these for

gathering the 802.11 NIC device from the user.

int main(int argc, char *argv[]){ const u_char *bssid; int offset = 0; struct radiotap_header *rtaphdr; char *erbuf; // A pointer to the error string char *dev; // A pointer to our device pcap_t *handle; // pointer to the header grabbed by pcap dev = argv[1]; // the device as an argument "mon0,wlan0,etc" // we grab out packet with pcap_open_live(); args are: // device, length in bytes to sniff, promisc mode boolen, // milliseconds to time out(0 = never), string to store error msg handle = pcap_open_live(dev, SNAP_LEN, 1, 0, erbuf); // descriptor // our data link type returned should be 127 for "LINKTYPE_IEEE802_11_RADIOTAP" printf("Type: %d\n",pcap_datalink(handle)); pcap_dispatch(handle, 1, sniff, NULL); // call sniff(); when we get a packet return(0); }

Code listing 6: The main(); C function that is called at runtime.

In main(); we set our integer data type variable of offset to 0. Then we design a struct data type

pointer rtaphdr. This will be used for the length variable used in sniff(); Next we create an error buffer

character array pointer and another for the device we are using. This device is passed to main(); as an

argument from the command line. We will run this application as ./sniffer <device> As an example in my

case I used ./sniffer mon0 after creating the VAP mon0 with airmon-ng start wlan0. Next the handle

variable is for file writing. In the sniff(); function, that we will define later, is the packet is a beacon we

save it to a file so we can analyze it with Wireshark later is need be.

Next, we run the function pcap_open_live(); from pcap.h and assign it’s returned value descriptor

to handle.

Then, we simply use the printf(); function from the stdio.h header file to display the frames Link

Layer Type which initially returned 127 for DLT_IEEE802_11_RADIO from our RTL8187 driver4. To do this

4 This is implementation specific. The values of the offset and DLT_ type may differ in another environment. We

can determine the DLT_ Link Layer Type by using the pcap_datalink(handle); function as in code listing 6 above.

Page 9: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

we call the pcap_datalink(); function from the pcap.h header file and pass it the handle variable of our

packet. During my initial development, I was basically left in the dark and needed to feel my way

through the first captured packet with Wireshark.

Next, we call another pcap.h function pcap_dispatch(); which we pass the packet via the handle

variable, an integer 1 for packets to process, the function sniff(); to handle the packet and NULL because

we pass nothing to the callback routine. Let’s take a look at the sniff(); function now.

void sniff(u_char *args, const struct pcap_pkthdr *header, const u_char *packet){ const u_char *bssid; // character array for BSSID and ESSID const u_char *essid; int offset = 0; struct radiotap_header *rtaphdr; rtaphdr = (struct radiotap_header *) packet; offset = rtaphdr->it_len; if(packet[offset] == 0x80){ // subtype 8 for beacon starts at offset 27 // bssid = packet + offset + 10; // BSSID starts here in beacons bssid = packet + 36; // BSSID starts here in beacons essid = packet + 64; // ESSID starts here and ends with a simple 0x1 char *ssid; // Let's make a simple char array of the ESSID // let's construct the essid: unsigned int i = 0; while(essid[i] > 0x1){ // loop through bytes values start with essid[] printf("hex char: %x\n",essid[i]); // until we hit the 0x1 ssid[i] = essid[i]; // ssid[] string i++; // here would be good chance to filter with strcmp(); } ssid[i] = NULL; // terminate the string printf("ESSID string: %s\n", ssid); // print the stringprintf("BSSID: %02X:%02X:%02X:%02X:%02X:%02X\n", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); // Below is more stuff to write the cap file: filehandle = pcap_open_dead(DLT_IEEE802_11_RADIO, BUFSIZ); pcapfile = pcap_dump_open(filehandle, filename); // open it for writing pcap_dump((u_char *) pcapfile, header, packet); // write the packet // Now we close the file stream so that it doesn't get corrupt: pcap_dump_close(pcapfile); pcap_close(filehandle); } }

Code listing 7: the sniff(); function that “handles” the packet by parsing the header, outputting information, and

writing the packet to file.

This function returns an integer value that can be matched to the table on the http://www.tcpdump.org/linktypes.html website.

Page 10: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

The Dispatched Function

This is a large snippet of code, but it is incredibly easy to understand. I have loaded all of these

with comments to help me understand the code when modifying or troubleshooting it. The goal of this

sniff(); function, once dispatched, is simple. Find the ESSID and BSSID and print them, then print the

binary packet data to a file, close it, then return. The arguments passed to sniff(); from pcap_dispatch();

are obtained, and assigned in the first line. The next two lines are pointer objects to the first values of

the BSSID and ESSID which we need to calculate. Next, we define the offset as 0 which we will need to

change soon also and use in the calculation for the offsets of ESSID and BSSID. Now we create a pointer

and then assign it to a new struct of radiotap_header. This struct will be used to calculate the length of

the offset. We then assign offset to the length with uint16_t it_len from the struct.

Now that we know the offset, we can check the frame type and we do with packet[offset]. If it is

a beacon frame as we describe above 00 80 then we continue to process the packet. Checking out figure

1, we notice that the ESSID of the beacon frame is 64 bytes away from the beginning of the frame

header. So we add 64 onto the packet length and assign that value to the *essid pointer. As pointers in C

go, is that we can now step through the values in memory from that value until we get a 0x1 terminator

byte for the ESSID.

Page 11: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

BSSID Retrieval

Figure 2: the BSSID has an offset of 16 x 2 rows + 4, or 36 byte offset.

We know that the BSSID is the MAC address of the AP and that a MAC address is 6 bytes. So we

need to find the BSSID in the frame, which we did in figure 2 and it starts 36 bytes away from the

beginning of the frame. We assign this to the *bssid pointer and we can now access the 6 bytes with

bssid[0] through bssid[5]. This is an aspect of C that proves to be most useful. Now, we create a new

pointer to a character array that we will assign the actual ESSID to *ssid[].

We create an integer token i and set it to 0, then use a while loop to gather all characters of the

ESSID, stopping with 0x1, and assign them to the *ssid[] character array. We then terminate the array so

truncate any leftover garbage from the assignment by adding the NULL character to the end of it.

Finally we print the ESSID and BSSID to the stdout (screen). We use the %02X hexadecimal

specifier with printf(); for the bytes of the BSSID and the %s string identifier to print the entire ESSID

character array to the screen. The remaining five lines simply write the packet binary data to the .cap file

and close it up. That’s all we need to do. We can repeat this process as much as we’d like to display any

values we’d like to from the packets.

Page 12: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

Channel Frequency

Let’s try to get the channel information from the packet as well. The channel is stored in little

endian format, which means the bytes are swapped. Say we have channel 11, or 2.412GHz which is sent

as the value 2412. 2412 = 0x096c. It’s sent in the packet as we can see in Figure 3 as 6c 09. We can easily

multiply the 09 by 256 then add 09 to get the channel frequency5. We can also see that the offset from

the first byte is 18.

Figure 3: the channel has an offset of 16 x 2 rows + 4, or 36 byte offset.

Let’s try that in C programming.

const u_char *ch; ch = packet + 18; int chi = ch[1] * 256 + ch[0]; printf("channel: %i\n",chi);

Code listing 8: simple calculation of concatenated hexadecimal values 0x096c we first multiply the 09 by 256, then

add the 6c which yields 2412 – which then corresponds to the frequency 2.412GHz or channel 11 for 802.11

networking.

There’s no conversion needed, we can multiply and add the values directly from the packet. We multiply

the second byte to 256 because of the little endian format. Then we add the first byte. This will yield the

following output:

channel: 2412

Code listing 9: output form our printf(); function.

When re-compiled and ran from our example.

5 Frequency times 1000 for Ghz (in our case 2412, which is channel 11 and 2.412GHz)

Page 13: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

Here is a simple example of this in action:

Figure 4: values stored in memory can be multiplied to integers without conversion in C programming.

Page 14: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

Conclusion

With these small steps and these few lines of code we have successfully pulled the ESSID and

BSSID and frame type out of the packet! Once we find the offset and length of any field, we can then

create new offsets to determine their values and then do simple hexadecimal to ASCII or integer

conversion and display them with printf();

Remember that the fields are not separated by anything, so we need to either know or calculate

the lengths of each. This is simply how I came to understand the 802.11 packet structure. Wireshark is

the most useful network traffic analyzer out there and it’s free! If you copy all of the code snippets, save

for the added channel calculation portion6, and save to a .c file, you can compile it with gcc input.c –o

output –lpcap. Then run with a device as an argument, as I did with ./output mon0

6 Unless you knew where to put it.

Page 15: Catching Pink Dolphins using Libpcap via 802weaknetlabs.com/files/libpcapandc.pdf · Then, I decided to dive right into C programming and using the Libpcap libraries for packet capture

References

TCPDUMP/Libpcap: Tcpdump,“TCPDUMP/LIBPCAP public repository”, Tcpdump, Changed by: , 20-May-

2013 http://www.tcpdump.org/

Google “wardriving fiasco”: Mathew J. Schwartz, “Google Wardriving: How Engineering Trumped

Privacy”,Mathew J. Schwartz http://www.informationweek.com/security/privacy/google-wardriving-

how-engineering-trumpe/232901230

Wireshark: the Wireshark Foundation, “Wireshark · Go Deep.”, the Wireshark Foundation,

ALFA AWUS036H rt8187: Amazon.com, “Amazon.com: Customer Reviews: Alfa AWUS036H Upgraded to

1000mW 1W 802.11b/g High Gain USB Wireless Long-Rang WiFi network Adapter with 5dBi Antenna -

for Wardriving & Range Extension”, Amazon.com, http://www.amazon.com/Alfa-AWUS036H-Upgraded-

Wireless-Long-Rang/product-reviews/B000QYGNKQ

Compat-Drivers: Mcgrof “Documentation/compat-drivers - Driver Backports Wiki” Mcgrof, last

modified on 25 September 2012, at 20:58 https://backports.wiki.kernel.org/index.php/Documentation/compat-drivers

Airodump-NG: Thomas D’otreppe, “airodump-ng [Aircrack-ng]”, Maintainer of Aircrack-NG Suite, Last

modified: 2012/05/08 15:18 by darkaudax. http://search.cpan.org/~saper/Net-Pcap-0.17/Pcap.pm

Perl Net::PCAP: Sébastien Aperghis-Tramoni, “Net::Pcap - Interface to pcap(3) LBL packet capture

library”,Sébastien Aperghis-Tramoni, http://search.cpan.org/~saper/Net-Pcap-0.17/Pcap.pm

Python LORCON: Dragorn, “LORCON wireless packet injection library”, Dragorn, unkown date,

http://search.cpan.org/~saper/Net-Pcap-0.17/Pcap.pm

Python SCAPY: Unknown (no meta data) “Scapy” unknown, unknown date (no meta data)

http://search.cpan.org/~saper/Net-Pcap-0.17/Pcap.pm