DEV Community

Cover image for Injecting a JavaScript Attack Vector using CSS Custom Properties
Bramus!
Bramus!

Posted on

Injecting a JavaScript Attack Vector using CSS Custom Properties

Earlier this week I saw this tweet by @Sansec float by:

This one is pretty nice I must say, and a bit hard to find:

  1. As the syntax for CSS Custom Properties is overly permissive you can use Custom Properties to store your JavaScript attack vector in.

    :root {
      --script: console.log('Attack!');
    }
    
  2. If you then use window.getComputedStyle to extract the contents out of the Custom Property and combine it with a function constructor and an IIFE, it’s possible to execute it.

    const scriptContents = window.getComputedStyle(document.documentElement)?.getPropertyValue('--script');
    new Function(scriptContents)();
    

Here’s a pen that loads a remote confetti script using the method described:


To prevent this remote evaluation, you'll need a proper Content Security Policy to do so. It took me some time to figure out — as I’m no CSP expert — but turns out the unsafe-inline keyword in the CSP’s source list is enough to block the execution of the JS-IN-CSS.

As a reminder, here are the four allowed keywords:

  • 'none', as you might expect, matches nothing.
  • 'self' matches the current origin, but not its subdomains.
  • 'unsafe-inline' allows inline JavaScript and CSS.
  • 'unsafe-eval' allows text-to-JavaScript mechanisms like eval.

I first thought unsafe-inline would be insufficient here as the code does not call eval, but apparently a function constructor is (correctly!) considered equally harmful, and therefore also blocked.

Here’s an updated demo that blocks the script evaluation:

The CSP used is this one:

<meta
  http-equiv="Content-Security-Policy"
  content="script-src 
    https://cpwebassets.codepen.io
    https://cdpn.io
    https://cdn.jsdelivr.net
    'unsafe-inline'
  ;"
>
Enter fullscreen mode Exit fullscreen mode

It works as follows:

  • https://cpwebassets.codepen.io and https://cdpn.io are there for the CodePen demo to work
  • https://cdn.jsdelivr.net is there to allow legitimate loading of scripts — such as a jQuery you might need — from that CDN.
  • unsafe-inline is the one that prevents the execution of the JS-IN-CSS defined script by blocking the call to the function constructor.

Now that calls for confetti! 🤪


💁‍♂️ This post first appeared on my blog bram.us. Want to stay in the loop? Here's how to do so:

Top comments (1)

Collapse
 
lionelrowe profile image
lionel-rowe

I'm not sure if this is intentionally clickbaitey, but there's nothing new or particularly special about this attack vector. If an attacker can inject the code containing the eval or Function, they already own your site. The fact the payload happens to be stored in a CSS custom property has nothing to do with it.