HTTP DoS By Slow Request

HTTP DoS By Slow Request

Slow DoS 就是以一个极慢的速率发送 HTTP 请求报文,长时间占用服务器的处理进程/线程,通过在短时间内大量重复此行为,让服务器无法为正常请求分配服务处理进程/线程,导致 DoS。

以下测试服务器版本:

httpd Apache/2.4.52 (Unix)

nginx version: nginx/1.20.2

Slowloris

Slowloris攻击是一种针对web服务器的慢速HTTP攻击,由安全研究人员在2009年提出。

在HTTP协议中规定,HTTP头部以连续的 “\r\n\r\n” 作为结束标志,许多Web服务器在处理HTTP请求的头部信息时,会等待头部传输结束后再进行处理,因此,如果Web服务器没有接受到连续的 “\r\n\r\n”,就会一直接受数据并保持与客户端的连接,利用这个特性,攻击者能够长时间与Web服务器保持联系,并逐渐耗尽Web服务器的连接资源。

步骤
  1. 攻击者首先通过发送多个部分 HTTP 请求标头来打开与目标服务器的多个连接。
  2. 目标为每个传入请求打开一个线程,目的是在连接完成后关闭该线程。为提高效率,如果连接花费时间太长,服务器将使非常长的连接超时,从而为下一请求释放线程。
  3. 为了防止目标使连接超时,攻击者会定期向目标发送部分请求标头,以使请求保持活动状态。
  4. 目标服务器在等待请求终止时永远无法释放任何打开的部分连接。一旦所有可用线程都被使用,服务器将无法响应来自常规流量的其他请求,从而导致拒绝服务。

测试工具

slowloris-py

slowloris github 源码

usage

➜ slowloris-py --help 
usage: slowloris-py [-h] [-p PORT] [-s SOCKETS] [-v] [-ua] [-x] [--proxy-host PROXY_HOST] [--proxy-port PROXY_PORT] [--https] [--sleeptime SLEEPTIME] [host]
options:
  -h, --help            show this help message and exit
  -p PORT, --port PORT  Port of webserver, usually 80
  -s SOCKETS, --sockets SOCKETS
                        Number of sockets to use in the test
  -v, --verbose         Increases logging
  -ua, --randuseragents
                        Randomizes user-agents with each request
  -x, --useproxy        Use a SOCKS5 proxy for connecting
  --proxy-host PROXY_HOST
                        SOCKS5 proxy host
  --proxy-port PROXY_PORT
                        SOCKS5 proxy port
  --https               Use HTTPS for the requests
  --sleeptime SLEEPTIME
                        Time to sleep between each header sent.

:利用工具 python slowloris.py ,创建 2048 个 socket 去发送 http 请求,每个标头延时6秒:

slowloris-py -s 2048 --sleeptime 6 127.0.0.1

从源码可以看出为建立指定数量 TCP 连接,对每个 socket 发送请求方法,User-Agent 以及 Accept-Language 标头后,每隔指定时间就遍历所有 socket 并发送 X-a 标头。

很简单的一个小工具,单线程,发送固定的标头,只能作为一个对小型服务应用的测试工具

在这期间以及停止 slowloris 后的很长一段时间(取决于创建的socket数量) httpd 会产生大量的 internal dummy connection 日志:

image-20220414174759756

原因是 apache 服务器会对子进程进行监听,会向子进程发送 OPTIONS 请求,通常这种请求隔很长时间才会发送一次,但 slowloris 工具创建了大量连接,apache 服务器为其创建了大量进程,数量一多,该日志就写入的比较频繁了。

可以通过以下方法过滤该日志:

SetEnvIf User-Agent "(internal dummy connection)" dontlog
CustomLog logs/access_log common env=!dontlog

也可以直接过滤掉h环回地址访问产生的日志:

SetEnvIf Remote_Addr "127\.0\.0\.1" loopback
CustomLog logs/access_log common env=!loopback
slowhttptest

slowhttptest 是一个测试 slow HTTP DoS vulnerabilities 的工具。

usage

slowhttptest, a tool to test for slow HTTP DoS vulnerabilities - version 1.8.1
Usage: slowhttptest [options ...]
Test modes:
  -H               slow headers a.k.a. Slowloris (default)
  -B               slow body a.k.a R-U-Dead-Yet
  -R               range attack a.k.a Apache killer
  -X               slow read a.k.a Slow Read

Reporting options:

  -g               generate statistics with socket state changes (off)
  -o file_prefix   save statistics output in file.html and file.csv (-g required)
  -v level         verbosity level 0-4: Fatal, Info, Error, Warning, Debug

General options:

  -c connections   target number of connections (50)
  -i seconds       interval between followup data in seconds (10)
  -l seconds       target test length in seconds (240)
  -r rate          connections per seconds (50)
  -s bytes         value of Content-Length header if needed (4096)
  -t verb          verb to use in request, default to GET for
                   slow headers and response and to POST for slow body
  -u URL           absolute URL of target (http://localhost/)
  -x bytes         max length of each randomized name/value pair of
                   followup data per tick, e.g. -x 2 generates
                   X-xx: xx for header or &xx=xx for body, where x
                   is random character (32)
  -f content-type  value of Content-type header (application/x-www-form-urlencoded)
  -m accept        value of Accept header (text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5)

Probe/Proxy options:

  -d host:port     all traffic directed through HTTP proxy at host:port (off)
  -e host:port     probe traffic directed through HTTP proxy at host:port (off)
  -p seconds       timeout to wait for HTTP response on probe connection,
                   after which server is considered inaccessible (5)
  -j cookies       value of Cookie header (ex.: -j "user_id=1001; timeout=9000")

Range attack specific options:

  -a start        left boundary of range in range header (5)
  -b bytes        limit for range header right boundary values (2000)

Slow read specific options:

  -k num          number of times to repeat same request in the connection. Use to
                  multiply response size if server supports persistent connections (1)
  -n seconds      interval between read operations from recv buffer in seconds (1)
  -w bytes        start of the range advertised window size would be picked from (1)
  -y bytes        end of the range advertised window size would be picked from (512)
  -z bytes        bytes to slow read from receive buffer with single read() call (5)

其实发现该工具支持:

  • slow headers(-H:就是 slowloris;
  • slow body(-B:slow post
  • range attack(-R:针对 apache 服务器的工具;
  • slow read(-X:即以缓慢的速度读取服务器的响应,保持与服务器的连接;

该工具默认为 slowloris 模式。

服务器相关配置

httpd
请求处理数量

mpm_prefork_modulempm_worker_module 以及 mpm_event_module 模块定义了同一时间内最大进程(其中 worker、event 模块还涉及到线程数量),以及最大请求数量。

:prefork 模块相关配置

LoadModule mpm_prefork_module modules/mod_mpm_prefork.so

<IfModule mpm_prefork_module> 
    StartServers 5 
    MinSpareServers 5 
    MaxSpareServers 10 
    MaxRequestWorkers 250 
    MaxConnectionsPerChild 1000 
</IfModule> 

Web服务器能够处理的并发连接数是有限的,如果攻击者利用大量的受控主机发送这种不完整的HTTP GET请求并持续占用这些连接,就会耗尽Web服务器的连接资源,导致其他用户的HTTP请求无法被处理,造成拒绝服务。

超时

apache httpd 提供了 reqtimeout_module 来设置超时和接收请求的最小数据速率。如果发生超时或数据速率太低,服务器将关闭相应的连接。

RequestReadTimeout [handshake=timeout[-maxtimeout][,MinRate=rate] [header=timeout[-maxtimeout][,MinRate=rate] [body=timeout[-maxtimeout][,MinRate=rate]

该指令可应用在基础配置或 VirtualHost 中。

默认:

RequestReadTimeout handshake=0 header=20-40,MinRate=500 body=20,MinRate=500

值为 \(0\) 表示没有超时限制

该指令可以设置各种超时来完成TLS握手,从客户端接收请求标头请求正文。如果客户端未能在配置的时间内完成所有这些阶段,408 REQUEST TIME OUT 则会发送错误。

对于SSL虚拟主机,handshake 超时值是执行初始SSL握手所需的时间。如果将用户的浏览器配置为查询证书吊销列表,并且无法访问CRL服务器,则初始SSL握手可能要花费相当长的时间,直到浏览器放弃等待CRL为止。因此,handshake 对于SSL虚拟主机,超时应考虑到这种可能的开销(如有必要)。主体超时值包括SSL重新协商所需的时间(如有必要)。

AcceptFilter 在使用中(通常在Linux和FreeBSD的情况下),套接字未至少一个字节(或整个请求之前发送到服务器的过程 httpready)被接收。配置的握手和标头超时 RequestReadTimeout 仅在服务器进程收到套接字后才有效

对于三个超时阶段(握手,标头或正文)中的每个阶段,都有三种指定超时的方法:

  • 固定超时值 stage=timeout:完成整个阶段所需的时间(以秒为单位)(握手,读取所有请求标头或正文)。值为0表示没有限制
  • 禁用虚拟主机模块 handshake=0 header=0 body=0:这将 mod_reqtimeout 完全禁用(请注意, handshake=0这已经是默认设置,可以省略)。
  • 接收数据时增加的超时值 stage=timeout, MinRate=data_rate:与上面相同,但是无论何时接收到数据,超时值都会根据指定的最小数据速率(以字节/秒为单位)增加。
  • 接收数据时增加的超时值,上限 stage=timeout-maxtimeout, MinRate=data_rate:与上面相同,但是超时不会增加到指定超时范围的第二个值以上。

Apache 2.4 模块 mod_reqtimeout

nginx
请求处理数量

nginx 最大请求处理量被限制为 worker_processes \(\times\) worker_connections

worker_processes  1; # 进程数量

events {
    worker_connections  1024;
    multi_accept on;  #设置一个进程是否同时接受多个网络连接,默认为off
}
超时

client_header_timeout

  • 语法client_header_timeout time
  • 默认值\(60\)s
  • 上下文http server
  • 说明: 指定等待client发送一个请求头的超时时间(例如:GET / HTTP/1.1)。仅当在一次read中,没有收到请求头,才会算成超时。如果在超时时间内,client没发送任何东西,nginx返回HTTP状态码 408 (Request timed out)

client_body_timeout

  • 语法client_body_timeout time
  • 默认值\(60\)s
  • 上下文http server
  • 说明: 该指令设置请求体(request body)的读超时时间。仅当在一次 read 中,没有得到请求体,就会设为超时。超时后,nginx返回HTTP状态码 408 (Request timed out)

keepalive_timeout

  • 语法keepalive_timeout timeout [ header_timeout ]

  • 默认值\(75\)s

  • 上下文http server location

  • 说明: 第一个参数指定了与client的keep-alive连接超时时间。服务器将会在这个时间后关闭连接。可选的第二个参数指定在响应头 Keep-Alive: timeout=time 中的time值。这个头能够让一些浏览器主动关闭连接,这样服务器就不必要去关闭连接了。没有这个参数,nginx不会发送Keep-Alive响应头(尽管并不是由这个头来决定连接是否“keep-alive”)。两个参数的值可并不相同。

    注意不同浏览器怎么处理“keep-alive”头:

    • MSIE和Opera忽略掉"Keep-Alive: timeout=" header.

    • MSIE保持连接大约60-65秒,然后发送TCP RST

    • Opera永久保持长连接

    • Mozilla keeps the connection alive for N plus about 1-10 seconds.

    • Konqueror保持长连接N秒

    这个其实与 Slow DoS 没关系,keepalive 是在一次请求之后,不会关闭TCP连接,之后会复用。而 Slow Dos 只是发生在一次请求之中的攻击。

对服务器的测试

apache httpd

Content-length\(10000\),每次最多发送 \(68\) 字节的 Body,每 \(5\) 秒发送一次 Body 数据,对 httpd 服务(:8080,prefork 默认配置)进行测试:

slowhttptest -c 1024 -r 1024 -s 10000 -i 5 -u http://127.0.0.1:80

大概 \(20\)s (apache httpd 默认超时时间 \(20\) s)后,httpd 会响应 \(408\) ,少部分为 \(200\)

image-20220414234941460

http 错误日志中也会有 timeout 错误:

[Thu Apr 14 23:41:49.134734 2022] [reqtimeout:info] [pid 35915] [client 127.0.0.1:38180] AH01382: Request header read timeout
[Thu Apr 14 23:41:49.134760 2022] [core:debug] [pid 35915] protocol.c(1479): [client 127.0.0.1:38180] AH00567: request failed: error reading the headers, referer: TESTING_PURPOSES_ONLY

image-20220414235802877

从结果上可以看出,在 \(108\) 秒内,apache httpd 就关闭了全部的 \(1024\) 个请求。也就是说 apache httpd 不会无限地去等待客户端把完整报文发送过来

显示还有24个 connected 是因为没更新输出,通过 -g 将结果输出到文件中可以看到全部都被 closed 了。

虽然不会无限等待,但是根据默认配置会,等待 20 s!

中间用浏览器访问很难得到响应:

image-20220415004347373

nginx

注释掉 nginx 配置中的 proxy_pass,并测试:

➜  slowhttptest -c 1024 -r 1024 -s 10000 -i 5 -u http://127.0.0.1:80

大概一分钟之后(nginx 默认超时时间为 \(60\) s),nginx开始输出日志,即返回 \(408\)

image-20220415012516532

120 秒后,1024个连接全部中断:

image-20220415001151348

nginx 反向代理

对 nginx (:808 processes \(\times\) 2048 connections)反向代理的 httpd(:8080,prefork 默认配置)进行测试:

➜  slowhttptest -c 1024 -r 1024 -s 10000 -i 5 -u http://127.0.0.1:80

image-20220415000432670

情况与 nginx 测试类似。

httpd 服务的日志中,中间基本上都是 \(408\),最后的十几个响应为 \(200\)

image-20220415000728246

image-20220415000745123

在以上三个测试中,httpd 与 nginx 服务器都会去断开超时的连接。

Slow Post

慢速POST请求攻击也是一种针对Web服务器的慢速HTTP攻击,由安全研究人员在2010年提出,与Slowloris攻击不同的是,慢速POST请求攻击利用缓慢发送HTTP BODY的方式达到占用并耗尽Web服务器连接资源的目的。

在HTTP头部信息中,可以使用 Content-Length 字段来指定HTTP消息实体的传输长度,当Web服务器接收到的请求头部中含有 Content-Length 字段时,服务器会将该字段的值作为HTTP BODY的长度,持续接收数据并在达到 Content-Length 值时对HTTP BODY的数据内容进行处理,利用这个特性,攻击者能够长时间与Web服务器保持连接,并逐渐耗尽Web服务器的连接资源。

攻击者在发送HTTP POST请求时,在请求头部中将 Content-Length 设置为一个很大的值,并将HTTP BODY以非常缓慢的速度一个字节一个字节的向Web服务器发送,这样,Web服务器就需要一直维持与客户端的连接并等待数据传输结束,攻击者就可以长时间占用这个连接,通过间断性的发送单字节的HTTP BODY内容,攻击者就能够确保连接不因超时而导致中断,如果攻击者利用大量的受控主机发送这种缓慢的HTTP POST请求并持续占用这些连接,就会耗尽Web服务器的连接资源,导致其他用户的HTTP请求无法被处理,造成拒绝服务。

apache httpd

与 Slowloris 测试类似,通过添加参数 -B 来慢发送 Http Request Body,结果几乎与 Slowloris 测试相同。

nginx

注释掉 nginx 配置中的 proxy_pass,并测试:

➜ slowhttptest -c 1024 -r 1024 -s 10000 -i 5 -u http://127.0.0.1:80 -B

image-20220415013443369

\(45\)s 将所有的连接关闭,但是原因是 nginx 不允许使用 POST 方式获取数据,会返回 \(405\) (Method Not Allowed)🤔

image-20220415013426545

nginx 反向代理

对 nginx (:808 processes \(\times\) 2048 connections)反向代理的 httpd(:8080,prefork 默认配置)进行测试:

➜ slowhttptest -c 1024 -r 1024 -s 10000 -i 5 -u http://127.0.0.1:80 -B

image-20220415014158293

发现 nginx 反向代理 apache httpd 的情况下,Slow Post 使得全部保持连接,最终测试超时。

再试试 1 个请求时的情形:

➜  slowhttptest -c 1 -r 1 -s 10000 -i 5 -u http://127.0.0.1:80 -B

依然没有断开连接:

image-20220415015021352

通过修改 nginx 配置,使各个 timeout 为比较小的值(其实 keeplive_timeout 可以不用设置,就只是一次 HTTP 请求):

image-20220415020106446

➜  slowhttptest -c 1024 -r 1024 -s 10000 -i 5 -u http://127.0.0.1:80 -B

image-20220415020407575

nginx 关闭全部连接。

防御 Slow Dos

利用第三方平台

例如 Cloudflare 会先缓存所有到达的 request

Cloudflare buffers incoming requests before starting to send anything to the origin server. As a result, “low and slow” attack traffic like Slowloris attacks never reach the intended target. Learn more about how Cloudflare's DDoS protection stops slowloris attacks.

适当优化服务器配置

适当的配置服务器的最大进程/线程以及最大处理处理数量。

不过该方法还是可能会被攻击者制造的大量伪请求占用过多线程,且开辟大量线程/经常也会对其调度造成影响。

也可以对服务器 timeout 参数进行配置,但是本身服务器很难判断是 slowloris 还是真的网速慢,这个值需要根据具体的场景来把握。

对ip进行统计并屏蔽

在服务器层面或者服务器以下层对IP进行统计与屏蔽。因为要造成 slowloris,最起码双方已完成 TCP 连接,所以攻击者提供了真实的IP(不一定是攻击者本身的IP,可能利用了代理),所以需要在这些层对IP进行统计,并进行处理。


参考

posted @ 2022-04-14 23:25  NIShoushun  阅读(266)  评论(0编辑  收藏  举报