Earlier this week I saw this tweet by Sansec float by:
After 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) pic.twitter.com/Hy6b6lxJoL
— Sansec (@sansecio) December 9, 2020
This one’s pretty nice I must say: as the syntax for CSS Custom Properties is overly permissive (see here) you can use Custom Properties to store your JavaScript attack vector in. If you then use window.getComputedStyle
to extract the contents of the Custom Property (see here) and combine it with a function constructor and an IIFE, it’s possible to execute it.
Here’s a pen that loads a remote confetti script using the method described:
Let this underline the importance of a Content Security Policy to prevent remote script loading script evaluation.
Update: Blocking this “hack” with a proper CSP
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 likeeval
.
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:
See the Pen
Injecting a JavaScript attack vector using CSS Custom Properties (with CSP) by Bramus (@bramus)
on CodePen.
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 workhttps://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
That calls for confetti! 🤪
Thank me with a coffee.
I don\'t do this for profit but a small one-time donation would surely put a smile on my face. Thanks!
To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.
Hey Bramus!
Came to your site after the recent checking on the your recent houdini.how update. Anyways, great site I’ve been here before as I regularly browse quality sites to get ideas and learn new topics.
I learned about this a week ago or so myself (I think from a js newsletter) but after seeing your clear demo decided to give it a try as I can just copy/paste the code to a temp site and test.
Your original and linked CSP article is correct that the following will work to block this attack:
However that will also block all images. A modified version:
Would block just the script. If you try that and view in Chrome it shows:
"Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'"
.”The most restrictive CSP:
blocks this as well
Thanks for your kind words and input Conrad 🙂
The CSP post I linked to contained an exemplary CSP rule. What I’m more curious about is a CSP that would both allow external scripts (we might still need a legitmate jQuery from
https://cdn.jsdelivr.net/
for example) and also block the passing of custom property data from the CSS to JS.I’m not too sure if an
unsafe-inline
— which is a bit more restrictive thanunsafe-eval
— will do in that case, as there’s no call toeval
happening…Will also try and take a stab at it … perhaps I can find a working combination.
PS: Fixed that formatting for you there 😉
UPDATE: Took me some time to figure out, but found a matching CSP. I’ve updated the post accordingly.