Pages

Saturday, June 9, 2012

forge ping (send forged icmp ping request)

     Linux is providing a wonderful platform for programmers also hackers ( better programmers). The one that admired me recently is the RAW-SOCKET. It allows you to send any packet yourself into your network. A well written network packet can do many things. Here in this post I shared a code to send a forged ping request.

     Ping, we all know, a regular network command. We (or else me) always use this command to check whether our computer is connected with the network. Nothing else. But also the ping does one important job of updating the ARP cache. So with a forged ping request we can easily poison the cache of all the host in the network. [Read more about ARP cache poisoning to get better understanding. May be in future I will write a blog entry on ARP cache, if I write a working example.]

Here is the code.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <linux/icmp.h>
#include <arpa/inet.h>
#include <errno.h>

#define SOURCE_ADDR "43.88.80.34"
#define DEST_ADDR "43.88.80.135"

int write_ip_header (void *);
void write_icmp_header (void *);
unsigned short calculate_checksum (unsigned short *, int len);

int main () {
    int sockfd;
    sockfd = socket (PF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (socket < 0) {
        perror ("socket failed");
        return -1;
    }

    char *packet = malloc (4096);
    if (!packet) {
        perror ("malloc failed");
        return -1;
    }

    memset (packet, 0, 4096);

    struct sockaddr_in to_addr;
    to_addr.sin_family = AF_INET;
    to_addr.sin_port = htons (23);
    inet_pton (AF_INET, DEST_ADDR, &to_addr.sin_addr);
   
    write_icmp_header (packet);
    int tot_len = write_ip_header (packet);

    int one = 1;
    if (setsockopt (sockfd, IPPROTO_IP, IP_HDRINCL, &one, sizeof (one)) < 0) {
        perror ("setsockopt faild: ");
        return -1;
    }

    if (sendto(sockfd, packet, tot_len, 0, (struct sockaddr *) &to_addr, sizeof (to_addr)) == -1) {
        perror ("sendto failed");
        return -1;
    }

    return 0;
}

int write_ip_header (void *packet) {

    struct iphdr *iph = (struct iphdr *) packet;

    iph->ihl = 0x5;
    iph->version = 0x4;
    iph->tos = 0;
    iph->tot_len = htons (sizeof (struct iphdr) + sizeof (struct icmphdr));
    iph->id = htons (1234);
    iph->frag_off = 0;
    iph->ttl = htons (16);
    iph->protocol = 1; //ICMP
    iph->check = 0;
    iph->saddr = inet_addr (SOURCE_ADDR);
    iph->daddr = inet_addr (DEST_ADDR);

    iph->check = calculate_checksum ((unsigned short *) packet, ntohs (iph->tot_len));
    printf ("ip checksum: %x\n", iph->check);

    return ntohs (iph->tot_len);
}

void write_icmp_header (void *packet) {

    struct icmphdr *icmph = (struct icmphdr *) ((char *)packet + sizeof (struct iphdr));

    icmph->type = 8;
    icmph->code = 0;
    icmph->checksum = 0;
    icmph->un.echo.id = htons (123);
    icmph->un.echo.sequence = 0;

    icmph->checksum = calculate_checksum ((unsigned short *) icmph, sizeof (struct iphdr));
    printf ("icmp checksum: %x\n", icmph->checksum);
}

unsigned short calculate_checksum (unsigned short *buf, int len) {
    unsigned long int sum;

    for (sum = 0; len > 1; len -= 2) {
        sum += *buf++;
    }

    if (len) {
        sum += (unsigned char) *buf;
    }

    while (sum >> 16) {
        sum = (sum >> 16) + (sum & 0xffff);
    }

    return (unsigned short) ~sum;
}


     Just edit the macros SOURCE_ADDR and DEST_ADDR to any valid IP address you like.

     I tried this code. It is perfectly working on my CentOS maching. I think it will work on all Linux distribution. But I didn't test. Sorry I just wrote this program for fun. So I didn't add any comment lines. Feel free to ask any questions.