A subtle web application vulnerability, CVE-2023-24539, illustrates how inserting *non-dangerous* characters like angle brackets (< and >) into CSS contexts can unexpectedly open the gate to HTML injection. In particular, this happens when web templates process multiple actions—separated by a /—within a single operation. Even if the template engine assumes certain characters are safe in CSS, tricky input can cause escaping to fail and new HTML to be injected. In this article, we break down how this happens, walk through code and exploit examples, and provide links for further reading.

The Problem in Simple Terms

Common wisdom says if user input ends up inside a <style> tag, you only have to be careful with characters like ', ", and CSS tokens. But what if your template lets you run several actions at once and lets you separate them with /? Then some combinations—even those *with* angle brackets, which aren’t usually dangerous in CSS—can pop you out of the <style> block entirely and back into the HTML document!

A template injection vulnerability occurs if developers allow untrusted user input to control parts of the template, and if the template engine isn’t properly escaping inputs within the right context.

Let's take a simplified Go HTML template as an example

// Go's html/template package
const tpl = `
<style>
  .username:after { content: "{{.UserInput}}" }
</style>
`

data := struct {
    UserInput string
}{
    UserInput: evil&quot;}&lt;/style&gt;&lt;script&gt;alert(1)&lt;/script&gt;&lt;style&gt;,
}

err := tpl.Execute(os.Stdout, data)

What’s the Risk Here?

- If UserInput comes from untrusted data (say, a query param), and the template engine simply escapes some characters (but not angle brackets), you could *break out* of the CSS, *close* the style tag, and *inject* arbitrary HTML or JS.

- Notably, the } character here is crucial: it closes the CSS rule. Then the injected </style><script>alert(1)</script><style> closes and reopens style blocks, injecting a script tag in the process.

- In many template engines, the / action separator means multiple templates/logic chunks can appear side by side, further confusing context boundaries.

Suppose a web service shows your username with custom styling using a template

<style>
  .user:after { content: "{{.username}}" }
</style>

If a user sets their username to

"} </style><img src=x onerror=alert(1)><style> 

And the template engine doesn’t properly escape or context-isolate values, this outputs

<style>
  .user:after { content: "} </style><img src=x onerror=alert(1)><style>" }
</style>

The browser processes

- Closes style block with }</style>

Why Angle Brackets Aren’t Always Safe

Template libraries often escape < and > inside HTML, but *not always* inside CSS or JS blocks. Angle brackets in CSS are harmless *normally*, but with sloppy template separation they can help attackers close the current context and escape.

Multiple action separators (think {{.a}}/{{.b}}) make the context-tracking even harder, and mistakes easier.

Cross-site scripting (XSS)

- Arbitrary HTML/JS injection

Bypassing filters simply by using context trickery

- This was especially a risk in Go's text/template and html/template packages, but also shows up in Django, Ruby ERB, and others if context is lost.

References and More Reading

- CVE-2023-24539 at NVD
- Go Security Advisory: "html/template: actions in style blocks are not properly context-aware"
- Security Issue Report (Google OSS-Fuzz)
- OWASP CSS Injection Cheatsheet
- PortSwigger Research: Template Injection

How to Fix and Prevent

- Never trust user input in any template context. Always use the right encoding/escaping *for the context* (CSS, JS, HTML, URL, etc).

Use robust context-aware escaping libraries

- Audit any use of action separators (/) or complex template logic

Summary

CVE-2023-24539 is a reminder: security in templates is all about *context*. Characters considered "not dangerous" in one context may allow subtle exploits in another, if engines lose track of where they are. Don't feed user input into your templates unless you're sure you're escaping *for the right language*—especially in <style>, <script>, and with template action chaining.

Stay secure, and keep your template engines patched!


If you want to try it out or check your code, see the Go Playgrounds with affected templates and more details in the upstream bug report.


*Written exclusively for you for a deep, practical understanding of a tricky CSS template escape bug—CVE-2023-24539.*

Timeline

Published on: 05/11/2023 16:15:00 UTC
Last modified on: 05/22/2023 18:22:00 UTC