DDoS al rescate: cómo realizamos pruebas de estrés y carga

DDoS al rescate: cómo realizamos pruebas de estrés y carga

Variti desarrolla protección contra bots y ataques DDoS, y también realiza pruebas de carga y estrés. En la conferencia HighLoad++ 2018 hablamos sobre cómo proteger los recursos de varios tipos de ataques. En resumen: aísle partes del sistema, utilice servicios en la nube y CDN y actualice periódicamente. Pero aún así no podrás gestionar la protección sin empresas especializadas :)

Antes de leer el texto, puedes leer los resúmenes cortos. en el sitio web de la conferencia.
Y si no te gusta leer o simplemente quieres ver el vídeo, la grabación de nuestro informe se encuentra debajo del spoiler.

Grabación en vídeo del informe.

Muchas empresas ya saben cómo realizar pruebas de carga, pero no todas realizan pruebas de estrés. Algunos de nuestros clientes piensan que su sitio es invulnerable porque tienen un sistema de alta carga y protege bien contra ataques. Mostramos que esto no es del todo cierto.
Por supuesto, antes de realizar pruebas obtenemos el permiso del cliente, firmado y sellado, y con nuestra ayuda no se puede realizar un ataque DDoS a nadie. Las pruebas se realizan en el momento elegido por el cliente, cuando el tráfico a su recurso es mínimo y los problemas de acceso no afectarán a los clientes. Además, como siempre puede salir algo mal durante el proceso de prueba, tenemos un contacto constante con el cliente. Esto le permite no solo informar los resultados obtenidos, sino también cambiar algo durante las pruebas. Al finalizar las pruebas, siempre elaboramos un informe en el que señalamos las deficiencias detectadas y damos recomendaciones para eliminar las debilidades del sitio.

Como estamos trabajando

Al realizar las pruebas, emulamos una botnet. Dado que trabajamos con clientes que no están ubicados en nuestras redes, para garantizar que la prueba no finalice en el primer minuto debido a la activación de límites o protección, suministramos la carga no desde una IP, sino desde nuestra propia subred. Además, para crear una carga significativa, tenemos nuestro propio servidor de prueba bastante potente.

Postulados

Demasiado no significa bueno
Cuanta menos carga podamos llevar a un recurso al fracaso, mejor. Si puede hacer que el sitio deje de funcionar con una solicitud por segundo, o incluso una solicitud por minuto, eso es genial. Porque según la ley de la mezquindad, los usuarios o atacantes caerán accidentalmente en esta vulnerabilidad particular.

El fracaso parcial es mejor que el fracaso total
Siempre aconsejamos hacer sistemas heterogéneos. Además, vale la pena separarlos a nivel físico, y no solo por contenedorización. En el caso de la separación física, incluso si algo falla en el sitio, existe una alta probabilidad de que no deje de funcionar por completo y los usuarios seguirán teniendo acceso a al menos parte de la funcionalidad.

La buena arquitectura es la base de la sostenibilidad
La tolerancia a fallas de un recurso y su capacidad para resistir ataques y cargas deben establecerse en la etapa de diseño, de hecho, en la etapa de dibujar los primeros diagramas de flujo en un cuaderno. Porque si aparecen errores fatales, es posible corregirlos en el futuro, pero es muy difícil.

No sólo el código debe ser bueno, sino también la configuración.
Mucha gente piensa que un buen equipo de desarrollo es garantía de un servicio tolerante a fallos. Un buen equipo de desarrollo es realmente necesario, pero también debe haber buenas operaciones, buenos DevOps. Es decir, necesitamos especialistas que configuren correctamente Linux y la red, escriban configuraciones correctamente en nginx, establezcan límites, etc. De lo contrario, el recurso funcionará bien sólo en pruebas y, en algún momento, todo se estropeará en producción.

Diferencias entre pruebas de carga y estrés.
Las pruebas de carga le permiten identificar los límites del funcionamiento del sistema. Las pruebas de estrés tienen como objetivo encontrar debilidades en un sistema y se utilizan para romper este sistema y ver cómo se comportará en el proceso de falla de ciertas partes. En este caso, el cliente suele desconocer la naturaleza de la carga antes de que comiencen las pruebas de estrés.

Rasgos distintivos de los ataques L7.

Normalmente dividimos los tipos de carga en cargas en los niveles L7 y L3 y 4. L7 es una carga a nivel de aplicación, la mayoría de las veces significa solo HTTP, pero nos referimos a cualquier carga a nivel de protocolo TCP.
Los ataques L7 tienen ciertas características distintivas. En primer lugar, llegan directamente a la aplicación, es decir, es poco probable que se reflejen a través de la red. Estos ataques utilizan la lógica y, debido a esto, consumen CPU, memoria, disco, base de datos y otros recursos de manera muy eficiente y con poco tráfico.

Inundación HTTP

En el caso de cualquier ataque, la carga es más fácil de crear que de manejar, y en el caso de L7 esto también es cierto. No siempre es fácil distinguir el tráfico de ataque del tráfico legítimo y, en la mayoría de los casos, esto se puede hacer por frecuencia, pero si todo se planifica correctamente, es imposible entender en los registros dónde está el ataque y dónde están las solicitudes legítimas.
Como primer ejemplo, consideremos un ataque de inundación HTTP. El gráfico muestra que este tipo de ataques suelen ser muy potentes; en el siguiente ejemplo, el número máximo de solicitudes superó las 600 mil por minuto.

DDoS al rescate: cómo realizamos pruebas de estrés y carga

HTTP Flood es la forma más sencilla de crear carga. Normalmente, se necesita algún tipo de herramienta de prueba de carga, como ApacheBench, y establece una solicitud y un objetivo. Con un enfoque tan simple, existe una alta probabilidad de encontrarse con el almacenamiento en caché del servidor, pero es fácil evitarlo. Por ejemplo, agregar cadenas aleatorias a la solicitud, lo que obligará al servidor a servir constantemente una página nueva.
Además, no se olvide del agente de usuario en el proceso de creación de una carga. Los administradores del sistema filtran muchos agentes de usuario de herramientas de prueba populares y, en este caso, es posible que la carga simplemente no llegue al backend. Puede mejorar significativamente el resultado insertando un encabezado más o menos válido del navegador en la solicitud.
Por más simples que sean los ataques HTTP Flood, también tienen sus inconvenientes. En primer lugar, se requieren grandes cantidades de energía para crear la carga. En segundo lugar, estos ataques son muy fáciles de detectar, especialmente si provienen de una dirección. Como resultado, las solicitudes comienzan a ser filtradas inmediatamente por los administradores del sistema o incluso a nivel de proveedor.

Que buscar

Para reducir la cantidad de solicitudes por segundo sin perder eficiencia, es necesario mostrar un poco de imaginación y explorar el sitio. De este modo, puede cargar no sólo el canal o el servidor, sino también partes individuales de la aplicación, por ejemplo, bases de datos o sistemas de archivos. También puedes buscar lugares en el sitio que hagan cálculos grandes: calculadoras, páginas de selección de productos, etc. Finalmente, sucede a menudo que el sitio tiene algún tipo de script PHP que genera una página de varios cientos de miles de líneas. Un script de este tipo también carga significativamente el servidor y puede convertirse en el objetivo de un ataque.

Donde mirar

Cuando escaneamos un recurso antes de probarlo, primero miramos, por supuesto, el sitio mismo. Buscamos todo tipo de campos de entrada, archivos pesados, en general, todo lo que pueda crear problemas al recurso y ralentizar su funcionamiento. Las herramientas de desarrollo banales en Google Chrome y Firefox ayudan aquí, mostrando los tiempos de respuesta de la página.
También escaneamos subdominios. Por ejemplo, hay una determinada tienda en línea, abc.com, y tiene un subdominio admin.abc.com. Lo más probable es que se trate de un panel de administración con autorización, pero si lo carga, puede crear problemas para el recurso principal.
El sitio puede tener un subdominio api.abc.com. Lo más probable es que se trate de un recurso para aplicaciones móviles. La aplicación se puede encontrar en App Store o Google Play, instalar un punto de acceso especial, analizar la API y registrar cuentas de prueba. El problema es que la gente suele pensar que cualquier cosa que esté protegida por autorización es inmune a los ataques de denegación de servicio. Supuestamente, la autorización es el mejor CAPTCHA, pero no lo es. Es fácil crear entre 10 y 20 cuentas de prueba, pero al crearlas, obtenemos acceso a funciones complejas y manifiestas.
Naturalmente, miramos el historial, robots.txt y WebArchive, ViewDNS y buscamos versiones antiguas del recurso. A veces sucede que los desarrolladores han implementado, digamos, mail2.yandex.net, pero la versión anterior, mail.yandex.net, permanece. Este mail.yandex.net ya no es compatible, no se le asignan recursos de desarrollo, pero continúa consumiendo la base de datos. En consecuencia, al utilizar la versión anterior, puede utilizar de manera efectiva los recursos del backend y todo lo que hay detrás del diseño. Por supuesto, esto no siempre sucede, pero todavía nos encontramos con esto con bastante frecuencia.
Naturalmente, analizamos todos los parámetros de la solicitud y la estructura de las cookies. Puede, por ejemplo, volcar algún valor en una matriz JSON dentro de una cookie, crear muchos anidamientos y hacer que el recurso funcione durante un tiempo excesivamente largo.

Carga de búsqueda

Lo primero que me viene a la mente al investigar un sitio es cargar la base de datos, ya que casi todo el mundo tiene una búsqueda y, lamentablemente, para casi todo el mundo está mal protegida. Por alguna razón, los desarrolladores no prestan suficiente atención a la búsqueda. Pero aquí hay una recomendación: no debe realizar solicitudes del mismo tipo, porque puede encontrar almacenamiento en caché, como es el caso de la inundación HTTP.
Realizar consultas aleatorias a la base de datos tampoco siempre es eficaz. Es mucho mejor crear una lista de palabras clave que sean relevantes para la búsqueda. Si volvemos al ejemplo de una tienda online: digamos que el sitio vende neumáticos para automóviles y permite configurar el radio de los neumáticos, el tipo de coche y otros parámetros. En consecuencia, las combinaciones de palabras relevantes obligarán a la base de datos a funcionar en condiciones mucho más complejas.
Además, vale la pena utilizar la paginación: es mucho más difícil para una búsqueda devolver la penúltima página de los resultados de la búsqueda que la primera. Es decir, con la ayuda de la paginación puedes diversificar ligeramente la carga.
El siguiente ejemplo muestra la carga de búsqueda. Se puede ver que desde el primer segundo de la prueba a una velocidad de diez solicitudes por segundo, el sitio se cayó y no respondió.

DDoS al rescate: cómo realizamos pruebas de estrés y carga

¿Si no hay búsqueda?

Si no hay búsqueda, esto no significa que el sitio no contenga otros campos de entrada vulnerables. Este campo puede ser autorización. Hoy en día, a los desarrolladores les gusta crear hashes complejos para proteger la base de datos de inicio de sesión de un ataque a la tabla Rainbow. Esto es bueno, pero esos hashes consumen muchos recursos de la CPU. Un gran flujo de autorizaciones falsas provoca una falla del procesador y, como resultado, el sitio deja de funcionar.
La presencia en el sitio de todo tipo de formularios para comentarios y opiniones es un motivo para enviar allí textos muy extensos o simplemente crear una avalancha masiva. A veces los sitios aceptan archivos adjuntos, incluso en formato gzip. En este caso, tomamos un archivo de 1 TB, lo comprimimos en varios bytes o kilobytes usando gzip y lo enviamos al sitio. Luego se descomprime y se obtiene un efecto muy interesante.

API de descanso

Me gustaría prestar un poco de atención a servicios tan populares como Rest API. Proteger una API Rest es mucho más difícil que un sitio web normal. Incluso los métodos triviales de protección contra la fuerza bruta de contraseñas y otras actividades ilegítimas no funcionan para la API Rest.
La API Rest es muy fácil de romper porque accede directamente a la base de datos. Al mismo tiempo, el fracaso de dicho servicio conlleva consecuencias bastante graves para las empresas. El hecho es que la API Rest se suele utilizar no sólo para el sitio web principal, sino también para la aplicación móvil y algunos recursos comerciales internos. Y si todo esto falla, el efecto será mucho más fuerte que en el caso de un simple fallo de un sitio web.

Cargando contenido pesado

Si nos ofrecen probar alguna aplicación normal de una sola página, página de destino o sitio web de tarjeta de presentación que no tenga una funcionalidad compleja, buscamos contenido pesado. Por ejemplo, imágenes grandes que envía el servidor, archivos binarios, documentación en PDF: intentamos descargar todo esto. Estas pruebas cargan bien el sistema de archivos y obstruyen los canales y, por lo tanto, son efectivas. Es decir, incluso si no apaga el servidor y descarga un archivo grande a baja velocidad, simplemente obstruirá el canal del servidor de destino y luego se producirá una denegación de servicio.
Un ejemplo de una prueba de este tipo muestra que a una velocidad de 30 RPS el sitio dejó de responder o produjo errores de servidor número 500.

DDoS al rescate: cómo realizamos pruebas de estrés y carga

No te olvides de configurar servidores. A menudo puede encontrar que una persona compró una máquina virtual, instaló Apache allí, configuró todo de forma predeterminada, instaló una aplicación PHP y a continuación puede ver el resultado.

DDoS al rescate: cómo realizamos pruebas de estrés y carga

Aquí la carga llegó a la raíz y ascendió a solo 10 RPS. Esperamos 5 minutos y el servidor falló. Es cierto que no se sabe del todo por qué se cayó, pero se supone que simplemente tenía demasiada memoria y por eso dejó de responder.

Basado en olas

En los últimos dos años, los ataques con oleadas se han vuelto bastante populares. Esto se debe al hecho de que muchas organizaciones compran ciertas piezas de hardware para la protección DDoS, que requieren una cierta cantidad de tiempo para acumular estadísticas para comenzar a filtrar el ataque. Es decir, no filtran el ataque en los primeros 30-40 segundos, porque acumulan datos y aprenden. En consecuencia, en estos 30-40 segundos puede iniciar tanto en el sitio que el recurso permanecerá durante mucho tiempo hasta que se aclaren todas las solicitudes.
En el caso del ataque a continuación, hubo un intervalo de 10 minutos, después del cual llegó una nueva parte modificada del ataque.

DDoS al rescate: cómo realizamos pruebas de estrés y carga

Es decir, la defensa aprendió, empezó a filtrar, pero llegó una parte nueva, completamente diferente del ataque, y la defensa empezó a aprender de nuevo. De hecho, el filtrado deja de funcionar, la protección se vuelve ineficaz y el sitio no está disponible.
Los ataques de oleada se caracterizan por valores muy altos en el pico, pueden llegar a cien mil o un millón de solicitudes por segundo, en el caso de L7. Si hablamos de L3 y 4, entonces puede haber cientos de gigabits de tráfico o, en consecuencia, cientos de mpps, si contamos en paquetes.
El problema con este tipo de ataques es la sincronización. Los ataques provienen de una botnet y requieren un alto grado de sincronización para crear un pico muy grande y único. Y esta coordinación no siempre funciona: a veces la salida es una especie de pico parabólico, lo que parece bastante patético.

No solo HTTP

Además de HTTP en L7, nos gusta explotar otros protocolos. Como regla general, en un sitio web normal, especialmente en un hosting normal, destacan los protocolos de correo y MySQL. Los protocolos de correo están sujetos a menos carga que las bases de datos, pero también pueden cargarse de manera bastante eficiente y terminar con una CPU sobrecargada en el servidor.
Tuvimos bastante éxito al utilizar la vulnerabilidad SSH de 2016. Ahora esta vulnerabilidad se ha solucionado para casi todos, pero esto no significa que la carga no se pueda enviar a SSH. Poder. Simplemente hay una gran carga de autorizaciones, SSH consume casi toda la CPU del servidor y luego el sitio web colapsa debido a una o dos solicitudes por segundo. En consecuencia, estas una o dos solicitudes basadas en los registros no se pueden distinguir de una carga legítima.
Muchas conexiones que abrimos en servidores también siguen siendo relevantes. Anteriormente, Apache era culpable de esto, ahora nginx realmente sufre, ya que a menudo está configurado de forma predeterminada. La cantidad de conexiones que nginx puede mantener abiertas es limitada, por lo que abrimos esta cantidad de conexiones, nginx ya no acepta una nueva conexión y, como resultado, el sitio no funciona.
Nuestro grupo de prueba tiene suficiente CPU para atacar el protocolo de enlace SSL. En principio, como muestra la práctica, a veces a las botnets también les gusta hacer esto. Por un lado, está claro que no se puede prescindir de SSL, porque los resultados de Google, el ranking y la seguridad. Por otro lado, SSL lamentablemente tiene un problema de CPU.

L3 y 4

Cuando hablamos de un ataque en los niveles L3 y 4, normalmente hablamos de un ataque en el nivel de enlace. Una carga de este tipo casi siempre se distingue de una legítima, a menos que se trate de un ataque de inundación SYN. El problema de los ataques SYN-flood a las herramientas de seguridad es su gran volumen. El valor máximo de L3 y 4 fue de 1,5 a 2 Tbit/s. Este tipo de tráfico es muy difícil de procesar incluso para grandes empresas, incluidas Oracle y Google.
SYN y SYN-ACK son paquetes que se utilizan al establecer una conexión. Por lo tanto, es difícil distinguir SYN-flood de una carga legítima: no está claro si se trata de un SYN que vino a establecer una conexión o parte de una inundación.

UDP-inundación

Normalmente, los atacantes no tienen las capacidades que tenemos nosotros, por lo que se puede utilizar la amplificación para organizar ataques. Es decir, el atacante escanea Internet y encuentra servidores vulnerables o configurados incorrectamente que, por ejemplo, en respuesta a un paquete SYN, responden con tres SYN-ACK. Al falsificar la dirección de origen a partir de la dirección del servidor de destino, es posible aumentar la potencia, digamos, tres veces con un solo paquete y redirigir el tráfico a la víctima.

DDoS al rescate: cómo realizamos pruebas de estrés y carga

El problema de las amplificaciones es que son difíciles de detectar. Ejemplos recientes incluyen el sensacional caso del vulnerable memcached. Además, ahora hay muchos dispositivos IoT, cámaras IP, que en su mayoría también están configurados de forma predeterminada, pero de forma predeterminada están configurados incorrectamente, razón por la cual los atacantes suelen realizar ataques a través de dichos dispositivos.

DDoS al rescate: cómo realizamos pruebas de estrés y carga

Difícil inundación SYN

SYN-flood es probablemente el tipo de ataque más interesante desde el punto de vista de un desarrollador. El problema es que los administradores de sistemas suelen utilizar el bloqueo de IP como protección. Además, el bloqueo de IP afecta no sólo a los administradores de sistemas que actúan mediante scripts, sino también, lamentablemente, a algunos sistemas de seguridad que se compran por mucho dinero.
Este método puede convertirse en un desastre, porque si los atacantes reemplazan las direcciones IP, la empresa bloqueará su propia subred. Cuando el Firewall bloquea su propio clúster, la salida fallará en las interacciones externas y el recurso fallará.
Además, no es difícil bloquear tu propia red. Si la oficina del cliente tiene una red Wi-Fi, o si el rendimiento de los recursos se mide mediante varios sistemas de monitoreo, entonces tomamos la dirección IP de este sistema de monitoreo o la red Wi-Fi de la oficina del cliente y la usamos como fuente. Al final, el recurso parece estar disponible, pero las direcciones IP de destino están bloqueadas. Así, la red Wi-Fi del congreso HighLoad, donde se presenta el nuevo producto de la compañía, puede quedar bloqueada, lo que conlleva ciertos costes comerciales y económicos.
Durante las pruebas, no podemos usar la amplificación a través de Memcached con ningún recurso externo, porque existen acuerdos para enviar tráfico solo a direcciones IP permitidas. En consecuencia, utilizamos amplificación a través de SYN y SYN-ACK, cuando el sistema responde al envío de un SYN con dos o tres SYN-ACK, y en la salida el ataque se multiplica por dos o tres veces.

Instrumentos

Una de las principales herramientas que utilizamos para la carga de trabajo L7 es Yandex-tank. En particular, se utiliza un fantasma como arma, además hay varios scripts para generar cartuchos y analizar los resultados.
Tcpdump se utiliza para analizar el tráfico de la red y Nmap se utiliza para analizar el servidor. Para crear la carga en el nivel L3 y 4, se utiliza OpenSSL y un poco de nuestra propia magia con la biblioteca DPDK. DPDK es una biblioteca de Intel que le permite trabajar con la interfaz de red sin pasar por la pila de Linux, aumentando así la eficiencia. Naturalmente, utilizamos DPDK no solo en los niveles L3 y 4, sino también en el nivel L7, porque nos permite crear un flujo de carga muy alto, dentro del rango de varios millones de solicitudes por segundo desde una máquina.
También utilizamos ciertos generadores de tráfico y herramientas especiales que escribimos para pruebas específicas. Si recordamos la vulnerabilidad en SSH, entonces el conjunto anterior no se puede explotar. Si atacamos el protocolo de correo, tomamos utilidades de correo o simplemente escribimos scripts en ellas.

Hallazgos

Como conclusión me gustaría decir:

  • Además de las clásicas pruebas de carga, es necesario realizar pruebas de estrés. Tenemos un ejemplo real en el que el subcontratista de un socio solo realizó pruebas de carga. Demostró que el recurso puede soportar la carga normal. Pero luego apareció una carga anormal, los visitantes del sitio comenzaron a usar el recurso de manera un poco diferente y, como resultado, el subcontratista se retiró. Por lo tanto, vale la pena buscar vulnerabilidades incluso si ya está protegido contra ataques DDoS.
  • Es necesario aislar algunas partes del sistema de otras. Si tiene una búsqueda, debe moverla a máquinas separadas, es decir, ni siquiera a Docker. Porque si la búsqueda o la autorización fallan, al menos algo seguirá funcionando. En el caso de una tienda online, los usuarios seguirán buscando productos en el catálogo, yendo desde el agregador, comprando si ya están autorizados o autorizando vía OAuth2.
  • No descuides todo tipo de servicios en la nube.
  • Utilice CDN no solo para optimizar los retrasos de la red, sino también como un medio de protección contra ataques por agotamiento del canal y simplemente inundación de tráfico estático.
  • Es necesario utilizar servicios de protección especializados. No puedes protegerte de los ataques L3 y 4 a nivel de canal, porque lo más probable es que simplemente no tengas un canal suficiente. Tampoco es probable que puedas defenderte de los ataques L7, ya que pueden ser muy grandes. Además, la búsqueda de pequeños ataques sigue siendo prerrogativa de servicios especiales, algoritmos especiales.
  • Actualízate periódicamente. Esto se aplica no sólo al kernel, sino también al demonio SSH, especialmente si los tienes abiertos al exterior. En principio, es necesario actualizar todo, porque es poco probable que usted mismo pueda rastrear ciertas vulnerabilidades.

Fuente: habr.com

Añadir un comentario