I'm doing a pentest and came by this code:

   (function() {
        var subdomain = (function() {
          var query = /[?&]css=([^&#]*)/i.exec(window.location.search);
          if(query) {
            return query[1];
          var URL = window.location.host.split('.');
          if (URL.length > 1) {
            return URL[0];
        if (subdomain) {
          var link = document.createElement('link');
          link.rel = "stylesheet";
          link.href = "/" + subdomain + ".css";

It adds a CSS file to the header for rebranding.
The target URL looks like this:
We have controlled the subdomain parameter here which equals to either query or URL[0].
query is the result of /[?&]css=([^&#]*)/i.exec(window.location.search); which equals to custom in this case.
if the query is not available in URL, then URL[0] equals to some here, I can't think of doing anything useful using URL[0] because, in order to control its value, I have to change some to something else which completely changes the URL and points to some other irrelevant page.
Anyway, The final CSS URL would be "/custom.css" or "/some.css" if the CSS parameter is not available.
I tried some payloads to exploit this but all failed.
Any ideas if this code is vulnerable and how can it be exploited?

  • 1
    //domain.tld/bad.css is a potentially valid url. no xss, but a css injection potential.
    – dandavis
    Sep 10, 2018 at 16:03

3 Answers 3


Yes, this code is vulnerable, but not to XSS. The subdomain variable's value can indeed be controlled by an attacker, but that variable is only used to set the href parameter of a CSS stylesheet; which won't accept JavaScript code. However, controlling this value does still allow CSS injection.

The stylesheet's href is prefixed by /, but an attacker can still point it to an arbitrary page by adding another slash to form a protocol-relative URL pointing to the attacker's server. (E.g. //evilsite.com/payload.css.) This would allow the attacker to get complete control over the site's appearance, if not it's functionality, which could be used to trick users into taking actions they wouldn't otherwise take. (For example, add a banner telling users they need to reset their password, then style the user settings page to make their public profile description field look like the input box for "current password".)

  • CSS might contain executable code - not for all browsers, but still.
    – Bergi
    Sep 10, 2018 at 21:46
  • @Bergi Interesting!
    – Sam
    Sep 16, 2018 at 12:10
  • So if the code was something like "/css/" + subdomain + ".css" was it still vulnerable to CSS injection?
    – Sam
    Sep 16, 2018 at 12:11

Any ideas if this code is vulnerable and how can it be exploited?

As mentioned in the comments, this code could be vulnerable to CSS injection.

For example, if the URL looks like:


Then this javascript will create a new link in the header like:

link rel="stylesheet" href="//evil.com/more_evil.css"

Which is a valid URL (even without the scheme http/https), which can access the remote CSS file.

  • You could avoid this specific issue by changing the regex to: /[?&]css=([a-zA-z]*)/
    – hft
    Sep 10, 2018 at 16:35

It is vulnerable but you can solve that easily with whitelisting:

var allowedBrands = ['brand1', 'brand2', 'brand3'],
    query = /[?&]css=([A-Za-z0-9]*)/i.exec(window.location.search);

if (query && allowedBrands.indexOf(query[1])) {
    return query[1];
} else {
    return allowedBrands[0]; // this is the default value
  • That's one way to do it, but I see two potential issues here, First, the list can get quite large, say for example 15000 or more "brands", and second, it is leaking information about all customers who are using this service. Your answer is technically correct, but I guess there are some considerations that must be taken into account.
    – Sam
    Sep 16, 2018 at 12:17
  • That's right, the best way I think is ok is to do the request directly (but with a secure Regex) into the server (that uses the same Regex) and let it validate whether the file exists to return a fallback if is needed, so the whitelist is the filesystem and no extra code is needed. Sep 17, 2018 at 15:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .