一 DoS攻擊
DoS攻擊即拒絕服務(wù)攻擊,如果細(xì)分來(lái)說(shuō)包括非常多種類(lèi),有UDP洪水,ACK類(lèi)型,DNS放大請(qǐng)求,NTP放大類(lèi)型,TCP洪水,HTTP洪水,SYN洪水。總之這些攻擊的目的是消耗服務(wù)器的帶寬,內(nèi)存,和cpu資源,從而讓服務(wù)器因資源耗盡對(duì)一切過(guò)來(lái)的請(qǐng)求,只能拒絕或提供性能很差的服務(wù),本文就是模擬SYN洪水攻擊,并對(duì)這種場(chǎng)景進(jìn)行分析,提供一些緩解攻擊的辦法。
二 環(huán)境和準(zhǔn)備
2.1 網(wǎng)絡(luò)環(huán)境
目前采用兩臺(tái)虛擬機(jī)和一臺(tái)物理機(jī)實(shí)現(xiàn)部署環(huán)境,虛擬機(jī)均為centos8.5 ,整個(gè)網(wǎng)絡(luò)環(huán)境如下:
2.2 軟件安裝
Nginx部署的web采用Docker簡(jiǎn)單部署,Dos工具采用hping3 發(fā)起syn攻擊。
#podman類(lèi)似docker,幾乎可以通用
#-i 以交互模式運(yùn)行容器,通常與 -t 同時(shí)使用
#-t 為容器重新分配一個(gè)偽輸入終端,通常與 -i 同時(shí)使用
#-d 后臺(tái)運(yùn)行容器,并返回容器ID
[root@localhost ~]# podman run -itd --name=nginx3 -.NETwork=host nginx
#測(cè)試服務(wù)是否啟動(dòng),雖然是404 但是請(qǐng)求速度還是很快的
[root@localhost ~]# curl -s -w 'Http code: %{http_code}nTotal time:%{time_total}sn' -o /dev/null http://192.168.31.50
Http code: 404
Total time:0.000963s
攻擊工具安裝:
yum install hping3
三 攻擊分析
3.1 hping3 發(fā)送SYN包攻擊
# -S 發(fā)送SYN包,-p 發(fā)送的端口80 -i u15 每1us 發(fā)送一個(gè)包
[root@Miwifi-RA72-srv ~]# hping3 -S -p 80 -i u1 192.168.31.50
3.2 服務(wù)機(jī)器分析
首先發(fā)現(xiàn)訪問(wèn)變慢了,注意有時(shí)候并不明顯,測(cè)試url訪問(wèn)性能:
[root@MiWiFi-RA72-srv ~]# curl -s -w 'Http code: %{http_code}nTotal time:%{time_total}sn' -o /dev/null http://192.168.31.50
Http code: 404
Total time:31.909641s
時(shí)間確實(shí)增加了,竟然要31s,注意一定要發(fā)一段時(shí)間,不然看不到效果。 那網(wǎng)絡(luò)有問(wèn)題,我們首先要看下網(wǎng)絡(luò)的流量情況:
[root@localhost ~]# sar -n DEV 3
平均時(shí)間: IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
平均時(shí)間: lo 1.22 1.22 0.06 0.06 0.00 0.00 0.00 0.00
平均時(shí)間: ens33 16441.20 15444.20 963.43 905.56 0.00 0.00 0.00 0.79
平均時(shí)間: ens37 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
平均時(shí)間: ens38 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
平均時(shí)間: ens39 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
計(jì)算平均包長(zhǎng): 963*1024/16441.20=59B ,發(fā)現(xiàn)這些都是小包,下面我們需要繼續(xù)分析下這些包是什么包,利用tcpdump抓包:
tcpdump -i ens33 -n tcp port 80 -w test.pcap
下載下來(lái)用wireshark分析下如下: wireshark 打開(kāi)文件后,通過(guò)統(tǒng)計(jì)->流量圖菜單彈出流分析:
首先我們可以看到192.168.31.200發(fā)起了很多的SYN包給192.168.31.50,192.168.31.50給192.168.31.200回復(fù)了SYN+ACK包,想建立了TCP連接,但是被192.168.31.200發(fā)送RST包中斷了,這個(gè)和我們預(yù)期有些差別,主要是我們的IP沒(méi)有隨機(jī),導(dǎo)致我們會(huì)回復(fù)RST包,這樣雖然半連接也在增多,因?yàn)镽ST會(huì)導(dǎo)致終止了,所以消耗資源增加不夠快,為了讓服務(wù)器不回復(fù)給我們,我們可以通過(guò)hping3的另外選項(xiàng)發(fā)送包:
hping3 --rand-source -S -p 80 -i u1 192.168.31.50
抓包后繼續(xù)用wireshark分析:
可以看到有大量的隨機(jī)ip對(duì)192.168.31.50發(fā)起連接, 192.168.31.50對(duì)這些ip做響應(yīng),但是這些ip其實(shí)是欺騙的ip,導(dǎo)致無(wú)法收到響應(yīng)包括RST報(bào)文,所以會(huì)導(dǎo)致SYN+ACK重發(fā),從而消耗系統(tǒng)的資源。
TCP的三次握手還不清楚的話,可以按照下面的圖理解下:
四 緩解辦法
4.1 封IP
如果我們發(fā)現(xiàn)有固定的IP發(fā)來(lái)大量的SYN包,可以采用iptables 封了這個(gè)IP,禁止特定ip來(lái)發(fā)起連接 然后啟動(dòng)防火墻:
#iptables -I INPUT -s 192.168.31.200 -p tcp -j REJECT
#啟動(dòng)防火墻
#systemctl start firewalld
這時(shí)候在抓包,發(fā)現(xiàn)被拒絕了。
[root@MiWiFi-RA72-srv ~]# hping3 -S -p 80 -i u1 192.168.31.50
HPING 192.168.31.50 (ens33 192.168.31.50): S set, 40 headers + 0 data bytes
ICMP Packet filtered from ip=192.168.31.50 name=UNKNOWN
ICMP Packet filtered from ip=192.168.31.50 name=UNKNOWN
ICMP Packet filtered from ip=192.168.31.50 name=UNKNOWN
ICMP Packet filtered from ip=192.168.31.50 name=UNKNOWN
ICMP Packet filtered from ip=192.168.31.50 name=UNKNOWN
ICMP Packet filtered from ip=192.168.31.50 name=UNKNOWN
ICMP Packet filtered from ip=192.168.31.50 name=UNKNOWN
ICMP Packet filtered from ip=192.168.31.50 name=UNKNOWN
[send_ip] sendto: Operation not permitted
tcpdump抓包看下,發(fā)現(xiàn)沒(méi)有回復(fù)了,也許是沒(méi)有來(lái)及回復(fù)- -
08:29:26.872385 IP 192.168.31.200.60180 > 192.168.31.50.http: Flags [S], seq 633025815, win 512, length 0
08:29:26.872389 IP 192.168.31.200.60181 > 192.168.31.50.http: Flags [S], seq 1327904571, win 512, length 0
08:29:26.872488 IP 192.168.31.200.60182 > 192.168.31.50.http: Flags [S], seq 1677934026, win 512, length 0
08:29:26.872498 IP 192.168.31.200.60183 > 192.168.31.50.http: Flags [S], seq 940613549, win 512, length 0
08:29:26.872510 IP 192.168.31.200.60184 > 192.168.31.50.http: Flags [S], seq 245284800, win 512, length 0
08:29:26.872514 IP 192.168.31.200.60185 > 192.168.31.50.http: Flags [S], seq 620067640, win 512, length 0
08:29:26.872612 IP 192.168.31.200.60186 > 192.168.31.50.http: Flags [S], seq 1859432659, win 512, length 0
08:29:26.872622 IP 192.168.31.200.60187 > 192.168.31.50.http: Flags [S], seq 2106267374, win 512, length 0
9856 packets dropped by kernel
這種局限性很大,一旦攻擊端改成隨機(jī)ip或者DDoS攻擊,那么就不好封IP了。
4.2 限制每秒的SYN報(bào)文
# 限制syn并發(fā)數(shù)為每秒1次 這個(gè)可能會(huì)誤傷啊,如果用戶(hù)比較多很多被限制了
#iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT
# 限制單個(gè)IP在60秒新建立的連接數(shù)為10
# iptables -I INPUT -p tcp --dport 80 --syn -m recent --name SYN_FLOOD --update --seconds 60 --hitcount 10 -j REJECT
#刪除
# 查看規(guī)則
iptables -nL --line-number
# 刪除規(guī)則 第二行
iptables -D INPUT 2
怎么知道規(guī)則生效那,除了上面的,還可以通過(guò)查看連接狀態(tài):
[root@localhost ~]# netstat -an|grep RECV|wc -l
128
加上規(guī)則后,這個(gè)數(shù)量為0,表示被拒絕了。
4.3 內(nèi)核參數(shù)調(diào)整
增大半連接長(zhǎng)度 通過(guò)上面的TCP三次握手圖可以知道,如果增加了半連接隊(duì)列的長(zhǎng)度,一定程度上可以緩解下Dos攻擊。
半連接隊(duì)列長(zhǎng)度 = roundup_pow_of_two(max_t(u32,min(somaxconn,sysctl_max_syn_backlog,backlog),8) +1)) roundup_pow_of_two為最接近2的指數(shù)次冪,簡(jiǎn)單理解為向上按照2的指數(shù)次冪取整。 不同的系統(tǒng)版本。
- tcp_max_syn_backlog是指定所能接受SYN同步包的最大客戶(hù)端數(shù)量,即半連接上限;
- somaxconn是指服務(wù)端所能accept即處理數(shù)據(jù)的最大客戶(hù)端數(shù)量,即完成連接上限。 默認(rèn)值:
[root@localhost ~]# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
128
[root@localhost ~]# cat /proc/sys/net/core/somaxconn
128
- backlog 為用戶(hù)程序設(shè)置的隊(duì)列長(zhǎng)度。 更改方法如下:
[root@localhost ~]# sysctl -w net.ipv4.tcp_max_syn_backlog=1024
net.ipv4.tcp_max_syn_backlog = 1024
[root@localhost ~]# sysctl net.ipv4.tcp_max_syn_backlog
net.ipv4.tcp_max_syn_backlog = 1024
[root@localhost ~]# sysctl -w net.core.somaxconn=1024
net.core.somaxconn = 1024
[root@localhost ~]# sysctl net.core.somaxconn
net.core.somaxconn = 1024
永久更改可以將配置寫(xiě)入到/etc/sysctl.conf中
[root@localhost ~]# vim /etc/sysctl.conf
net.core.somaxconn = 1024
net.ipv4.tcp_max_syn_backlog = 1024
#生效
[root@localhost ~]# sysctl -p
順便聊下全連接,完成三次握手的會(huì)將連接信息放到全連接隊(duì)列中,我們可以通過(guò):
ss -lnt
[root@localhost ~]# ss -lnt
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:111 0.0.0.0:*
LISTEN 0 128 0.0.0.0:80 0.0.0.0:*
在Listen中其中Send-Q 為全連接隊(duì)列的長(zhǎng)度,Recv-Q為目前使用了多少。 全連接隊(duì)列的大小= min(backlog, somaxconn) backlog為用戶(hù)設(shè)置的隊(duì)列長(zhǎng)度,somaxconn同上,為系統(tǒng)參數(shù)。
減少syn-ack重發(fā)次數(shù) 服務(wù)器端收到SYN連接后會(huì)回復(fù)SYN+ACK,如果回復(fù)失敗會(huì)進(jìn)行多次重試,默認(rèn)5次,我們可以減少重試次數(shù):
[root@localhost ~]# sysctl net.ipv4.tcp_synack_retries
net.ipv4.tcp_synack_retries = 5
[root@localhost ~]# sysctl -w net.ipv4.tcp_synack_retries=1
net.ipv4.tcp_synack_retries = 1
[root@localhost ~]# sysctl net.ipv4.tcp_synack_retries
net.ipv4.tcp_synack_retries = 1
開(kāi)啟SYN cookie 對(duì)于半連接的隊(duì)列長(zhǎng)度,設(shè)置多長(zhǎng)合適那,不太好設(shè)置,我們可以打開(kāi)SYN cookie,這樣在連接隊(duì)列滿了之后,會(huì)根據(jù)SYN的包計(jì)算一個(gè)cookie值,這個(gè)cookie作為將要返回的SYN ACK包的初始序列號(hào),服務(wù)器端不再保存任何信息,客戶(hù)回一個(gè)ACK包時(shí)候,根據(jù)包頭信息計(jì)算cookie值,在于返回的確認(rèn)序列號(hào)對(duì)比,如果相同,則是一個(gè)正常連接,如果不合法,則返回一個(gè)RST報(bào)文,這其中校驗(yàn)cookie是否合法時(shí)候,還要判斷ACK時(shí)間是否為4分鐘內(nèi)到達(dá),是才合法。
缺點(diǎn) 看起來(lái)好像cookie方式比較完美,但是由于不再服務(wù)器端保存任何信息,所以就無(wú)法重發(fā)SYN+ACK報(bào)文,由于有一定計(jì)算量,且如果對(duì)方采用ACK攻擊,那么服務(wù)器端需要計(jì)算比較,也無(wú)法預(yù)防的。
[root@localhost ~]# sysctl -w net.ipv4.tcp_syncookies=1
net.ipv4.tcp_syncookies = 1
同樣,永久生效需要寫(xiě)入到/etc/sysctl.conf中去。
五 總結(jié)
Dos攻擊,特別是DDos攻擊,采用的是發(fā)送大量包的方法來(lái)消耗服務(wù)器端的資源,上面的手段也只能減緩攻擊,不能徹底解決問(wèn)題,業(yè)界對(duì)這種攻擊一般用流量清洗的辦法,將DDos攻擊的流量和正常用戶(hù)請(qǐng)求分離出來(lái),或者增加CDN緩存,和WAF等方式。