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.