SSTI Guide
Guía sobre SSTI
Certificaciones
- eWPT
- eWPTXv2
- OSWE
- BSCP
Descripción
Explicación técnica de la vulnerabilidad SSTI (server side template injection)
. Detallamos cómo identificar
y explotar
esta vulnerabilidad, tanto manualmente
como con herramientas automatizadas
. Además, exploramos estrategias clave para prevenirla
¿Qué es un SSTI?
Un SSTI (server side template injection)
ocurre cuando un atacante
puede usar la sintaxis nativa de la plantilla
para inyectar
un payload malicioso
en una plantilla
que se ejecuta del lado del servidor
Los motores de plantillas
están diseñados para generar páginas web combinando plantillas fijas con datos variables. Un SSTI
pueden ocurrir cuando el input
del usuario se concatena directamente en una plantilla
, en lugar de pasarse como datos
. Esto permite a los atacantes inyectar directivas de plantilla arbitrarias
para manipular el motor de plantillas
, a menudo permitiéndoles tomar el control completo del servidor
. Como indica el nombre, los payloads
para explotar un SSTI
se envían y evalúan en el servidor
, lo que los hace potencialmente mucho más peligrosos
que un client side template injection
¿Cuál es el impacto de un SSTI?
Los server side template injection
pueden exponer sitios web
a diversos ataques
según el motor de plantillas
y el uso que haga la aplicación. En circunstancias raras
pueden no suponer un riesgo real
, pero la mayoría de las veces el impacto
puede ser catastrófico
En el extremo más grave, un atacante puede lograr un RCE (remote code execution)
, tomando así, el control total del servidor back-end
y usándolo para realizar
otros ataques
contra la infraestructura interna
Incluso cuando no es posible ejecutar código
, un atacante suele poder usar el SSTI
como base para numerosos ataques, pudiendo leer archivos sensibles
del servidor
¿Cómo surge un SSTI?
Un server side template injection
surge cuando el input
del usuario
se concatena en la plantilla
en vez de pasarse como datos
Las plantillas estáticas
que simplemente proporcionan marcadores de posición en los que se renderiza contenido dinámico generalmente no son vulnerables
. El ejemplo clásico es un correo electrónico
que saluda
a cada usuario
por su nombre
, como el siguiente extracto de una plantilla Twig
:
1
$output = $twig->render("Dear {first_name},", array("first_name" => $user.first_name) );
Esto no es vulnerable
porque el first_name
del usuario se pasa a la plantilla como datos
Sin embargo, como las plantillas
son simplemente cadenas
, los desarrolladores web
a veces concatenan directamente
el input del usuario
en las plantillas
antes de renderizarlas
. Tomemos un ejemplo similar al anterior, pero esta vez los usuarios
pueden personalizar
partes del correo
antes de enviarlo. Por ejemplo, en el siguiente caso los usuarios podrían elegir el nombre
que se usa:
1
$output = $twig->render("Dear " . $_GET['name']);
En este ejemplo, en lugar de pasar un valor estático
a la plantilla
, parte de la plantilla se genera dinámicamente
usando el parámetro GET name
Como la sintaxis de la plantilla
se evalúa
en el servidor
permite a un atacante
inyectar un payload
dentro del parámetro name
1
http://vulnerable-website.com/?name=
Estas vulnerabilidades
normalmente son causadas debido a un diseño deficiente de las plantillas
y esto ocurre porque sus desarrolladores
no están familiarizadas
con las implicaciones de seguridad
que podría tener un diseño deficiente
de las plantillas
Sin embargo, a veces este comportamiento
se implementa de forma intencionada
. Por ejemplo, algunos sitios permiten deliberadamente
que ciertos usuarios privilegiados
, como editores de contenido
, editen o envíen plantillas personalizadas por diseño
. Esto plantea un enorme riesgo de seguridad
si un atacante
consigue comprometer
una cuenta
con estos privilegios
Construir un ataque para explotar un SSTI
Identificar
un SSTI
y elaborar
un ataque exitoso
típicamente implica el siguiente proceso
de alto nivel
Detectar
Como con cualquier vulnerabilidad, el primer paso hacia la explotación es ser capaz de encontrarla
. Quizás el enfoque inicial más sencillo sea realizar
un ataque de fuerza bruta inyectando una secuencia de caracteres especiales comúnmente usados en expresiones de plantilla, como \$%\.
Si se produce una excepción
, esto indicaría que el payload
inyectado está siendo interpretado
por el servidor
de algún modo
y esto es una señal de que puede existir una vulnerabilidad
Los SSTI
ocurren en dos contextos distintos
y cada uno requiere su propio método de detección
. Independientemente de los resultados del ataque de fuerza bruta
, es importante probar los siguientes enfoques específicos por contexto
Contexto de texto plano
La mayoría de los lenguajes de plantillas
permiten introducir contenido libremente
bien usando etiquetas HTML directamente
o usando la sintaxis nativa de la plantilla
, que se renderiza a HTML en el back-end antes de enviar la respuesta HTTP
. Por ejemplo, en Freemarker
, la línea render('Hello ' + username)
se renderizaría a algo como Hello Carlos
Esto a veces puede explotarse como si fuera un XSS
y es por eso que a menudo se confunde con un simple XSS
. Sin embargo, debemos testear
si este punto de entrada
también permite explotar un SSTI
estableciendo operaciones matemáticas
como valor del parámetro
Por ejemplo, consideremos una plantilla
que contiene el siguiente código vulnerable
:
1
render('Hello ' + username)
Durante la auditoría, debemos probar la inyección
solicitando una URL
como la siguiente:
1
http://vulnerable-website.com/?username=${7*7}
Si la salida resultante contiene Hello 49
, esto muestra que la operación matemática
está siendo evaluada en el servidor
. Es importante recalcar que la sintaxis específica
necesaria para evaluar con éxito la operación matemática
varía según el motor de plantillas
que se esté usando
Contexto de código
En otros casos, la vulnerabilidad
se expone porque el input del usuario
se coloca dentro
de una expresión de plantilla
, como vimos antes con el ejemplo
del correo
. Esto puede tomar la forma de un nombre de variable controlable por el usuario colocado dentro de un parámetro
, por ejemplo:
1
2
greeting = getQueryParameter('greeting')
engine.render("Hello +greeting+", data)
En el sitio web
, la URL
resultante sería algo como lo siguiente:
1
http://vulnerable-website.com/?greeting=data.username
Esto se renderizaría en la salida como Hello Carlos
, por ejemplo. Este contexto
se pasa fácilmente por alto
durante la evaluación
porque no produce
un XSS obvio
y es casi indistinguible
de una simple búsqueda en un hashmap
. Un método para testear el SSTI
en este contexto
, es primeramente saber si el parámetro es vulnerable a XSS
inyectando código HTML
de la siguiente forma:
1
http://vulnerable-website.com/?greeting=data.username<tag>
En ausencia de XSS
, esto normalmente resultará en una entrada en blanco en la salida (simplemente Hello sin nombre)
, etiquetas codificadas
o un mensaje de error
. El siguiente paso es intentar salir de la sentencia
usando la sintaxis de plantillas común
y tratar de inyectar código HTML
después de ella
1
http://vulnerable-website.com/?greeting=data.username}}<tag>
Si esto de nuevo resulta en un error
o en una salida en blanco
, hemos
usado la sintaxis del motor de plantillas equivocado
. Si probamos diferentes sintaxis
para diferentes plantillas
y ninguna parece válida
, no es posible explotar un SSTI
Alternativamente, si la salida
se renderiza correctamente
junto con el código HTML
es una indicación de que es vulnerable
a SSTI
. Por ejemplo:
1
Hello Carlos<tag>
Identificar
Una vez que hemos detectado que puede existir un SSTI
, el siguiente paso es identificar el motor de plantillas
Aunque existe una gran cantidad de lenguajes de plantillas
, muchos usan sintaxis muy parecida
diseñada para no entrar en conflictor
con caracteres HTML
. Como resultado, puede ser relativamente sencillo crear payloads
para probar qué motor de plantillas
se está usando
Enviar simplemente sintaxis inválida
suele ser suficiente porque el mensaje de error resultante
nos dirá exactamente qué motor de plantillas
es, y a veces incluso la versión
. Por ejemplo, la expresión inválida <%=foobar%>
provoca la siguiente respuesta del motor ERB (basado en Ruby)
:
1
2
3
4
(erb):1:in `<main>': undefined local variable or method `foobar' for main:Object (NameError)
from /usr/lib/ruby/2.5.0/erb.rb:876:in `eval'
from /usr/lib/ruby/2.5.0/erb.rb:876:in `result'
from -e:4:in `<main>'
Si no obtenemos un mensaje de error claro
, tendremos que probar manualmente payloads específicos de cada lenguaje
y estudiar
cómo los interpreta
el motor de plantillas
. Usando un proceso de eliminación
basado en qué sintaxis
resulta válida
o inválida
, podemos reducir las opciones
de forma rápida
Una forma común de hacerlo es inyectar operaciones matemáticas arbitrarias
usando la sintaxis de diferentes motores de plantillas
y observar si se evalúan correctamente
. Para ayudar en este proceso, podemos usar un árbol de decisiones
similar al siguiente:
Explotar
Una vez que descubramos
un SSTI
y identifiquemos
el motor de plantillas
que se está usando, la explotación
normalmente sigue el siguiente proceso
:
- Leer
- Sintaxis de la plantilla
- Documentación de seguridad
- Exploits documentados
- Explorar el entorno
Leer
A menos que ya conozcamos a fondo el motor de plantillas
, leer su documentación
suele ser el primer paso
. Aunque no sea lo más entretenido, la documentación es una fuente de información muy útil
Aprender la sintaxis básica de la plantilla
Es importante aprender la sintaxis básica
, junto con las funciones clave
y la gestión de variables
. Incluso algo tan sencillo como aprender a incrustar bloques de código nativo
en la plantilla
puede llevar rápidamente a un exploit
Por ejemplo, una vez que sabemos que se usa el motor de plantillas Mako (basado en Python)
, lograr un RCE (remote code execution)
podría ser tan simple como esto:
1
2
3
4
5
<%
import os
x=os.popen('id').read()
%>
${x}
En un entorno sin sandbox
, lograr ejecución remota de código
y usarla para leer, editar o eliminar archivos
es igualmente sencillo en muchos motores de plantillas
En estos laboratorios
podemos ver como aplicar
esta técnica
:
Basic server-side template injection - https://justice-reaper.github.io/posts/SSTI-Lab-1/
Basic server-side template injection (code context) - https://justice-reaper.github.io/posts/SSTI-Lab-2/
Leer sobre las implicaciones de seguridad
Además de explicar los fundamentos de cómo crear y usar plantillas
, la documentación puede incluir una sección de seguridad (el nombre varía)
que generalmente, describe las acciones potencialmente peligrosas
que se deben evitar
en la plantilla
. Esto puede ser un recurso invaluable
, funcionando como una especie de guía
para identificar comportamientos a auditar
y cómo explotarlos
Incluso si no hay una sección dedicada a la seguridad
, si un objeto
o función incorporada
puede representar un riesgo
, casi siempre habrá algún tipo de advertencia
en la documentación
. La advertencia puede ser breve, pero al menos marca el elemento incorporado como algo a investigar
Por ejemplo, en ERB
, la documentación
muestra que se pueden listar todos los directorios
y luego leer archivos
de la siguiente manera:
1
2
<%= Dir.entries('/') %>
<%= File.open('/example/arbitrary-file').read %>
En este laboratorio
podemos ver como aplicar
esta técnica
:
- Server-side template injection using documentation - https://justice-reaper.github.io/posts/SSTI-Lab-3/
Buscar exploits conocidos
Otra parte clave es ser buenos buscando recursos adicionales en línea
. Una vez que identifiquemos
el motor de plantillas
que se está usando, debemos buscar
en la web
si existen vulnerabilidades
que otros ya hayan descubierto.
Debido al uso generalizado de algunos motores de plantillas
, a veces podemos encontrar exploits bien documentados
que podamos
adaptar para explotar
nuestro propio objetivo
En este laboratorio
podemos ver como aplicar
esta técnica
:
- Server-side template injection in an unknown language with a documented exploit - https://justice-reaper.github.io/posts/SSTI-Lab-4/
Explorar
En este punto, podemos haber dado con un exploit
utilizable usando la documentación
. Si no es así, el siguiente paso es explorar el entorno
e intentar descubrir todos los objetos a los que tenemos acceso
Muchos motores de plantillas exponen un objeto tipo self o environment
, que actúa como un namespace
que contiene todos los objetos, métodos y atributos soportados por el motor de plantillas
Si tal objeto existe, podemos
usarlo para generar una lista de objetos que estén en el scope
. Por ejemplo, en lenguajes de plantillas
basados en Java
, a veces podemos listar todas las variables en el entorno
usando la siguiente inyección
:
1
${T(java.lang.System).getenv()}
Esto puede formar la base para crear una lista corta de objetos y métodos interesantes
a investigar. Además, para usuarios de Burpsuite Professional
, el Intruder
proporciona una wordlist incorporada
para bruteforcear
diferentes nombres de variables
Objetos suministrados por desarrolladores
Es importante notar que los sitios web
contendrán tanto objetos incorporados
proporcionados por la plantilla
como objetos personalizados
específicos del propio sitio web
, los cuales son suministrados
por el desarrollador web
Debemos
prestar especial atención a estos objetos no estándar
porque son propensos
a contener información sensible
o métodos explotables
. Como estos objetos
pueden variar
entre diferentes plantillas
dentro del mismo sitio web
, debemos
estudiar el comportamiento de un objeto
en el contexto
de cada plantilla
antes de encontrar una forma de explotarlo
Aunque un SSTI
puede potencialmente llevar a un RCE (remote code execution)
y tomar el control total del servidor
, en la práctica esto no siempre es posible
. Sin embargo, solo porque hayamos descartado esta posibilidad no significa que no exista potencial para otro tipo de exploits
. Debemos aún poder aprovechar estas vulnerabilidades
para otros ataques críticos
, como un path traversal
, el cual nos puede permitir obtener acceso a datos sensibles
En este laboratorio
podemos ver como aplicar
esta técnica
:
- Server-side template injection with information disclosure via user-supplied objects - https://justice-reaper.github.io/posts/SSTI-Lab-5/
Cheatsheet
Usaremos estas cheatsheet
para facilitar la detección
y explotación
de esta vulnerabilidad
:
- Hacking Cheatsheet https://justice-reaper.github.io/posts/Hacking-Cheatsheet/
¿Cómo detectar y explotar un SSTI?
Teniendo en cuenta que los términos y herramientas mencionados a continuación
se encuentran
en la cheatsheet mencionada anteriormente
, llevaremos a cabo los siguientes pasos:
Instalar
lasextensiones básicas
deBurpsuite
Añadir
eldominio
y sussubdominios
alscope
Hacer un
escaneo general
conBurpsuite
. Comotipo de escaneo
marcaremosCrawl and audit
y comoconfiguración de escaneo
usaremosDeep
Escanearemos partes específicas de la petición
usando elescáner de Burpsuite
. Paraescanear
losinsertion points
debemos seleccionar entipo de escaneo
la opciónAudit selected items
Jugaremos con las opciones de
Tplmap
y deSSTImap
para intentarexplotar
elSSTI
Si no podemos explotarlo de primeras, vamos a usar la herramienta
TInjA
para intentaridentificar
laplantilla
que se estáusando
Si esto no da resultado, usaremos
Template Injection Table
Si no podemos explotarlo con estas herramientas, ejecutamos una
ataque de fuerza bruta
con elIntruder
deBurpsuite
empleando variosdiccionarios
. Primeramente vamos a usar eldiccionario integrado de Burpsuite
llamadoFuzzing - template injection
, posteriormente usaremos los diccionarios que contenganpayloads
para estavulnerabilidad
y si enHacktricks
oPayloadsAllTheThings
hay alguno, también podemos probarlosSi no encontramos nada,
checkearemos
lascheatsheets
dePayloadsAllTheThings
yHacktricks
e iremostesteando de forma manual
Si hemos logrado
identificar el motor de plantillas
perono llevar a cabo una explotación
debemosbuscar vulnerabilidades para esa plantilla
. Si no encontramos ninguna,revisaremos su documentación
para ver si podemosaprovecharnos de alguna característica para obtener información interesante
Prevenir un SSTI
La mejor forma de prevenir un SSTI
es no permitir
que ningún usuario modifique o envíe nuevas plantillas
. Sin embargo, esto a veces es inevitable
por requisitos
de negocio
Una de las formas más simples de evitar
estas vulnerabilidades
es usar
siempre un motor de plantillas "sin lógica"
, como Mustache
, a menos que sea absolutamente necesario. Separar la lógica de la presentación
tanto como sea posible puede reducir enormemente la exposición
a los ataques más peligrosos basados en plantillas
Otra medida es ejecutar el código de los usuarios solo en un entorno aislado (sandbox)
donde los módulos y funciones peligrosas se eliminen por completo
. Desafortunadamente, aislar código no confiable es difícil y es propenso a ser bypasseado
Finalmente, otro enfoque complementario es asumir que la ejecución de código arbitrario es inevitable
y aplicar un sandboxing propio
desplegando el entorno de plantillas
en un contenedor Docker con ciertas restricciones y permisos limitados
, por ejemplo