[SSTI] Breaking Go's template engine to get XSS


Hi, I recently started working with Go and decided to play around with it's in built packages for web related stuff. It's a very neat language which is easy to learn and implement. While looking at the web development side of the language, I came across the package for templates. My first thought was to see if I could get an SSTI to work in it, but sadly I couldn't find any resources online. Through some thorough reading of the templating documentation and some bypasses, I was able to successfully achieve XSS. This blogpost signifies the process of finding and exploiting an SSTI in a server written in Go.

Introduction to Go's templating engine

Go provides two templating packages. One is text/template and the other is html/template. The text/template package has no protections for XSS or any kinds of HTML encoding whatsoever. This isn't suited for building web apps, but instead for use in different apps which don't require processing of HTML. The second package, html/template is basically the same as text/template, but with added security protections like HTML encoding and so on.

For sake of understanding the exact templating used, the following code will be used to demonstrate further attacks:

Detecting and confirming

Detecting SSTI in Go isn't as simple as sending {{7*7}} and checking for 49 in the source code. Our first step is going through the documentation to find behaviour in templates that is native only to Go- this is done so as to confirm the backend language so that we can focus our payloads only in context of that language. The way to confirm that the template engine used in the backed is Go, following is a non-exhaustive list of payloads and their respective outputs:

  • {{ . }} 

This will result in an output containing the data struct being passed as input to the template, which in our case is the user1 struct. This can be thought of as the equivalent of {{ self }} in other templating engines. The output in our case would be something like: {1 [email protected] ameya#123}

  • {{printf "%s" "ssti" }}

In case the above payload doesn't output anything, we can use this payload, which should simply output the string ssti in the response.

  • {{html "ssti"}}, {{js "ssti"}} etc.

These are a few other payloads which should output the string "ssti" without the trailing words "js" or "html". You can refer to more keywords in the engine here.

Exploitation

If you have confirmed the above payloads to match Go's behaviour, you can now proceed to exploit it to achieve XSS. If the server is using the text/template package, XSS is very easy to achieve by simply providing your payload as input. However, that is not the case with html/template.

When the server uses the html/template package, all HTML entities are encoded. So, in our case if you try to send /?q={{"<script>alert(1)</script>"}} the response will be rendered to &lt;script&gt;alert(1)&lt;/script&gt;. Even going through the documentation doesn't help much as there is no way to render any of our XSS payloads without the default encoding behaviour. However, Go allows to DEFINE a whole template and then later call it.

`{{define "T1"}}ONE{{end}}{{template "T1"}}`

Using the above behaviour, we can introduce an XSS payload in a template, and then get it rendered by simply calling it.

The payload will be something like:

`{{define "T1"}}<script>alert(1)</script>{{end}} {{template "T1"}}`

Using this, you should be able to bypass the inbuilt HTML encoder of the html/template package and successfully achieve XSS.

Popular Posts