IPTV Pentest Lab

Timeline de Sesiones

Cronologia completa de las 13 sesiones de pentest — desde el descubrimiento inicial hasta HTTP Request Smuggling

Timeline de Sesiones

Evolucion del Pentest


Detalle de Sesiones Clave

Sesiones 1-2: Descubrimiento y Primer Contacto

Empezamos con recon basico: nmap full port scan, HTTP banner grabbing, Xtream API fingerprinting. Construimos la primera version de la herramienta CLI (iptv-recon) con commander, structured logging, y Result pattern.

Descubrimientos clave:

  • Stalker Portal v5.3.1 confirmado (/c/version.js)
  • Routing dual framework identificado (Silex 405 vs Laravel 404)
  • 51 rutas admin Silex encontradas — todas devolviendo 405
  • STB API auth bypass: get_profile devuelve 75+ campos sin autenticacion
  • Ruta _fragment RCE existe pero bloqueada por la paradoja de routing
  • 30+ tecnicas de bypass intentadas contra el front controller — todas fallaron
  • Testing SSRF en 12 endpoints — todos negativos (timing analysis confirma sin fetch)
  • SQLi en series_id resulta ser PHP type crash, no SQL injection

Sesion 3: Mapeo de Infraestructura

Full port scan revelo infraestructura multi-puerto: :53 DNS, :4499 RTMP, :5060 SIP, :31210 internal API, :65432 SSH. Descubierto Nodo 141 con nginx 1.21.2 mas viejo. Token delivery probado en 8 canales — todos rechazados.

Construimos scripts de exploit: stalker-exploit.ts (Check Point 2019 chain), confirm-ssrf.ts, fragment-exploit.ts.

Sesion 4: Brute Force y DNS

SSH brute con 2700 combinaciones — cancelado al 32% sin exito. DNS zone transfer (AXFR) denegado en los 4 dominios a traves de 2 servidores. Reverse DNS revelo ns31681901.ip-141-94-3.eu. Puerto 4499 identificado como RTMP.

Sesion 5: Nace "La Cupula de Cristal"

Agotamos los vectores HTTP y le pusimos nombre al patron. Tambien encontramos:

  • Laravel Ignition instalado pero POST -> 405
  • RTMP solo tiene app /live con auth callback

Conclusion estrategica: Solo protocolos no-HTTP o ataques a nivel de protocolo pueden funcionar.

Sesion 6: Tercer Servidor Descubierto

Un descubrimiento importante: HEAD vs GET devuelve resultados diferentes:

  • HEAD /live/... -> 200 Content-Length:0 (sin redirect)
  • GET /live/... -> 302 redirect al nodo 135

Esto revelo el tercer servidor (XXX.XXX.XXX.XXX) especializado en live streaming.

DNS recursive resolver descubierto en el servidor principal. Probamos h2c upgrade, WebSocket, CONNECT tunneling — todos cerrados.

Sesion 7: Fingerprint Completo Nodo 135

Port scan y dome-fingerprint del Nodo 135. Confirmada cupula identica en los 3 nodos. Player API leak muestra los mismos 22K+ canales. Comparativa de tres nodos completada. Todos los nodos comparten la misma base de datos, versiones de SSH, y patron de deploy.

Sesion 8: Deep Dive de Protocolos

RTMP PUBLISH en :4499 — notify callback llega a Stalker API pero no hace outbound HTTP fetch (timing analysis confirma sin SSRF). Path traversal en stream names: I/O error o "operation not permitted", nunca SSRF.

SIP :5060 — sin respuesta en TCP (3 nodos), sin respuesta en UDP (svmap). Puerto tcpwrapped — probablemente solo interno.

_method bypass revelo la mecanica exacta del routing dual: decision de routing hecha desde el metodo HTTP raw antes de procesar _method. Usar _method=GET en body POST escapa a Laravel — pero Laravel no tiene rutas admin.

Sesion 9: STB API Deep Dive y Descubrimiento de MAC

Encontramos MAC real (F5:57:59:66:90:AB) de una cuenta registrada de Apple TV. User profile revela blocked=1, passwords 0000, timezone Europe/Brussels. Rate limiting cross-node confirmado (DB compartida). CORS completamente abierto: Access-Control-Allow-Origin: *.

Descubiertos dos sistemas de auth independientes:

  • STB API (load.php): MAC cookie + token (usuario bloqueado)
  • Xtream API (player_api.php): username/password (funciona, 22K canales)

Sesion 10: Ultimas Aristas

SSH user enumeration cerrada (CVE-2018-15473 parchado). set_settings confirmado read-only (8 nombres de action, 3 formatos de body). 4 acciones STB candidatas a SSRF — todas {"js":[]}. SQLi basada en MAC cerrada (prepared statements). Type juggling, PHP CVEs (6 evaluadas), XXE — todo cerrado. APP_DEBUG=false confirmado.

Sesion 11: RoadRunner y Cadena VOD

Testeo de stream real MP4/MKV desde Nodo 141 -> 302 redirect a main con tokens cifrados de un solo uso. Accept-Ranges con formato no estandar confirma RoadRunner (servidor de aplicaciones PHP basado en Go) en el stack — no es nginx mp4_module. CVEs de MP4 definitivamente cerradas.

Stack actualizado: nginx -> RoadRunner -> PHP (Silex+Laravel) -> MySQL

Rate limit bypass: ban solo de POST — GET handshake funciona durante cooldown.

Sesion 12: Check Point Chain y Portal.php

Check Point 2019: auth bypass a type=vclub funciona (no requiere auth), pero las tablas vclub estan vacias. SQLi via sortby no ejecuta en tablas vacias. type=vod/itv/series tiene datos (22K+) pero requiere is_authenticated=true. Vulnerabilidad existe en codigo pero es inexplotable en esta instancia.

portal.php: punto de entrada alternativo en root — mismo backend, misma cupula. Path manipulation exhaustivo (double slashes, encoded slashes, null bytes, CRLF, dot segments) — todo normalizado por nginx. 16+ puntos de entrada PHP alternativos — ninguno existe. 12 tipos STB no documentados — todos ignorados.

Sesion 13 (Actual): HTTP Request Smuggling

Target: CVE-2025-22871 (CVSS 9.1): Go net/http acepta bare LF (\n) en lineas chunk-size de chunked encoding. Como RoadRunner esta basado en Go y se situa detras de nginx, un diferencial de parsing podria permitir request smuggling para bypasear protecciones de nginx y alcanzar rutas internas (/_fragment, /adm/login).

Resultado: nginx y RoadRunner ambos aceptan bare LF — sin diferencial de parsing, sin smuggling. Bare CR en chunk-ext genera 400 en nginx 1.26.2, TIMEOUT en nginx 1.21.2 (ambiguo pero no explotable).

Cupula intacta.