Ghost AcademyGhostOffensive · Ghost Academy
GhostOffensiveMódulo 06Con laboratorio

Ataques web comunes

Tres familias de fallos aparecen una y otra vez en aplicaciones reales: dejar que el usuario elija qué fichero se lee, dejar que suba lo que quiera, y dejar que sus datos acaben dentro de un comando del sistema. Este módulo las trabaja una por una.

Objetivos de la unidad

1. Inclusión de fichero local (LFI) y recorrido de rutas

Ocurre cuando una aplicación construye una ruta de fichero a partir de un dato del usuario sin comprobar que el resultado se queda dentro de la carpeta prevista. Para entenderlo hay que entender ../: en una ruta, .. significa "el directorio padre". Si la aplicación hace abrir("plantillas/" + idioma) y tú pones como idioma el valor ../../../../etc/passwd, la ruta resultante sube cuatro niveles y sale por completo de plantillas/ hasta la raíz del sistema:

kali@lab
$ curl 'http://objetivo/pagina?fichero=es.txt'      # uso normal
kali@lab
$ curl 'http://objetivo/pagina?fichero=../../../../etc/passwd'   # traversal

Los ../ de más no molestan: en la raíz, subir un nivel te deja en la raíz. Por eso se ponen de sobra. No hace falta llegar a /etc/passwd: a menudo lo valioso es salir de la carpeta prevista para leer el código fuente de la propia aplicación (y con él, sus credenciales y otras vulnerabilidades) o sus ficheros de configuración.

LFI vs RFI, y de leer a ejecutar. Si el fichero incluido puede estar en otro servidor (una URL) se llama RFI (Remote File Inclusion), hoy más raro pero más grave. Y una LFI puede escalar a ejecución de código si el atacante logra que el fichero leído contenga código que el servidor interprete: envenenando un log que luego se incluye, o abusando de envoltorios del lenguaje. La frontera entre "solo leo ficheros" y "ejecuto código" es más fina de lo que parece.

2. Subida de ficheros: romper cada validación

El objetivo de atacar una subida es colocar en el servidor un fichero que luego se pueda ejecutar (una webshell). Para eso hay que saltarse la validación, y casi todas las validaciones débiles caen así:

Comprobación débilPor qué falla / cómo se evade
El nombre "contiene" .jpgshell.jpg.php también lo contiene: la comprobación no mira el final
Confiar en el Content-Type de la peticiónLo fija el cliente; se cambia con un proxy a image/png
Comprobar solo la extensión declaradaExtensiones alternativas (.phtml, .php5) que el servidor sigue ejecutando
Comprobar los "números mágicos" (cabecera del fichero)Anteponer los bytes mágicos de una imagen real seguidos del código

Una validación correcta hace lo contrario de cada fila: comprueba la extensión al final contra una lista blanca, inspecciona el contenido real, renombra el fichero con un nombre generado por el servidor, y lo guarda en un directorio sin permiso de ejecución. Que se salte la validación no basta si además no se puede ejecutar lo subido.

kali@lab
$ curl -F 'fichero=@shell.jpg.php' http://objetivo/subir   # doble extensión
kali@lab
$ curl -F 'fichero=@shell.php;type=image/png' http://objetivo/subir   # Content-Type falso

3. Inyección de comandos

Sucede cuando una aplicación pasa un dato del usuario a un intérprete de comandos del sistema en lugar de invocar el programa directamente. Si el código hace ejecutar("ping -c1 " + host), el shell que ejecuta esa cadena también interpreta sus metacaracteres, y con ellos se encadenan órdenes propias:

OperadorEfectoCuándo usarlo
;Ejecuta el segundo comando siempreEl caso general
&&Segundo comando solo si el primero tuvo éxitoCuando controlas que el primero funcione
||Segundo comando solo si el primero fallaCuando fuerzas un fallo en el primero
|La salida del primero entra en el segundoEncadenar filtros
$(...) / backticksSustitución: ejecuta lo de dentro y lo insertaInyectar dentro de un argumento
kali@lab
$ curl -d 'host=127.0.0.1; whoami' http://objetivo/diagnostico

Inyección de comandos a ciegas: muchas veces la salida del comando no se muestra en la respuesta. Igual que en la SQLi ciega, hay dos canales para confirmar la ejecución: el tiempo (; sleep 5 y mides si la respuesta tarda) y la red (haces que el servidor te haga un ping o una petición a una máquina que tú controlas). Si tarda 5 segundos, o si recibes ese ping, el comando se ejecutó aunque no veas su salida.

La forma segura de invocar un programa externo pasa el binario y sus argumentos por separado (una lista, no una cadena), de modo que el intérprete de comandos no llega a entrar en juego y los metacaracteres se tratan como texto literal.

4. Un mismo dato, muchas superficies

La idea que une las tres familias de este módulo: un mismo parámetro de entrada puede acabar en sitios muy distintos —el sistema de ficheros (LFI), un comando del sistema (inyección de comandos), una consulta (SQLi, módulo 7), el HTML de otra víctima (XSS, módulo 8)—. La vulnerabilidad no está en el dato, sino en a dónde llega sin ser tratado. Por eso, ante cada entrada, la pregunta correcta no es "¿esto parece peligroso?", sino "¿a dónde va a parar este valor y qué lo interpreta allí?".

Para profundizar
🧰 Herramientas relacionadas (Ghost Academy)

Guías y laboratorios ya publicados en Ghost Academy sobre las herramientas de este módulo:

FFuF · Wapiti · RevShell · GTFOBins

Laboratorio: Aurora Reports

⬇ Descargar laboratorio (ZIP)

Descomprime, lee el LEEME.md y lanza startlab.sh. Encontrarás 8 flags GO{...} repartidas entre inclusión de ficheros, subida de ficheros e inyección de comandos. Verifícalas en http://localhost:8383/verificador.

Ninguna vulnerabilidad de este laboratorio permite salir del contenedor: practica con confianza.

¿Atascado o ya terminado? La solución paso a paso revela todas las flags. Úsala cuando lo hayas intentado.

Autoevaluación

  1. ¿Por qué funcionan los ../ de un recorrido de rutas, y por qué se ponen más de los necesarios?
  2. ¿En qué se diferencian LFI y RFI, y cómo puede una LFI acabar ejecutando código?
  3. ¿Por qué comprobar que un nombre "contiene" una extensión no es lo mismo que comprobar que "termina" en ella?
  4. Además de saltarse la validación, ¿qué otra condición necesita cumplirse para que una webshell subida sea explotable?
  5. Explica la diferencia entre ;, && y || en una inyección de comandos.
  6. Si una inyección de comandos no muestra la salida, ¿cómo confirmas que se ejecutó?
  7. ¿Cómo evitarías por completo la inyección de comandos al invocar un programa externo con un dato del usuario?