What Your (Website) Style Says About You

March 10, 2022 in Behind the Code

Content stylesheets (CSS) are the primary way that websites tell a browser how to shape, color, and indent the content of a website and what fonts to use for the text. But did you know that malicious actors can also use CSS stylesheets to add malware to your site?

In this article, we look at a simple and obvious stylesheet injection attack and discuss how this could have been a lot worse.


During an investigation, we discovered that a block of JavaScript was performing some questionable operations. It appeared, briefly, to be creating a new stylesheet within the rendered page and then using it, along with a large array of integers, to do “something”. In terms of an HTML page, a “stylesheet” is any <link> tags with the rel=”stylesheet” attribute and any text between <style> and </style> tags within the page. In JavaScript, you can access these stylesheets through document.styleSheets, which is an array of all the <link> stylesheets and <style> tags in a page; and, in combination with document.createElement, you can add dynamically created stylesheets to that array and pull it out later with the cssRules and cssText calls on the document object.

The Injection

Code Analysis

The first thing of note in this injection is a huge array of numbers, each of which is followed by /t, without any quotes around them. This means the array is not a set of strings but rather a set of mathematical operations.

Array of math operations

Figure 1: Array Of Math Operations

The next important part is at the beginning of the script block, a function named createCSS. With the lack of carriage returns in the original code, it might be a little difficult to read, but after prettying it up, we have a much better idea of what it is doing.

Original functioning code

Figure 2: Original Function Code

Easy to read code

Figure 3: Easy-to-Read Code

Above we see that the createCSS function receives two arguments: a variable named “selector”, and another named “declaration.” The function begins by taking the browser UserAgent and converting it to all lowercase characters before checking for the presence of three strings. In this case, it is looking for “msie” in the UserAgent string as well as “win” and it is making sure that the string “opera” is not present in the UserAgent. This is essentially a check to see if the browser is an older version of Windows Internet Explorer, setting the “isIE” variable to true if it is an older Internet Explorer and setting it too “false” if it is anything else. It then creates a new style element and, if isIE is false, it creates an HTML stylesheet entry, using the selector and declaration passed in, that gets inserted into the page immediately after the <head> element. If isIE is true, however, the code checks to see how many stylesheets there are. It then finds the last stylesheet and tries to add a new rule to that sheet using the selector and declaration.

So far, this is just odd, but nothing terrible. However, after this point, the code shows its true colors and we can say, with 100% certainty, that this is a malicious injection even before we decode the payload.

There is no way this is good code

Figure 4: There”s no way that this is “good”

Now that the function is defined, the injected JavaScript makes use of it by calling it to set up a new “stylesheet”. The selector is set to “#va” and the declaration is set to “background:url(data:,String.fromCharCode)”. The selector is nothing unusual, just a string that will trigger the declaration values on any element with the id attribute set to “va”, but the declaration itself is a red flag. First, it is setting a background URL to something that is not a URL or URI. Instead, it is setting it to a type-less data object. Setting a background URL to a data object is nothing special, but it is almost always done when the data object is an encoded image, in which case it would have the data tag following by a MIME type such as image/gif followed by a semi-colon and the image as an encoded string. Without a MIME type, the data tag is left incomplete. Even more interesting is that there is a comma following the data tag and then the JavaScript function “String.fromCharCode”.

Uh oh. This is NOT looking good.

If we continue, the code initializes a new variable (ucyq) and then sets another variable to the array of stylesheets present in the page, before entering a loop that will iterate over those stylesheets. As it iterates over the stylesheets, the code sets the “vpm” variables to all the rules for the stylesheet and iterates over those rules. For each of the rules in a sheet, the code checks to see if the selector contains “#va”. It skips all selectors that do not contain the string. If the rule selector contains “#va”, it sets the zio variable to the name of the selector without the “#” and the “ucyq” variable to the part of the declaration that starts with a capital S and continues to the end of the declaration. In the code sample above, that means zio is set to “va” and ucyq is set to “String.fromCharCode”.

This is followed by a curious two lines of code that get the seconds from a Date object and then the huge array mentioned in the beginning.

A specific date object with number of seconds.

Figure 5: Create a specific Date object and get the number of seconds

The action of assigning the seconds from this Date object to the variable t is illuminating, however. The seconds are represented by the last argument to the “new Date()” call, in this case, 4, which means that the array is dividing all the values by 4 and brings them well within the range of values for ASCII characters (0-128).

The final section of code simply confirms this is malicious JavaScript that needs to be removed.

The final code block, where the magic happens.

Figure 6: The Final Code Block, Where The Magic Happens

Here, the code is initializing a new variable, fme, to an empty string and creates a function reference, g, that simply returns the arguments passed. Then, the code sets the tevq variable to the concatenation of “e,” the value of the zio variable, and “l”. Since we know that zio is the string “va”, we now know that tevq is the string “eval”. Next, another variable, cet, is initialized to an empty string and the variable hj is set to the eval of the ucyq variable. This means that hj is now the same as “String.fromCharCode”. Lastly, the code iterates over the large array, evals each element to perform the math, and appends the character to the cet variable, before eval’ing the resulting string.

This is 100% malicious even before we look at the payload.

The Payload

It is worthwhile to mention that even though the code itself is malicious and needs to be removed, it can be helpful to understand what it was attempting to inject.

If we change the eval at the end to print out the code rather than executing it, we see that it creates this additional chunk of malicious JavaScript.

The malicious code itself

This code checks to see if there is a pre-existing body tag in the page. If there is one, the code simply creates an iframe element, sets the source to the URL specified, makes it hidden, and appends the iframe to the body tag. If there are no body tags, the code creates a new body and, if successful, executes that same code. If it was not able to create a body tag, the code simply writes out an iframe tag with identical properties.

In the sample that we encountered above; the IP address (belonging to Vodafone Portugal) was no longer responding to requests.


The Good News

The good news is that this is much easier to detect because of the large array of values that are included as the payload. Additionally, the fact that the supposed CSS is clearly not a stylesheet means that you, as a website owner, can feel confident that you can safely remove the script block that this was in. Overall, this injection was dangerous but easy to spot. However, in combination with the techniques we found in last month’s article, this could have been significantly harder to detect.

The Bad News

The bad news is that web applications are filled to the brim with resources that are located on other servers. Whether it is Google Analytics, Google Fonts, WordPress images, or any number of other services, you will find <link> tags in websites that pull those resources in. A malicious payload could easily be encoded in a stylesheet referenced by a link tag rather than constructed by JavaScript. Similarly, the processing code to turn that payload into actionable code could easily be in JavaScript referenced by a script tag rather than injected into an existing page. With some slick packaging, it could even end up getting distributed through normal CMS channels and made available to unsuspecting users to willingly install on their websites.

The Better News

SiteLock is here to help. Whether it is our informative blog articles or our knowledgeable support staff, SiteLock is here for you if you have questions or need assistance. If you think your website has been infected by malware, give us a call and talk to an agent today.

About The Author

Maarten Broekman has worked as a system administrator and systems engineer for over 25 years, primarily in the shared web-hosting space. One of the main concerns for web-hosting providers is being able to serve their customers’ websites as quickly and efficiently as possible. As a result, anything that detracts from performance needed to be examined closely and this is where his interest in malware and code analysis sprang from. For over a decade, finding, decoding, and removing malware (and automating that process) has been his primary focus.

Latest Articles
Follow SiteLock