[HackerNotes Ep.79] The State of CSS Injection - Leaking Text Nodes & HTML Attributes

We're back with some weekly security including RCE via browser extensions, CSPT and a metric ton of CSS injection tips, tricks and techniques for you.

Hacker TLDR;

  • Universal Code Execution - Browser Extensions: Some pretty critical research by Spaceraccoon on gaining RCE through browser extensions. The TL;DR on the exploitable conditions for this:

    • Content Script Injection: The extension must inject content scripts into web pages.

    • Insecure postMessage Handling: Content scripts accept postMessage without validating the origin.

    • Message Forwarding: Background scripts forward messages from content scripts using chrome.runtime.sendMessage().

    • Sink: Extension improperly handles input, allowing code execution on the host system.

    • More details below and the original research can be found here: https://spaceraccoon.dev/universal-code-execution-browser-extensions/

  • CSPT To XSS: If you’re on a target running WordPress and you have a CSPT that is reflected in some form of <script src> context, abuse the /wp-json/?_jsonp= endpoint to turn the CSPT into XSS.

  • Full-time Bug Bounty Blueprint: Justin has launched the Full-time Bug Bounty Blueprint. Check it out here: https://www.criticalthinkingpodcast.io/p/how-to-go-full-time-bug-bounty/

  • CSS Injection: CSS Injection writeups, sequential import chaining, smuggling protected DOM contents, blind CSS injection and font ligature vectors. Check it out and a BUNCH more below.

Find out more at: https://www.criticalthinkingpodcast.io/tlbook

This episode is sponsored by ThreatLocker. Check out their eBook "The IT Professional's Blueprint for Compliance" here!

Universal Code Execution - Browser Extensions

Spaceraccoon has dropped some more research, this time hitting browser extensions. If you haven’t checked out some of SpaceRaccoons work before, I highly recommend checking the full blog out here: https://spaceraccoon.dev/about/

This research is quite a cool balance between web hacking and browser API hacking. By abusing messaging APIs in browsers along with browser extensions, the research demonstrates it’s possible to jump from web pages to RCE.

In order to get to that point though, we have to look at some fundamentals in Chrome extension architecture. Let’s dive in.

Content scripts: Scripts which get added to every page by the extension that is in the scope of the extension. They:

  • Have access to the DOM, but doesn’t have access to JS variables on the page.

  • Are injected into web pages via manifest.json, isolated from the web page context.

  • A common way to communicate with these is via postMessage.

  • Uses chrome.runtime.sendMessage() to communicate with background scripts.

Messaging APIs: These are what allow the in-browser communication to happen between web pages to extensions. Examples of these include:

  • postMessage(): Allows communication between web pages and content scripts. Vulnerable without proper origin validation.

  • chrome.runtime.sendMessage(): Used by content scripts to send messages to background scripts.

  • chrome.runtime.onMessage.addListener(): Background scripts listen for messages from content scripts.

Now, this research abuses a few conditions for the attack to work. In order to achieve RCE, the attack chain needs:

  • Content Script Injection: The extension must inject content scripts into web pages.

  • Insecure postMessage Handling: Content scripts accept postMessage without validating the origin.

  • Message Forwarding: Background scripts forward messages from content scripts using chrome.runtime.sendMessage().

  • Sink: Extension improperly handles input, allowing code execution on the host system.

This might seem like a complex attack chain at first and you may wonder the odds of this happening. Turns out, there are a number of extensions that were vulnerable. Using The https://github.com/palant/chrome-extension-manifests-dataset, around 230 potential candidates were found. This combined with Semgrep rules helped find a few high-signal targets.

Solid research. The write-up also includes common patterns to search for in Chrome extensions and some Semgrep rules. Check it out here: https://spaceraccoon.dev/universal-code-execution-browser-extensions/

CSPT To XSS

Now you may have seen a lot of chatter about CSPT lately. If you’re hunting for CSPT on WordPress sites, we have a good one for you from Isira Adithya to escalate CSPT into XSS in some contexts.

If you find a CSPT which is being reflected in a <script src> context, use the /wp-json/?_jsonp= endpoint to traverse to the endpoint with a payload, resulting in XSS. Quite a cool little gadget!

Something which will pay big dividends is to find a gadget which allows you to arbitrarily control a JSON response in Drupal. WordPress or something similar. A gadget that allows you to specify arbitrary JSON as a query parameter which then gets reflected in the response will be MASSIVE for CSPT escalations.

Full-time Bug Bounty Blueprint

We already had an announcement on this here but it deserves another special shout-out. The full-time bug bounty blueprint had been dropped for the Critical Thinkers discord channel. Check it out below:

CSS Injection

CSS Injection. If you listened to this week's pod, you could probably tell it kept Justin up a lot of the night filling his mind with exploit possibilities.

CSS Injection allows you to inject CSS into a page and achieve exfiltration of that page's (or part of the page's) contents. The attack vectors get incredibly creative, with various CSS methods which can be abused to exfil, and even abusing types of fonts themselves (font ligatures, more on this shortly) as a means of exfiltration.

Some things to consider with CSS injection:

  • Only require a user to browse to a payload to begin exfil

  • CSPs can mitigate CSS injection.

If this is a relatively new topic for you, three blogs you MUST read to get a handle on CSS Injection:

CSS Injection - Sequential import chaining

One nice tactic to know when it comes to CSS injection which can turn some unexploitable scenarios into exploitable ones is sequential import chaining. Typically, a problem with CSS injection is having to constantly reload an iframe or page to exfil and load a new font or so on.

Sequential import chaining is a method which allows you to avoid reloading by:

  1. Inject an @import rule to the staging payload

  2. Staging payload uses @import to begin long-polling for malicious payloads

  3. Payloads cause the browser to call out using background-img: url(...) causing the next long-polled @import rule to be generated and returned to the browser.

So you have the import of a CSS stylesheet, and that stylesheet does an import @import of another remote stylesheet and does some CSS rules to trigger a callback.

On the server side, you don’t respond to the second style sheet import until you have the callback from the first style sheet import. The stylesheet import will mean you have smuggled out the first character.

A tool was dropped to help exploit this kind of behaviour ‘sic’ by Donot here: https://github.com/d0nutptr/sic

The full blog post for this method by Donut is also detailed here: https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b

Blind CSS injection

Now there’s even such thing as Blind CSS injection by, of course, Gareth Heyes. This is quite a cool piece of research demoing a variant of CSS injection which doesn’t rely on JavaScript and uses various types of CSS selectors to trigger HTTP requests to an external server to smuggle data out.

The use case here is primarily in the context of a hardened target, with something like a strong CSP or strong filtering in place.

Full POC videos, writeups and even labs are available with this one and it’s from PortSwigger, so it’s going to be good. Check it out here:

Font ligature CSS Injection

Font ligature vectors in CSS injection are a pretty awesome variant. Using custom fonts with ligatures attackers can create a custom font where ligatures (character sequences) are mapped to specific custom glyphs.

It’s also important to note you can define any length of font ligature. So if you want a ligature that maps s3cr3tk3y to a unique glyph, you could do that.

A quick TL;DR of the steps involved for font ligatures in CSS injection:

  1. Custom Fonts: Attackers create a custom font where ligatures (special character sequences) are mapped to specific glyphs.

  2. CSS Injection: Malicious CSS is injected to apply the custom font to target elements.

  3. Character Encoding: Sensitive data (e.g., tokens) are rendered using the custom font, converting character sequences to unique glyphs.

  4. Exfil: The rendered glyphs will trigger a scroll bar in the browser due to having an intentionally set huge width. This scroll bar can then by styled with a background image, and abused to make HTTP requests (via background images or other methods) to smuggle the character out.

If you want to dive into some more research on exactly how this happens with some examples, check these blogs out:

Smuggling Isolated DOM Elements

I had no idea this functionality existed, but there are projects out there that can be used to completely isolate parts of the DOM, meaning JS can’t read it. Seems to resemble something like magic on the surface, but fortunately, with CSS injection, any protected parts of the DOM can be leaked.

Projects like LavaDome were created off of the premise: ‘..

Under today's web standards, there is no established way to selectively isolate DOM subtrees in a secured manner. In other words, we can't control access to sections of the DOM by granting access for some parties while blocking access for others if they share the same JavaScript execution environment.

We live in a world where we can no longer trust the code in our own apps, and same-origin execution does not guarantee safety. To secure secrets in the frontend, we must be able to present content to the user while ensuring that it cannot be compromised by JavaScript code running under the same origin.’

Interesting mission statement for a project right?

Unfortunately, there are active methods of bypassing some of these protections using CSS injection. Using CSS injection variants, and in some cases, some native APIs, a few POCs are kicking around providing means of exfiltrating protected DOM content:

CSS-based bypasses:

JS-based bypasses:

You may recognize some of the CSS-based bypasses via the font ligature techniques by Kinugawamasato. He dropped a thread on this on X covering the technique too (Google translate if you can’t read Japanese, like myself):

So, to summarise we now have a few tools we can use for the exfiltration of different types of data in different types of locations:

  1. For text nodes: (think <h1>the data in here</h1>) - Sequential Import Chaining + Font ligature + Scroll bar -> background-image to exfil on a character by character basis

  2. For HTML attributes: (think <a href="data/I/want">) - Sequential Import Chaining + CSS attribute selectors (a[href^=d])background-image to exfil on a character by character basis

CSS Exfil Repo

If you’re looking for an entire repo covering a tonne of different CSS exfiltration methods, including most of the ones above, PortSwigger has yet again delivered. Check out the repo for a bunch of different payloads and proof of concept vulnerable pages to try them on here:

If you’re looking to sink your teeth into some more advanced CSS content, detailing a lot of these techniques + more and exploiting them in the wild, check these two posts out below:

A very heavy CSS injection-oriented HackerNotes this time around. We can thank Justin for driving that.

As always, keep hacking!