Sunday, February 16, 2014

libnet library part 1: Introduction and ICMP Echo Packets



Introduction:
Libnet is a library that lets developers easily construct and inject individual network packets into the network. Libnet hides most of the low-level details of how to construct and inject packets allowing the developer to easily create cross-platform security tools.

In this blog we will be showing how to use libnet (and also libpcap) to develop various network security tools.  The libnet library that we develop over the next few posts will be used to develop these tools.  While libnet can be used to write some powerful network security tools it can also be used to write malicious tools as well.

If you wish to use libnet to develop tools specifically for Apple's OS X environment I would recommend reading the libnet chapter in my book iOS and OS X Network Programming Cookbook which shows how to develop applications with libnet and Objective-C.  In this series of libnet articles we will be developing a basic libnet library in C that you can integrate in your applications.  While you could use this C library to develop applications for OS X as well, using an Objective-C library is preferred.

Libnet Installation:
Libnet was originally maintained at http://packetfactory.openwall.net however this site has not been updated since 2007.  Since then a number of individuals have forked the library in an attempt to maintain the development.  The version that I use for my projects is the libnet-dev project maintained on sourceforge.  You can find the code at http://sourceforge.net/projects/libnet-dev/files/libnet-1.2-rc3.tar.gz/download   

Once you download the code you will want to unzip and untar the package.  You can then install libnet by running the following three commands within the directory that was created when you untarred the libnet package.

./configure
make
sudo make install

These commands will install the libnet headers to /usr/local/include and the libnet library files to /usr/local/lib.  Remember to link libnet to your project when you build it.

Creating a ICMP packet:
Before you begin creating network packets you need to understand how a packet is constructed.  I wrote a brief introduction in this post:  http://network-development.blogspot.com/2014/02/layers-of-internet-protocol-suite.html.  While this introduction will give you the information that you need to begin constructing packets, I would recommend getting a packet capture utility like Wireshark and examining packets coming into your system.  You can learn a lot about how packets are constructed and how protocols work by examining the packets coming in and going out of your system. 

When we write the functions to create ICMP, TCP and UDP packets, we will need a couple of helper functions to create the IPv4 header and to write the packet to the network.  If you are familiar with how packets are created, you may be asking yourself about the Link Layer headers.  When we initiate the libnet context we will be setting the injecting type to LIBNET_RAW4 which tells libnet to automatically generate the Link Layer headers for us.  We will briefly discuss the injection types when we look at the code later in this post.
To see what fields are in the IPv4, ICMP Echo and ICMP Echo Reply headers, you can refer to this post:  http://network-development.blogspot.com/2014/01/packet-headers.html.  I am still unsure if that is the best way to present the headers but it is the best I have been able to come up with.  If anyone has any suggestions please let me know.


libnet_lib header file:
Lets dive into my favorite part, the code.  We will begin by creating a header file for our libnet library.  Lets create a file called libnet_lib.h and add the following code to it. 

#include <sys/types.h>
#include <stdbool.h>
#ifndef LIBNET_LIB_H
#define LIBNET_LIB_H
bool sendICMPEcho(char *addr, char *payload, int type, char *interface);

#endif // LIBNET_LIB_H

In this code we define one function which is the sendICMP() function.  This function accepts four arguments:

char *addr:  This is the address to send the packet too.
char *payload:  This is any payload that you wish to send in the ICMP packet.
int type:  The ICMP packet type.  Since this function sends a ICMP Echo packet we would want to set this to ICMP_ECHO or ICMP_ECHOREPLY however libnet defines the following ICMP types:
ICMP_ECHOREPLY
ICMP_UNREACH
ICMP_SOURCEQUENCH
ICMP_REDIRECT
ICMP_ECHO
ICMP_ROUTERADVERT
ICMP_ROUTERSOLICIT
ICMP_TIMXCEED
ICMP_PARAMPROB
ICMP_TSTAMP
ICMP_TSTAMPREPLY
ICMP_IREQ
ICMP_IREQREPLY
ICMP_MASKREQ
ICMP_MASKREPLY
char *interface:  The interface to send the packet though.  By setting this to NULL libnet will pick the most appropriate interface to send the packet.  Unfortunately on some systems (like my Ubuntu laptop) this automatically defaults to eth0 even if it is not connected to anything.

In future posts, as we add additional functions (UDP and TCP) to our library, we will define those functions in this header file as well.

sendICMPEcho():
The sendICMPEcho() function will build and inject a ICMP echo packet into the network.  Lets take a look at the code and then we can examine it:

bool sendICMPEcho(char *addr, char *payload, int type, char *interface)
{
    libnet_t *lnet;
    u_int16_t id,seq;
    char errbuf[LIBNET_ERRBUF_SIZE];
    lnet = libnet_init(LIBNET_RAW4, interface, errbuf);
    if ( lnet == NULL ) {
        printf("Error with libnet_init():  %s\n", errbuf);
        return false
    }
    /* Generating a random id */
    libnet_seed_prand (lnet);
    id = (u_int16_t)libnet_get_prand(LIBNET_PR16);
    /* Building ICMP header */
    seq = 1;
    if (libnet_build_icmpv4_echo(type,
                             0,
                             0,
                             id,
                             seq,
                             (u_int8_t*)payload,
                             sizeof(payload),
                             lnet,
                             0) == -1)
    {
        printf("Error building UDP header: %s\n",libnet_geterror(lnet));
        libnet_destroy(lnet);
        return false;
    }
    if (!build_ipv4 (addr, IPPROTO_ICMP, sizeof(payload) + LIBNET_ICMPV4_ECHO_H, lnet))
                         return false;

    bool success = write_packet (lnet);
    libnet_destroy(lnet);
    return success;
}
We begin the sendICMPEcho() function by using the libnet_init() function to initiate the libnet context.  We need to initiate this context prior to calling any other libnet functions.  The prototype for the libnet_init function is:  libnet_init(int injection_type, const char *device, char *err_buf);  As you can see, this function takes three arguments which are:
int injection_type:  This is the libnet injection type.  I almost exclusively use LIBNET_RAW4 which tells libnet to automatically create the link layer headers for me.  Some of the other possible values are:  LIBNET_LINK , LIBNET_LINK_ADV , LIBNET_RAW4 , LIBNET_
RAW4_ADV , LIBNET_RAW6 , and LIBNET_RAW6_ADV.
const char *device:  This is the name of the interface to use.  This can be set to NULL to let libnet choose the interface.
char *err_buf:  This buffer will contain any errors that occur if something goes wrong with the request.
After we initiate the libnet context we then verify that it was properly initiated.  If it was not properly initiated we return false to let the calling function know that the packet was not sent out.

Next we need to create a random number to be used as the identifier.  We use the libnet_seed_prand() function to seed the pseudo-random number generator and then the libnet_get_prand() function to retrieve a random number.  The LIBNET_PR16 constant specifies a number between 0 and 32767.

Next we build the ICMP header.  To create a echo request we use the libnet_build_icmpv4_echo() function which has a prototype of:  libnet_ptag_t libnet_build_icmpv4_echo(uint8_t type, uint8_t code, uint16_t sum,uint16_t id, uint16_t seq, const uint8_t* payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag);.  As you can see this function accepts nine arguments:
uint8_t type:  This is the ICMP type that we passed to the function.  Since we are using the libnet_build_icmpv4_echo() funciton, this value should be either  ICMP_ECHO or ICMP_ECHOREPLY.
uint8_t code:  We set this to 0, it is not used for the ICMP Echo request.
uint16_t sum:  This is set to 0 to let libnet generate the checksum
uint16_t id:  We set this to the pseudo-random number that we generated.
uint16_t seq:  This is the sequence number for the packet.
const uint8_t* payload:  This is our payload.
uint32_t payload_s:  This is the size of the payload.
libnet_t *l:  This is the libnet context.
libnet_ptag_t ptag:  We set this to 0 to generate a new header.

There are several other libnet functions that can be used to create other ICMP packet types.  These are:  libnet_build_icmpv4_mask(), libnet_build_icmpv4_unreach(), libnet_build_icmpv4_redirect(), libnet_build_icmpv4_timeexceed() and libnet_build_icmpv4_timestamp().  For this example we will be using just the libnet_build_icmpv4_echo() function.

If the libnet_build_icmpv4_echo() function returns -1, there was a problem generating the header.  If this happens we return a boolean false to the calling function to let it know that the packet was not sent out.

After we generate the the ICMPv4 header we call our build_ipv4() function to build the IPv4 headers and our write_packet() function to send the packet out.  We will be discussing these functions later in this post. 

After the packet is sent we use the libnet_destroy() function to release the memory used by the libnet context.

Build_ipv4():
The build_ipv4() function accepts four arguments, they are:
char *addr:  This is the IP address of the destination device.
u_int8_t proto:  This is the protocol that is being sent.  In the sendICMPEcho() function above we send IPPROTO_ICMP. 
u_int32_t  psize:  This is the size of the ICMP header and the payload.
libnet_t *lnet:  This is the libnet context that the IPv4 header will be added too.

Lets look at the code for the build_ipv4() function:

void build_ipv4(char *addr, u_int8_t proto, u_int32_t  psize, libnet_t *lnet) {
    u_int32_t target, source;
    u_int16_t id;
    target = libnet_name2addr4(lnet, addr, LIBNET_DONT_RESOLVE);
    source  = libnet_get_ipaddr4(lnet);
    if ( source == -1 ) {
        printf("Error retrieving IP address: %s\n",libnet_geterror(lnet));
        libnet_destroy(lnet);
        return false;
    }
     libnet_seed_prand (lnet);
     id = (u_int16_t)libnet_get_prand(LIBNET_PR16);
    if( libnet_build_ipv4(LIBNET_IPV4_H + LIBNET_ICMPV4_ECHO_H + psize,
                              0,
                              id,
                              0,
                              64,
                              proto,
                              0,
                              source,
                              target,
                              NULL,
                              0,
                              lnet,
                              0) == -1)
    {
        printf("Error building IP header: %s\n",libnet_geterror(lnet));
        libnet_destroy(lnet);
        return false;
    }
return true;
}

We begin this function by calling the libnet_name2addr4() function which accepts a char array and returns a network-byte ordered IPv4 address.  The libnet_name2addr4() function accepts the following three arguments:
libnet_t *lnet: This is a pointer to the libnet context to use.
char* host_name: This is a pointer to the char array containing the name.
uint8_t use_name: This can be either LIBNET_RESOLVE or LIBNET_DONT_RESOLVE . If the char array contains a hostname such network-development.blogspot.com, we would want the libnet_name2addr() function to perform a DNS lookup prior to creating the network-byte-ordered IPv4 address; therefore, we would set this to LIBNET_RESOLVE constant. If the hostname contained an IP address, we would not want libet_name2addr4() to perform a DNS lookup, so we would use LIBNET_DONT_ RESOLVE constant.  For this function we assume that the addr char array contains an IP address.

The libnet_get_ipaddr4() function is then used to retrieve the IP address of our local device.  This function accepts one argument which is the libnet context.

Just like in the sendICMPEcho() function, we use the libnet_seed_prand() and libnet_get_prand() functions to create a pseudo-random number to use for an ID.

We use the libnet_build_ipv4() function to build our IPv4 header.  This function takes the following thirteen arguments:
uint16_t ip_len:  This is the size of the packet.  We add the IPv4 header size with the size of the headers that were created from the upper layers plus the payload (TCP, UDP or ICMP header size plus the payload).
uint8_t tos:  This field, now known as Differentiated Services Code Point (DSCP), may indicate a particular quality of service needs. Libnet still refers to TOS and is usually set to 0
uint16_t id:  This field is primarily used to uniquely identify fragments of an original packet.
uint16_t frag:  This specifies the offset of a particular fragment and is relative to the beginning of the original unfragmented packet.
uint8_t ttl:  This indicates the number of hops the packet can be routed. This number is decremented at each hop until it reaches its destination or it reaches 0.  If the Time to Live reaches 0 before the packet reaches its destination, the packet is discarded.
uint8_t prot:  This is the IP Protocol ID.
uint16_t sum:  This maps to the Checksum field and is set to 0 (zero) to have libnet auto-fill the checksum.
uint32_t src:  This is the IP address of the sending device.
uint32_t dst,:  This is the IP address of the device the packet is going to.
const uint8_t* payload:  This is the optional payload.
uint32_t payload_s:  This is the size of the optional payload.
libnet_t *l:  The libnet context to use for the IP header
libnet_ptag_t ptag:  We set this to 0 to generate a new header.

If the libnet_build_ipv4() function returns -1, then there was a problem creating the IPv4 header and we  release the libnet context and return false to the calling function.  If the libnet_build_ipv4() function was successful we return true.

write_packet():
The write_packet() function injects the packet that we created into the network.  This function accepts one argument which is the libnet context that contains the newly created packet.   The write_packet() function looks like this:

bool write_packet(libnet_t *lnet) {
        int bytes_written = libnet_write(lnet);
        if ( bytes_written != -1 ) {
            printf("%d bytes written to device %s.\n", bytes_written, libnet_getdevice(lnet));
            return true;
        } else {
            printf("Error writing packet: %s\n",libnet_geterror(lnet));
            return false;
        }
}

The write_packet() function calls the libnet_write() function to inject the packet.  This function accepts the libnet context that contains the packet as it's only argument and returns the number of bytes written.  If there was a problem injecting the packet, the libnet_write() function returns -1.

Using the sendICMPEcho() function:
Lets look at how we would use the sendICMPEcho() function:
bool success = sendICMPEcho("10.0.1.4", "Hello from libnet", ICMP_ECHO, "wlan0");
    if (success) {
        printf("Success");
    } else {
        printf("Failed");
    }

The sendICMPEcho() function accepts four arguments as described earlier in tis post.  In this code snippet I am sending a ICMP Echo request to a device with an IP Address of 10.0.1.4 using the wlan0 interface.  You could set the interface to null to let libnet pick the appropriate interface but on my Ubuntu laptop libnet defaults to eth0 which, depending where I am at, is not always connected to a network.

You can experiment a little with ICMP packets.  Try running Wireshark and seeing the request and replies as you send the packet out.  You can also try to send out an ICMP_ECHOREPLY (rather than a request) and seeing how different Operating systems respond.  It is a lot more fun to experiment with TCP packets but that will be for our next post when we cover creating both TCP and UDP packets with libnet.


No comments:

Post a Comment