the_open_door
café
ESEN
← Volver al índice

Client-Side Template Injection (CSTI)

ATT&CKT1059.007

La Client-Side Template Injection (CSTI) aparece cuando una aplicación incrusta la entrada del usuario dentro de una plantilla que evalúa el navegador, no el servidor. Frameworks como AngularJS, Vue o Mavo interpretan expresiones {{ ... }} en el DOM; si el texto que controlamos llega sin escapar a una zona que el motor evalúa, esa expresión se ejecuta en el contexto de la página. A diferencia de la SSTI (que corre en el servidor), la CSTI se ejecuta en el cliente, así que su desenlace natural es un XSS: robo de cookie de sesión, acciones en nombre de la víctima o pivote hacia el resto de la aplicación.

La sonda es la misma que en SSTI, pero interpretada por el framework de cliente: si al inyectar una operación aritmética la página devuelve el resultado en vez del texto literal, el motor está evaluando.

{{7*7}}        <!-- si la página pinta 49, evalúa la plantilla -->
${7*7}         <!-- variantes segun el motor -->

Confirmada la evaluación, se escapa del sandbox del framework para llegar a JavaScript. En AngularJS moderno (>= 1.6, ya sin sandbox) basta con alcanzar el constructor de Function:

{{constructor.constructor('alert(1)')()}}

En versiones antiguas (1.5.x) el sandbox obliga a un escape más elaborado, publicado en PayloadsAllTheThings para cada versión concreta. Si además la web filtra ciertos caracteres del mensaje (un alert('PWNED') que no dispara), se evita el filtro construyendo la cadena con String.fromCharCode, que no contiene comillas ni letras sospechosas:

{{constructor.constructor('alert(String.fromCharCode(80,87,78,69,68))')()}}

Requisitos. La entrada debe reflejarse dentro de una región que el motor de plantillas de cliente procesa (no en un <script> cualquiera ni como texto escapado) y la página debe cargar un framework vulnerable. Identificar el framework y su versión (con Ctrl+U sobre el fuente, o por las clases ng-*/v-* del DOM) es el paso que decide qué payload de sandbox-escape usar.

Cómo detectarlo. Del lado servidor apenas hay rastro: la evaluación ocurre en el navegador. La firma está en el tráfico de entrada, donde un WAF marca los {{...}}, constructor.constructor o String.fromCharCode en parámetros que acaban renderizados. En el cliente, una CSP estricta (script-src sin unsafe-eval) rompe la mayoría de estos escapes y su bloqueo queda registrado como violación de CSP, un indicador fiable de intento.

Defensa. No mezclar datos del usuario con plantillas de cliente: tratar la entrada como texto con el binding seguro del framework (interpolación que escapa, nunca v-html ni compilar plantillas dinámicas), mantener el framework actualizado (los sandbox antiguos son inseguros por diseño) y aplicar una CSP sin unsafe-eval. Como la CSTI desemboca en ejecución de JavaScript, la defensa se solapa con la del XSS.

HerramientasXSStrike