La Cupula de Cristal
El descubrimiento central del pentest — un routing dual Silex/Laravel que bloquea 30+ vectores de ataque confirmados
El Descubrimiento de La Cupula
Que es La Cupula?
En las primeras sesiones vi algo raro: todas las rutas del panel admin existian, pero ninguna era accesible. Las respuestas cambiaban segun el metodo HTTP:
# Test 1: GET request a ruta admin
$ curl -sS "http://XXX.XXX.XXX.XXX:8080/stalker_portal/server/adm/login" | wc -c
9
# Response body: "Not Found"
# Test 2: POST request a la MISMA ruta
$ curl -sS -X POST "http://XXX.XXX.XXX.XXX:8080/stalker_portal/server/adm/login" | wc -c
835
# Response body: HTML error de Silex "Method Not Allowed"Que esta pasando aqui? La misma ruta existe (/adm/login) pero responde diferente segun el metodo HTTP. Esto no es normal.
La Metafora de La Cupula de Cristal
Imagina que eres un ladron entrando a un museo. Ves una sala con una caja fuerte expuesta en el centro. La caja esta abierta — puedes ver claramente los diamantes dentro. Pero cuando intentas alcanzarla...
El problema:
| Si usas GET | Si usas POST |
|---|---|
| Llegas a Laravel | Llegas a Silex (el vulnerable) |
| Pero Laravel no tiene las rutas admin | Pero Silex solo acepta GET en esas rutas |
| Resultado: 404 Not Found | Resultado: 405 Method Not Allowed |
Ambos caminos son callejones sin salida. Es como si el arquitecto hubiera disenado dos pasillos que nunca se cruzan, con la caja fuerte en el medio.
El Codigo Detras de La Cupula
El front controller PHP implementa esta logica simple pero devastadora:
// Este codigo NO es del target real - es pseudocodigo explicativo
// basado en el comportamiento observado
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !isset($_SERVER['HTTP_X_REQUESTED_WITH'])) {
// Rama Silex: el framework VULNERABLE original de Stalker Portal
// TODAS las rutas admin aqui estan definidas como $app->get()
// -> POST devuelve 405 Method Not Allowed
require_once __DIR__ . '/silex_app.php';
} else {
// Rama Laravel: el framework NUEVO sin rutas admin
// GET, POST+XHR, PUT, DELETE... todo va aqui
// -> Las rutas /adm/* no existen -> 404 Not Found
require_once __DIR__ . '/laravel_app.php';
}La decision se toma ANTES de procesar _method o cualquier otro truco. El metodo HTTP real del socket determina la rama, y ahi termina la historia.
Lo Mas Frustrante: Puertas Abiertas que No Podemos Cruzar
La parte mas dolorosa de este pentest no es que el sistema sea seguro. Es que sabemos exactamente donde estan las vulnerabilidades, las podemos ver, confirmar que existen, pero no hay forma de alcanzarlas.
Catalogo Completo de Puertas Abiertas
RCE (Ejecucion Remota de Codigo):
| Vulnerabilidad | CVE | Estado | Por que no funciona |
|---|---|---|---|
Symfony _fragment RCE | CVE-2014-4931 | Ruta existe | POST -> 405 (GET-only), GET -> 404 (Laravel) |
| Laravel Ignition RCE | CVE-2021-3129 | Instalado | POST -> 405 (Silex recibe POST), GET -> 404 |
| Check Point 2019 Chain | N/A | Auth bypass funciona | Pero tabla vclub vacia -> SQLi no ejecuta |
| ESI Sub-request Bypass | CVE-2015-4050 | Posible | Mismo problema de routing |
Debug Modes Expuestos:
| Vulnerabilidad | Estado | Por que no funciona |
|---|---|---|
Silex _profiler | Registrado (405) | GET -> 404 (Laravel), POST -> 405 |
Silex _profiler/search | Registrado | Mismo routing dual |
Silex _profiler/phpinfo | Registrado | Mismo routing dual |
Silex _wdt (Web Debug Toolbar) | Registrado | Mismo routing dual |
| Laravel Ignition health-check | Ruta existe | assets bloqueados por nginx |
Bypass de Routing Intentados:
| Vector | CVE | Estado | Resultado |
|---|---|---|---|
| CVE-2025-64500 | PATH_INFO sin slash | Cerrado | nginx no matchea como PHP |
_path= parameter | Symfony _fragment bypass | Cerrado | STB API ignora el parametro |
| HTTP Smuggling | CVE-2025-22871 | Cerrado | Sin diferencial nginx/RoadRunner |
| CL.TE / TE.CL | Smuggling clasico | Cerrado | nginx rechaza con 400 |
| Double Content-Length | Header conflict | Cerrado | nginx rechaza con 400 |
| TE obfuscation | Tab/space/obs-fold | Cerrado | 400 o 501 Not Implemented |
Infraestructura Vulnerable:
| Componente | Estado | Nota |
|---|---|---|
| nginx 1.21.2 (Nodo 141) | CVEs aplicables | CVE-2022-41741/42 MP4 memory corruption |
| RoadRunner (Go) | CVE-2025-22871 | Pero sin diferencial con nginx |
| API Interna :31210 | 403 externo | Sin auth si llegaramos internamente |
| RTMP :4499 | Notify activo | Pero no hace HTTP fetch saliente |
| DNS :53 | Recursivo | Posible DNS rebinding |
PHP CVEs Evaluadas (Todas CERRADAS - no aplican):
| CVE | CVSS | Por que NO aplica |
|---|---|---|
| CVE-2024-4577 | 9.8 | Windows CGI only, target es Linux |
| CVE-2024-1874 | 9.8 | Windows proc_open only |
| CVE-2022-31630 | 8.2 | Requiere file upload, sin vector |
| CVE-2022-37454 | 9.8 | Requiere control de input a hash() |
| CVE-2023-3823 | 7.5 | XXE: load.php no parsea XML |
| CVE-2023-3824 | 9.8 | Phar overflow: sin upload |
30+ Vectores: Resultado Final
La Sensacion en Resumen
"Estas frente a La Cupula. A traves del cristal ves todo: _fragment RCE, Ignition, Check Point, los debug modes... Se exactamente donde estan. Pero el cristal no se rompe. GET te manda a Laravel (404), POST te manda a Silex (405). Treinta vulnerabilidades confirmadas al otro lado, y ninguna al alcance."
La Tabla de Respuestas (Tu Guia de Diagnostico)
Cuando haces una peticion, el tamano de la respuesta te dice que esta pasando:
| Respuesta | Tamano | Origen | Significado |
|---|---|---|---|
404 Not Found | ~9 bytes | Laravel | La ruta no existe en Laravel |
405 Method Not Allowed | ~835 bytes (HTML) | Silex | La ruta existe pero rechaza POST |
405 Allow: GET, HEAD | ~99 bytes (JSON) | Laravel | POST+XHR llego a Laravel |
400 Bad Request | ~157 bytes | nginx | Error de parsing HTTP |
403 Forbidden | variable | nginx | Restriccion de IP o rate limit |
429 Too Many Attempts | variable | RoadRunner | Rate limiting activo |
{"message":"blip"} | variable | PHP | Ban persistente (5-15 min) |
{"js":[]} | 9 bytes | STB API | Respuesta vacia / sin datos |
Evidencia Real: Outputs de Comandos
# ═══════════════════════════════════════════════════════════════
# DETECTANDO EL DUAL FRAMEWORK
# ═══════════════════════════════════════════════════════════════
$ curl -sS -w "\n[Status: %{http_code}, Size: %{size_download}B]\n" \
"http://XXX.XXX.XXX.XXX:8080/stalker_portal/server/adm/login"
Not Found
[Status: 404, Size: 9B]
# GET -> Laravel -> 404
$ curl -sS -X POST -w "\n[Status: %{http_code}, Size: %{size_download}B]\n" \
"http://XXX.XXX.XXX.XXX:8080/stalker_portal/server/adm/login"
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Error 405</title>
<style>body { background-color: #fff; color: #333; ... }</style>
</head>
<body>
<h1>405 Method Not Allowed</h1>
<p>The requested method POST is not allowed for the URL /stalker_portal/server/adm/login.</p>
</body>
</html>
[Status: 405, Size: 835B]
# POST -> Silex -> 405 (LA RUTA EXISTE pero rechaza POST)
# ═══════════════════════════════════════════════════════════════
# 51 RUTAS SILEX DESCUBIERTAS (todas 405)
# ═══════════════════════════════════════════════════════════════
$ for route in login logout dashboard users settings _fragment; do
resp=$(curl -sS -o /dev/null -w "%{http_code}:%{size_download}" \
-X POST "http://XXX.XXX.XXX.XXX:8080/stalker_portal/server/adm/$route")
echo "$route -> $resp"
done
login -> 405:835
logout -> 405:835
dashboard -> 405:835
users -> 405:835
settings -> 405:835
_fragment -> 405:835 # <- ESTA ES LA RUTA RCE!
# ═══════════════════════════════════════════════════════════════
# INTENTO DE _FRAGMENT RCE (Symfony CVE-2014-4931)
# ═══════════════════════════════════════════════════════════════
$ curl -sS "http://XXX.XXX.XXX.XXX:8080/stalker_portal/server/adm/_fragment?_path=_controller%3Dphpinfo"
Not Found
[Status: 404, Size: 9B]
# GET -> Laravel -> 404 (Laravel no tiene esta ruta)
$ curl -sS -X POST "http://XXX.XXX.XXX.XXX:8080/stalker_portal/server/adm/_fragment?_path=_controller%3Dphpinfo"
<!DOCTYPE html>...405 Method Not Allowed...</html>
[Status: 405, Size: 835B]
# POST -> Silex -> 405 (Silex TIENE la ruta pero solo acepta GET)
# PARADOJA: GET llega a Silex pero la firma HMAC es invalida
# porque no tenemos el APP_SECRET. POST llegaria a Silex
# pero POST esta bloqueado por 405. CUPULA INTACTA.