Skip to content

Template Injection

Template Injection

Description

Template Injection is a vulnerability that allows an attacker to inject malicious code into a template-rendered text, which can then be executed by the server. This can lead to a range of attacks, including data theft, privilege escalation, and remote code execution. Template Injection attacks are particularly dangerous because they can be difficult to detect and can offer dangerous capabilities to an attacker.

Here, the code is vulnerable because we're creating the template string using direct concatenation from the user input (userName), allowing the user to control the structure of the template. This can lead to template injection if the user provides a string that contains Mustache template tags.

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)
}

Recommendation

To mitigate this vulnerability, it is important to ensure that all user input is properly sanitized and validated before being passed to the server-side template engine.

Template engine will typically have a render method that takes a context that will be safely embedded in the text.

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)
}

Standards

  • 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