Algunos de los mejores hallazgos vienen de no rendirse cuando el camino obvio está bloqueado.
Estaba trabajando en un programa privado de bug bounty con un alcance amplio. Durante el reconocimiento, encontré un subdominio que ejecutaba una aplicación interna. La página de inicio de sesión era pública, pero el registro estaba deshabilitado. No había forma obvia de entrar.
Saqué los archivos JavaScript de la página de inicio de sesión buscando endpoints codificados o llamadas a la API. Nada útil. Callejón sin salida.
La mayoría de la gente seguiría adelante en este punto. Casi lo hago. Pero luego revisé los resultados de enumeración de subdominios de mis herramientas de reconocimiento.

Un subdominio destacaba. Si la aplicación se ejecutaba en app1.domain.com, la API estaba en app1api.domain.com. El patrón de nombres era obvio una vez que lo veías.
Empecé a hacer fuzzing en el subdominio de la API. Fuerza bruta de directorios estándar, buscando endpoints que pudieran no estar bloqueados correctamente. Después de recorrer listas de palabras, una ruta devolvió una respuesta diferente: /company/registertest.
ffuf -u https://app1api.domain.com/company/FUZZ \
-w raft-large-words.txt -mc 200,302 -fs 0
...
registertest [Status: 200, Size: 412]El nombre del endpoint contaba la historia. Era un endpoint de registro de prueba, probablemente un resto del desarrollo. Alguien lo había construido para facilitar las pruebas y nunca lo quitó cuando la aplicación pasó a producción.
Lo probé. Funcionó. Podía crear cuentas en una aplicación que no se suponía que permitiese el registro.
En este punto, tenía un hallazgo válido. Creación de cuentas no autorizadas a través de un endpoint de desarrollo olvidado. Severidad baja a media dependiendo de cómo el programa valorase los problemas de control de acceso.
Aún no lo reporté.
El alcance era *.domain.com. Eso significaba que todo bajo el dominio era válido. Ahora tenía acceso a la aplicación. La pregunta era si había algo peor dentro.
Me registré, inicié sesión y empecé a mapear la funcionalidad de la aplicación. Proceso de prueba estándar. Revisar las funciones, observar el tráfico de red, examinar el JavaScript en busca de lógica del lado del cliente.
En uno de los archivos JS, encontré una llamada AJAX que destacaba. Llegaba a un archivo PHP con un parámetro llamado "function". El valor del parámetro se establecía dinámicamente según la interacción del usuario.
$.post('/ajax/handler.php', {
function: action, // se asigna según el botón pulsado
args: JSON.stringify(args)
}, ...);Eso ya es mala señal. Un parámetro llamado "function" en una petición a un endpoint PHP suele significar que alguien está haciendo algo peligroso con la entrada del usuario. Ya sea llamando funciones directamente o usando patrones tipo eval que ejecutan código.
Lo probé. Envié diferentes nombres de funciones. Probé comandos del sistema. Funcionó.
POST /ajax/handler.php HTTP/1.1
Host: app1.domain.com
Cookie: PHPSESSID=...
function=system&args=["id"]HTTP/1.1 200 OK
Content-Type: text/plain
uid=33(www-data) gid=33(www-data) groups=33(www-data)Ejecución remota de código. Ejecución completa de comandos en el servidor. Severidad crítica. El tipo de hallazgo que se prioriza de inmediato.
La cadena era directa una vez que la planteabas:
- Endpoint de desarrollo olvidado permite registro no autorizado
- El registro da acceso a la aplicación interna
- La aplicación interna tiene una vulnerabilidad RCE en funcionalidad autenticada
¿Reportar el primer hallazgo por sí solo? Es válido pero limitado en impacto. ¿Encadenarlos? Es un compromiso crítico de toda la aplicación.
Así es como se ve la escalada de impacto en la práctica. Encuentras una debilidad, la usas para conseguir acceso y luego encuentras la vulnerabilidad real dentro. El primer problema es la llave que abre la puerta. El segundo es lo que hace que merezca la pena cruzarla.
He visto a investigadores reportar hallazgos demasiado rápido. Encuentran la primera vulnerabilidad, la envían inmediatamente y pierden la visión de conjunto. Luego alguien más reporta la cadena una semana después y se lleva el crédito por el crítico.
La lección no es retener los hallazgos indefinidamente. Es pensar en el alcance y el impacto antes de darle a enviar. Si el programa permite pruebas amplias y has encontrado una forma de entrar, pasa unas horas buscando qué más podría estar escondido dentro. Puede que no encuentres nada. Puede que encuentres algo peor.
En este caso, habría reportado el endpoint de registro incluso si no hubiese encontrado el RCE. Pero me di unas horas para probar primero. Esa decisión convirtió un problema de control de acceso de severidad media en una ejecución remota de código crítica.
El endpoint olvidado fue descuidado. El RCE fue peor. Juntos, demostraron un fallo completo de los controles de seguridad. Ese es el hallazgo que importa.
Los endpoints de desarrollo nunca deberían llegar a producción. Los parámetros de función nunca deberían aceptar entrada arbitraria del usuario. Estos son principios básicos de seguridad y se violan constantemente. Cuando se acumulan en la misma aplicación, el impacto se multiplica.
Eso es lo que hace que el trabajo de bug bounty sea interesante. No solo buscas vulnerabilidades individuales. Buscas cadenas, combinaciones, patrones que agraven la severidad. El investigador que ve esas conexiones es el que encuentra los problemas críticos.
