Saltar a contenido

Template Injection

Inyección de plantillas (Template Injection)

Descripción

La inyección de plantillas es una vulnerabilidad que permite a un atacante inyectar código malicioso en un texto renderizado por una plantilla, el cual puede ser ejecutado por el servidor. Esto puede dar lugar a una serie de ataques, que incluyen el robo de datos, la escalada de privilegios y la ejecución remota de código. Los ataques de inyección de plantillas son particularmente peligrosos porque pueden ser difíciles de detectar y pueden ofrecer capacidades peligrosas a un atacante.

Aquí, el código es vulnerable porque estamos creando la cadena de la plantilla mediante la concatenación directa desde la entrada del usuario (userName), lo que le permite controlar la estructura de la plantilla. Esto puede conducir a una inyección de plantillas si el usuario proporciona una cadena que contenga etiquetas de plantilla Mustache.

import 'dart:io';
import 'package:mustache_template/mustache_template.dart';

Future main() async {
  var server = await HttpServer.bind(
    InternetAddress.loopbackIPv4,
    8080,
  );
  print('Listening on localhost:${server.port}');

  await for (HttpRequest request in server) {
    final userName = request.uri.queryParameters['name'] ?? 'guest';
    // Vulnerable to template injection due to template string concatenation
    final template = 'Hello, ${userName}';
    final output = Template(template, lenient: true, htmlEscapeValues: false)
        .renderString({});
    request.response
      ..write(output)
      ..close();
  }
}
import com.github.mustachejava.DefaultMustacheFactory
import com.github.mustachejava.Mustache
import io.ktor.application.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.http.Parameters

fun main() {
    embeddedServer(Netty, port = 8080) {
        routing {
            get("/") {
                val parameters: Parameters = call.request.queryParameters
                val userName = parameters["name"] ?: "guest"

                // Vulnerable to template injection due to template string concatenation
                val mf = DefaultMustacheFactory()
                val mustache: Mustache = mf.compile("template", "Hello, $userName")

                val writer = StringWriter()
                mustache.execute(writer, emptyMap<String, Any>())
                writer.flush()

                call.respondText(writer.toString())
            }
        }
    }.start(wait = true)
}

Recomendación

Para mitigar esta vulnerabilidad, es importante asegurarse de que todas las entradas de los usuarios se saneen y validen adecuadamente antes de pasarlas al motor de plantillas del lado del servidor.

El motor de plantillas generalmente tendrá un método render que toma un contexto que se integrará de forma segura en el texto.

import 'dart:io';
import 'package:mustache_template/mustache_template.dart';

Future main() async {
  var server = await HttpServer.bind(
    InternetAddress.loopbackIPv4,
    8080,
  );
  print('Listening on localhost:${server.port}');

  await for (HttpRequest request in server) {
    final userName = request.uri.queryParameters['name'] ?? 'guest';
    // Vulnerable to template injection due to template string concatenation
    final template = 'Hello, {{ username }}';
    final output = Template(template, lenient: true, htmlEscapeValues: false)
        .renderString({'username': username});
    request.response
      ..write(output)
      ..close();
  }
}
import com.github.mustachejava.DefaultMustacheFactory
import com.github.mustachejava.Mustache
import io.ktor.application.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.http.Parameters
import java.io.StringWriter

fun main() {
    embeddedServer(Netty, port = 8080) {
        routing {
            get("/") {
                val parameters: Parameters = call.request.queryParameters
                val userName = parameters["name"] ?: "guest"

                // Use a predefined template and pass untrusted data as values
                val mf = DefaultMustacheFactory()
                val mustache: Mustache = mf.compile("template", "Hello, {{name}}")

                val writer = StringWriter()
                mustache.execute(writer, mapOf("name" to userName))
                writer.flush()

                call.respondText(writer.toString())
            }
        }
    }.start(wait = true)
}

Enlaces

Estándares

  • OWASP_MASVS_L1:
    • MSTG_PLATFORM_2
  • OWASP_MASVS_L2:
    • MSTG_PLATFORM_2
  • PCI_STANDARDS:
    • REQ_2_2
    • REQ_6_2
    • REQ_6_3
    • REQ_11_3
  • OWASP_MASVS_v2_1:
    • MASVS_CODE_4
  • SOC2_CONTROLS:
    • CC_2_1
    • CC_3_4
    • CC_4_1
    • CC_7_1
    • CC_7_2
    • CC_7_4
    • CC_7_5
  • HIPAA_CONTROLS:
    • SECURITY212
    • SECURITY213
    • SECURITY255