Earlier this week I saw this tweet by @Sansec float by:
Sansec@sansecioAfter finding skimmers in SVG files last week, we now discovered a #magecart skimmer in perfectly valid CSS. It is parsed and executed during checkout. Malware loaded from cloud-iq[.]net (faking @cloudIQApps)10:13 AM - 09 Dec 2020
This one is pretty nice I must say, and a bit hard to find:
-
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!'); }
-
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'
;"
>
It works as follows:
-
https://cpwebassets.codepen.io
andhttps://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)
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
orFunction
, they already own your site. The fact the payload happens to be stored in a CSS custom property has nothing to do with it.