Inyección de Plantillas del Lado del Servidor (SSTI)
cuando el render te juega en contra
SSTI ocurre cuando una aplicación mete valores que vienen del usuario directamente en una plantilla sin controlarlos ni filtrarlos. Eso puede acabar en ejecución remota de código si el motor de plantillas lo permite y el dev no cortó lo que tocaba.
¿Dónde empieza la liada?
Algo básico:
Hola {{nombre}}
Si metes {{7*7}} y te sale Hola 49, hay SSTI. Pero eso es solo el principio.
Subiendo de nivel: explorando el entorno
En motores como Jinja2, podrías meter cosas como:
{{ config }}
O peor:
{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('ls').read() }}
Y si eso te saca cosas del sistema, ya estamos hablando de RCE directo. Ahi la fiesta empieza de verdad.
Plantillas: qué hacen y por qué se rompen
Una template debería recibir datos ya validados, tipo:
"Bienvenido {{usuario}}"
Pero si al final el usuario puede modificar la propia lógica de la plantilla (y no solo el valor), puede colarse lógica, llamadas a funciones, e incluso escapes del sandbox.
Motores que han petado feo
- Jinja2: sin sandbox, puedes llegar a ejecutar comandos del sistema desde el navegador. Literalmente
os.popen()desde un campo de input. - Twig: permite acceder a propiedades y funciones internas. Si no tienes cuidado, se filtra info o ejecutas código PHP.
- Handlebars: si alguien cuela un helper o manipula el prototipo del objeto, puedes llegar a modificar la lógica del render.
- Pug / Jade: errores en la indentación o uso de mixins pueden derivar en lógica inyectada sin querer.
Explotación típica
1. Mandas un input con algo que sabes que se ejecuta, tipo {{7*8}}.
2. Si ves que sale 56 en vez de lo que escribiste, ya sabes que evalúa.
3. Buscas objetos accesibles: {{ self }}, {{ config }}, {{ ''.__class__.__mro__ }}...
4. Intentas llegar a funciones tipo os.system o popen() para ejecutar comandos.
Bypasses y evasiones
Hay filtros tontos tipo bloquear { o }}, pero los motores suelen permitir notación alternativa:
${7*7}<%= 7*7 %>- Uso de
set,eval, objetos internos, contextos ocultos...
Prevención real
- Jamás metas datos directos del usuario dentro de la plantilla sin ni siquiera echarles un vistazo.
- Procura usar motores que no permitan que se ejecute código dinámico, así evitas problemas chungos.
- Escapa todo, valida con ojo de halcón lo que entra, no te quedes corto filtrando.
- Si el motor que usas deja, activa un sandbox que limite lo que puede hacer el código.
- Chequea los logs para encontrar patrones raros, como
{{o palabras como__class__, que suelen ser señal de ataques. - Olvídate de funciones peligrosas como
eval()oexec()cuando el input viene del usuario, no las uses para nada. - Restringe bien qué variables y métodos puede acceder la plantilla, no le des acceso a todo el cotarro.
- Con Jinja2, usa
autoescape=Truey crea un entorno (Environment) con las reglas bien puestas. - No dejes que los usuarios puedan tirar mano de atributos con doble guión bajo (
__*) o métodos sensibles. - Evita que el usuario decida qué plantilla cargar o la ruta, eso es peligroso y abre puertas a ataques.
- Usa listas blancas para los inputs que terminen en plantillas, controla bien los patrones, algo tipo
^[a-zA-Z0-9 ]+$va bien. - Monta un sistema para vigilar y auditar los puntos donde llegan datos para plantillas, así pillas cualquier cosa rara rápido.
- Haz pruebas tú mismo con payloads locos tipo
{{config}},{{request}}o{{self.__class__}}, para ver si te cuela algo. - Si hay errores, no muestres información que deje ver la estructura interna ni tracebacks completos, eso da pistas a los malos.
- Piénsate usar análisis estático que te diga dónde el código mete plantillas inseguras.