La candidata número uno de la lista priorizada del Módulo 3 es una probable inyección SQL en tienda.technova.lab/producto.php?id=, la aplicación PHP/MySQL de comercio electrónico de TechNova. Es también nuestro objetivo principal: la tienda está en Internet y concentra los datos de clientes. En esta lección validamos esa candidata y recorremos las clases de vulnerabilidad web más relevantes del OWASP Top 10 / WSTG, aplicando el ciclo controlado de 04-01: mecanismo, PoC de bajo impacto y remediación con código seguro.
Recordatorio del encuadre, que aquí es especialmente importante porque la tienda maneja datos personales ficticios: todo ocurre en el laboratorio autorizado de TechNova, dentro del alcance y con control del impacto. No volcamos la base de clientes, no borramos nada, no dejamos scripts persistentes. Cada vulnerabilidad se enseña con su mecanismo (por qué existe), una PoC que solo demuestra (leer una versión, un id, un valor centinela) y su arreglo. El objetivo del pentester es reportar y ayudar a corregir, no causar daño.
Contenido
- Por qué la web es la superficie más expuesta
- Inyección SQL y su automatización responsable (sqlmap)
- Cross-Site Scripting (XSS): reflejado y almacenado
- Cross-Site Request Forgery (CSRF)
- Inclusión de ficheros: LFI y RFI
- Subida de ficheros insegura
- IDOR y control de acceso roto
- SSRF (Server-Side Request Forgery)
- Deserialización insegura (visión general)
- Detección y herramientas
- Errores comunes y consejos
- Ejercicios
- Conclusión
- Por qué la web es la superficie más expuesta
Las aplicaciones web están, por definición, expuestas a Internet y procesan entrada no confiable del usuario en cada petición. Casi todas las vulnerabilidades web nacen del mismo pecado original: mezclar datos controlados por el atacante con código o comandos (SQL, HTML, rutas de fichero, peticiones HTTP) sin separarlos correctamente. Entender ese patrón hace que todas las clases siguientes encajen.
- Inyección SQL y su automatización responsable
Mecanismo. La inyección SQL ocurre cuando la entrada del usuario se concatena directamente en una consulta SQL. El atacante escapa del contexto de "dato" y pasa a inyectar "código" SQL.
Código vulnerable (el producto.php de TechNova):
<?php
// VULNERABLE: el parámetro id se concatena sin separar dato de codigo
$id = $_GET['id'];
$sql = "SELECT nombre, precio, descripcion FROM productos WHERE id = $id";
$res = mysqli_query($conn, $sql);
?>Si llega producto.php?id=1, la consulta es normal. Pero si llega producto.php?id=1 OR 1=1, se convierte en ... WHERE id = 1 OR 1=1, que devuelve todos los productos: la entrada ha cambiado la lógica.
PoC de validación de bajo impacto. Siguiendo el marco de 04-01, no volcamos datos: solo demostramos que la entrada se interpreta como SQL.
# 1) Prueba de error: una comilla rompe la sintaxis -> confirma inyeccion
curl "http://tienda.technova.lab/producto.php?id=1'"
# -> "You have an error in your SQL syntax..." (la entrada llega al motor)
# 2) Prueba booleana: misma pagina con TRUE, distinta/vacia con FALSE
curl "http://tienda.technova.lab/producto.php?id=1 AND 1=1" # producto visible
curl "http://tienda.technova.lab/producto.php?id=1 AND 1=2" # producto ausente
# 3) Extraer UN dato inofensivo del motor (la version), no datos de clientes
curl "http://tienda.technova.lab/producto.php?id=-1 UNION SELECT @@version,2,3"
# -> muestra "5.5.62" en el hueco del nombre: acceso confirmado, sin tocar clientesLeer @@version (que además confirma el MySQL 5.5.62 del inventario) demuestra el control sobre la consulta sin extraer ni un solo registro de clientes. Eso basta para el informe.
Automatización responsable con sqlmap. Para mapear el alcance real (qué bases de datos y tablas serían accesibles) se usa sqlmap, pero de forma controlada:
# Enumerar solo estructura (bases de datos), sin volcar contenido sensible
sqlmap -u "http://tienda.technova.lab/producto.php?id=1" --batch --dbsUso responsable: enumerar estructura para dimensionar el riesgo, no --dump-all. Nada de --os-shell ni escritura salvo que las RoE lo autoricen expresamente. sqlmap es potente y ruidoso; se usa dentro de la ventana acordada.
Remediación (código seguro):
<?php
// SEGURO: consulta preparada, el dato NUNCA se mezcla con el codigo SQL
$stmt = $conn->prepare("SELECT nombre, precio, descripcion FROM productos WHERE id = ?");
$stmt->bind_param("i", $id); // "i" fuerza entero: separa dato de codigo
$stmt->execute();
$res = $stmt->get_result();
?>Las consultas parametrizadas (prepared statements) envían la consulta y los datos por separado; el motor nunca interpreta el dato como SQL. Es la defensa definitiva, más el principio de mínimo privilegio en la cuenta de BD.
- Cross-Site Scripting (XSS): reflejado y almacenado
Mecanismo. El XSS inyecta JavaScript en una página que otros usuarios verán, porque la aplicación devuelve entrada del usuario sin escaparla en el HTML. El código se ejecuta en el navegador de la víctima.
| Tipo | Dónde vive el payload | Ejemplo en TechNova |
|---|---|---|
| Reflejado | En la URL, se refleja en la respuesta | Buscador: buscar.php?q=... |
| Almacenado | Guardado en BD, se sirve a todos | Comentario/reseña de un producto |
| DOM | En JS del cliente, sin pasar por servidor | Fragmento # procesado por JS |
Código vulnerable (buscador reflejado):
<?php // VULNERABLE: eco directo del parametro en el HTML
echo "<p>Resultados para: " . $_GET['q'] . "</p>";
?>PoC de bajo impacto. No robamos sesiones reales: probamos con un payload inofensivo y visible.
Si salta el alert con tienda.technova.lab, el XSS está confirmado. Un alert() demuestra la ejecución sin exfiltrar cookies; para el informe basta la captura.
Remediación (código seguro):
<?php // SEGURO: escapar segun el contexto de salida (HTML)
echo "<p>Resultados para: " . htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8') . "</p>";
?>Defensas: escapar según contexto (htmlspecialchars en HTML), Content-Security-Policy (cabecera que restringe qué scripts se ejecutan), cookies HttpOnly (JS no puede leerlas) y validación de entrada. En marcos modernos, el autoescape de plantillas cubre la mayoría de casos.
- Cross-Site Request Forgery (CSRF)
Mecanismo. El CSRF abusa de que el navegador envía las cookies de sesión automáticamente. Un sitio malicioso hace que el navegador de una víctima autenticada envíe una petición legítima a TechNova sin su intención (cambiar email, dirección de envío...).
Código vulnerable (formulario sin token):
<!-- VULNERABLE: la accion solo depende de la cookie de sesion -->
<form action="/cuenta/cambiar_email.php" method="POST">
<input type="email" name="email">
<input type="submit" value="Guardar">
</form>PoC de bajo impacto. Una página externa con un formulario autoenviado hacia TechNova; en laboratorio se prueba cambiando el email a un valor centinela ficticio ([email protected]) para demostrar el efecto sin dañar cuentas reales.
Remediación (código seguro):
<?php // SEGURO: token CSRF por sesion, validado en el servidor
$token = bin2hex(random_bytes(32));
$_SESSION['csrf'] = $token; ?>
<form action="/cuenta/cambiar_email.php" method="POST">
<input type="hidden" name="csrf" value="<?php echo $token; ?>">
<input type="email" name="email">
</form><?php // Al procesar: comparacion en tiempo constante
if (!hash_equals($_SESSION['csrf'] ?? '', $_POST['csrf'] ?? '')) {
http_response_code(403); exit('CSRF');
}Defensas: token anti-CSRF por sesión, cookies SameSite=Lax/Strict, y reautenticación para acciones sensibles.
- Inclusión de ficheros: LFI y RFI
Mecanismo. Ocurre cuando una ruta de fichero se construye con entrada del usuario. LFI (Local File Inclusion) incluye ficheros del propio servidor; RFI (Remote File Inclusion) incluye ficheros remotos (más grave: ejecución de código).
Código vulnerable:
PoC de bajo impacto. Leer un fichero no sensible pero probatorio con recorrido de directorios:
Si aparecen líneas de /etc/passwd (nombres de usuario del sistema, no secretos), el LFI está confirmado. No leemos claves ni ficheros privados: /etc/passwd basta como prueba.
Remediación (código seguro):
<?php // SEGURO: lista blanca; la entrada nunca forma la ruta directamente
$permitidas = ['home' => 'home.php', 'ayuda' => 'ayuda.php'];
$page = $permitidas[$_GET['page']] ?? 'home.php';
include(__DIR__ . '/paginas/' . $page);
?>Defensas: lista blanca de valores, basename(), desactivar allow_url_include (mata el RFI) y nunca construir rutas con entrada cruda.
- Subida de ficheros insegura
Mecanismo. Si la aplicación acepta ficheros sin validar tipo ni destino, el atacante sube un script ejecutable (por ejemplo un .php) a una carpeta servible y luego lo invoca: ejecución remota de código.
Código vulnerable:
<?php // VULNERABLE: guarda lo que sea en un directorio publico y ejecutable
move_uploaded_file($_FILES['f']['tmp_name'], "uploads/" . $_FILES['f']['name']);
?>PoC de bajo impacto. Subir un .php inofensivo que solo demuestre ejecución, no una webshell operativa:
Al visitar uploads/poc.php y ver el poc-technova con el kernel, queda demostrada la ejecución. No se sube una webshell completa ni se opera con ella: la prueba centinela es suficiente.
Remediación: validar tipo real (MIME/magic bytes) contra lista blanca, renombrar con nombre aleatorio y extensión controlada, guardar fuera del webroot o en almacenamiento sin ejecución, y desactivar la ejecución de PHP en uploads/.
- IDOR y control de acceso roto
Mecanismo. IDOR (Insecure Direct Object Reference) es acceder a objetos de otros usuarios cambiando un identificador, porque el servidor no comprueba la propiedad. Es la esencia del "control de acceso roto".
Código vulnerable:
<?php // VULNERABLE: sirve la factura por id sin comprobar de quien es
$id = $_GET['factura'];
$f = $db->query("SELECT * FROM facturas WHERE id = $id"); // ademas SQLi...
echo render($f);
?>PoC de bajo impacto. Autenticado como un usuario de prueba (cliente01), cambiar factura=1001 por factura=1002 y comprobar si se ve la factura de otro usuario ficticio. Basta ver una factura ajena de prueba para confirmar; no se recolectan datos masivos.
Remediación:
<?php // SEGURO: filtrar SIEMPRE por el propietario de la sesion
$stmt = $db->prepare("SELECT * FROM facturas WHERE id = ? AND usuario_id = ?");
$stmt->bind_param("ii", $_GET['factura'], $_SESSION['user_id']);
?>Defensas: comprobar autorización por objeto en cada acceso, referencias indirectas o no predecibles, y negar por defecto.
- SSRF (Server-Side Request Forgery)
Mecanismo. La aplicación hace una petición HTTP a una URL que controla el usuario. El atacante apunta esa petición a la red interna (a la que él no llega) o a servicios de metadatos, usando el servidor como proxy.
Código vulnerable:
PoC de bajo impacto. Pedir una URL interna del laboratorio y comprobar que el servidor la alcanza:
Si vuelve contenido de un servicio interno de 10.10.10.0/24 inaccesible desde fuera, el SSRF está confirmado. La prueba se limita a leer una respuesta, no a atacar el servicio interno.
Remediación: lista blanca de dominios/hosts permitidos, resolver y validar la IP de destino (bloquear rangos privados y 169.254.169.254), y desactivar redirecciones y esquemas peligrosos (file://, gopher://).
- Deserialización insegura (visión general)
Mecanismo (alto nivel). Cuando una aplicación deserializa datos controlados por el usuario (objetos PHP/Java/Python serializados), un atacante puede construir un objeto que, al reconstruirse, dispara código (gadget chains). Es una clase avanzada; a este nivel basta con reconocer el patrón de riesgo: entrada no confiable → unserialize()/pickle.loads() → posible RCE.
Regla defensiva: no deserialices datos no confiables. Si necesitas intercambiar datos, usa formatos que solo transportan datos (JSON) con validación de esquema, firma la carga (HMAC) para detectar manipulación y evita unserialize() sobre entrada de usuario.
- Detección y herramientas
Estas vulnerabilidades se descubren y validan con proxys de interceptación y escáneres, que se ven a fondo en el Módulo 7:
- Burp Suite (07-03): interceptar, repetir (Repeater) y automatizar peticiones (Intruder); mapear la tienda.
- OWASP ZAP (07-04): alternativa libre con escaneo pasivo/activo.
- sqlmap: automatización responsable de SQLi (apartado 2).
Desde el lado defensivo, se detectan con WAF, revisión de logs (picos de errores SQL, ../ en parámetros), pruebas SAST/DAST en el ciclo de desarrollo y cabeceras de seguridad (CSP, X-Content-Type-Options).
- Errores Comunes y Consejos
- Volcar la base de datos entera "porque se puede". Rompe el control de impacto y expone datos personales. Extrae solo el dato mínimo que prueba el fallo.
- Usar un XSS para robar cookies reales de sesión. En la PoC basta un
alert(); robar sesiones ajenas es innecesario y peligroso. - Confundir LFI con RFI. El RFI (fichero remoto) suele dar RCE directa y es más grave; distínguelos en el informe.
- Subir una webshell operativa. Un fichero centinela que imprime
php_uname()ya demuestra la ejecución; una webshell completa es exceso de impacto. - Reportar sin remediación. Cada hallazgo web debe ir con su código seguro. El valor del informe está en el arreglo, no en el susto.
- Lanzar sqlmap con
--dump-allo--os-shellsin autorización. Es intrusivo y puede exceder el alcance. Enumera estructura; el volcado y la ejecución requieren permiso explícito. - Consejo: casi todas estas clases comparten raíz —mezclar dato con código—. Interioriza el patrón "separar/escapar/validar según contexto" y las reconocerás todas.
- Ejercicios
Ejercicio 1. Sobre tienda.technova.lab/producto.php?id=, describe la secuencia de PoC de bajo impacto para confirmar la SQLi de la candidata #1 sin extraer datos de clientes, y escribe la versión remediada de la consulta. Explica por qué la consulta preparada cierra el fallo.
Ejercicio 2. El campo de reseñas de un producto guarda el texto y lo muestra a todos los visitantes. Clasifica el tipo de XSS que supone, propón un payload de prueba inofensivo para validarlo y da dos medidas de remediación con su código.
Ejercicio 3. Como cliente01 ves tu factura en /factura.php?id=1001. Explica cómo comprobarías un IDOR de forma controlada, qué evidencia mínima capturarías y cómo lo arreglarías en el servidor.
Soluciones
Solución 1. Secuencia: (1) id=1' → error SQL en la respuesta confirma que la entrada llega al motor; (2) prueba booleana id=1 AND 1=1 (producto visible) frente a id=1 AND 1=2 (ausente) confirma control de la lógica; (3) id=-1 UNION SELECT @@version,2,3 muestra 5.5.62, un dato del motor, sin tocar clientes. Remediación: $stmt = $conn->prepare("SELECT nombre,precio,descripcion FROM productos WHERE id = ?"); $stmt->bind_param("i",$id);. Cierra el fallo porque la consulta y los datos viajan por canales separados: el ? es un marcador de posición y el id se trata siempre como dato entero, nunca como SQL ejecutable, así que 1 OR 1=1 deja de ser interpretado como código.
Solución 2. Es un XSS almacenado (el payload se guarda en BD y se sirve a todos los visitantes), el más peligroso porque no requiere engañar a cada víctima. Payload de prueba inofensivo: <script>alert(document.domain)</script> en la reseña; si salta al abrir la página del producto, está confirmado (captura como evidencia, sin robar nada). Remediación: (1) escapar en la salida, echo htmlspecialchars($reseña, ENT_QUOTES, 'UTF-8');; (2) cabecera CSP restrictiva, p. ej. Content-Security-Policy: default-src 'self', que impide ejecutar scripts inyectados aunque se cuele texto. Complemento: cookies HttpOnly.
Solución 3. Autenticado como cliente01, cambiar id=1001 por id=1002 (u otro cercano) y observar si se muestra la factura de otro usuario ficticio; con ver una factura ajena de prueba basta para confirmar el IDOR (evidencia mínima: la petición con el id alterado y la respuesta que muestra datos de otro titular, sin recolectar más). Arreglo en servidor: consultar siempre filtrando por el propietario de la sesión, ... WHERE id = ? AND usuario_id = ? con usuario_id = $_SESSION['user_id'], de modo que un id ajeno no devuelva nada. Negar por defecto y comprobar la propiedad en cada acceso.
Conclusión
Hemos convertido la candidata número uno del Módulo 3 en un hecho: la SQLi de producto.php?id= es real y explotable, validada con una PoC de bajo impacto que solo leyó la versión del motor. Y no nos hemos quedado ahí: hemos recorrido el mapa de la superficie web de TechNova —XSS reflejado y almacenado, CSRF, LFI/RFI, subida insegura, IDOR/control de acceso roto, SSRF y deserialización— viendo en cada una el mecanismo, una prueba que demuestra sin destruir y, sobre todo, el código seguro que la cierra. El hilo conductor de todas: no mezclar datos del usuario con código, y separar/escapar/validar según el contexto.
Con la tienda auditada, giramos hacia la infraestructura. La siguiente lección, 04-03 Explotación de Vulnerabilidades de Red, baja del navegador a los cables y los servicios: los servicios con CVE explotable del inventario, la interceptación de tráfico (MITM/ARP), los ataques a SMB de la red interna 10.10.10.0/24 y las configuraciones débiles, siempre con su detección (IDS) y su hardening como contrapartida.
Curso de Pentesting: Técnicas de Pruebas de Penetración
Módulo 1: Introducción al Pentesting
- ¿Qué es el Pentesting?
- Tipos de Pentesting
- Fases del Pentesting
- Ética y Legalidad en el Pentesting
- Metodologías y Estándares del Sector
Módulo 2: Reconocimiento y Recolección de Información
- Reconocimiento Pasivo
- Reconocimiento Activo
- Herramientas de Recolección de Información
- OSINT y Análisis de la Superficie de Ataque
Módulo 3: Escaneo y Enumeración
Módulo 4: Explotación de Vulnerabilidades
- Introducción a la Explotación
- Explotación de Vulnerabilidades Web
- Explotación de Vulnerabilidades de Red
- Explotación de Vulnerabilidades de Sistemas
- Ataques a Contraseñas y Autenticación
Módulo 5: Post-Explotación
- Escalada de Privilegios
- Mantenimiento del Acceso
- Pivoting y Movimiento Lateral
- Cobertura de Huellas y Anti-Forense
Módulo 6: Reporte y Remediación
- Documentación de Hallazgos
- Clasificación de Riesgos y CVSS
- Recomendaciones de Remediación
- Presentación de Resultados
