[HackerNotes Ep. 68]: 0-days & HTMX-SS with Mathias

Mathias is back with some fresh HTMX research, including CSP bypass using HTMX triggers, converting client-side response header injection to XSS, and bypassing HTMX disable. Some cool behaviour in Cloudflares image optimization functionality is also dropped to achieve partial open redirect.

Hacker TLDR;

  • HTMX Bypasses

    • CSP Bypass: HTMX triggers can be abused to bypass CSP’s via <img src=x hx-on:htmx:load='alert(0)' /> - full writeup below.

    • Client-side response header injection to XSS: HTMX uses certain headers to help instruct the framework for certain behaviours. This can be abused via HX-Redirect: javascript:alert(1) for XSS if you can inject a response header.

    • Bypassing hx-disable: hx-disable is supposed to disable any HTMX functionality within the children of the given tag. This feature can be bypassed by using newer syntax to call HTMX triggers via <div hx-disable><img src=x hx-on:error="alert(4)"></div>

    • XSS-ing impossible elements: Due to the way triggers are processed in HTMX, you can achieve XSS in elements which usually aren’t possible to achieve XSS with. For example: <meta hx-trigger='x[1)}),alert(3);//]'>

  • CDN-CGI Gadgets: If the image optimization feature is enabled for a subdomain, it can be abused to cause redirects to arbitrary subdomains of the TLD: https://avlidienbrunn.se/cdn-cgi/image/onerror=redirect/http://anything.avlidienbrunn.se

HTMX Research

Overview

HTMX essentially gives you access to a few things directly in HTML - think AJAX, WSS, and JS. It allows you to build out features that you would need JS for but in HTML.

If you haven’t come across it before, a high-level overview taken from the docs https://htmx.org/docs/:

You can think of it as combining all of the HTML, JS and so on on the server side instead of the clientside (like the old days).

With that being said, let's jump into some cool research from Mathias.

CSP Bypass

When using HTMX and you want to use its triggers such as hx-on it would be equivalent to an onload event. Under the hood, it’s using eval internally, so if you’re using HTMX with triggers alongside a CSP you have to have unsafe-eval enabled in your CSP directives.

This becomes a global CSP bypass as you can replace it with an HTMX equivalent. A sample payload for the HTMX equivalent: <img src=x hx-on:htmx:load='alert(0)' />

Checking the documentation, we can see the below hx-on attribute classified as deprecated with some slightly different syntax:

Interestingly, you can define multiple handlers at once by placing them on a new line. This probably means the new line character can be abused to achieve XSS by adding additional handlers in certain contexts.

Equally, if you’re familiar with Angular you might be familiar with this CSP bypass via Angular’s events which is similar:

?search=<input id=x ng-focus=$event.path|orderBy:'(z=alert)(document.cookie)'>#x

A cool writeup which abused this functionality in Microsoft Teams to achieve XSS and bypass CSP: https://medium.com/@numanturle/microsoft-teams-stored-xss-bypass-csp-8b4a7f5fccbf

Turning client-side response header injection into XSS

If you can make HTMX fetch something cross-origin it's almost XSS by default. More commonly you have a situation where you control the path of something being fetched instead of the origin, which in some instances can be exploitable.

With HTMX, if there’s a path you control and you can also inject a response header, you can achieve XSS.

This is because there are certain headers in HTMX you can use to control the framework. In this specific case we can use the header below to redirect to a JavaScript URI:

HX-Redirect: javascript:alert(1)

This instructs the framework to perform a redirect but we instead supply a javascript URI, achieving XSS.

Bypassing hx-disable

hx-disable is supposed to disable any HTMX functionality within the children of the given tag. It’s also recommended in the docs to enable the ‘hx-disable’ feature. The feature is documented here:

However, the syntax of HTMX triggers has changed, so historically you’d use something like hx-on=error:alert(1) but it’s now hx-on:error=alert(1). This new way of specifying triggers bypasses the hx-disable feature, for example:

<div hx-disable><img src=x hx-on:error="alert(4)"></div>

Even when using DOMPurify this can be tricky to defend against. With HTMX you can optionally prefix all HTMX attributes with data- so they look like data attributes. Everything starting with data- is allowed in DOMPurify.

XSS-ing impossible elements

Triggers in HTMX are essentially JS functions. They are made by putting whatever input you have in the HTMX trigger with some parsing into an anonymous function and evaling until you get the function back.

A high-level overview of the process:

  1. Add the HTMX trigger attribute to the HTML element

  2. When it's processed, it concats your input with some other templated/anonymous function under the hood

  3. Evals it, takes the function and assigns it to the trigger

Because of this behaviour, you can break out of it to hit elements which otherwise wouldn’t be exploitable. For example the meta tag - <meta hx-trigger='x[1)}),alert(3);//]'> is a valid XSS payload which will execute.

All of this research was bred from Mathias’s research whilst developing a CTF with HTMX. One takeaway mentioned if you’re currently a CTF player thinking about going into bounties is having an instinct on a target to know when there is a bug versus when there isn’t.

A lot of times there’s weird behaviour on targets which you could sink a lot of time into but it doesn’t necessarily mean there’s an exploitable bug. In bug hunting, knowing when to move on to the next piece of functionality that stands out or even targets is essential to avoid sinking time into weird behaviour instead of finding bugs.

CDN-CGI Gadgets

Cloudflare image optimization is an optional feature that can be enabled per domain. If this is enabled, you get a local domain image proxy on the domain. You can also pass flags and attributes to tell the proxy how to scale the image for clients.

This can be abused as you can also pass a onerror handler which can be used to specify a redirect in the attribute, causing a 307 with the below:

https://avlidienbrunn.se/cdn-cgi/image/onerror=redirect/http://anything.avlidienbrunn.se

Nice gadget to add to the list. One thing to note is that these redirects are bound to the top-level domain, meaning you can only redirect to subdomains.

Lots of cool nuggets in this episode. Make sure to document them all for your next encounter with HTMX or Cloudflare.

As always, keep hacking!