• Home
  • /
  • Blog
  • /
  • All You Need to Know About CSS Keylogger

All You Need to Know About CSS Keylogger

Definition of a Keylogger

A Keylogger is a program that logs the user’s keystrokes without the user being aware of it. Generally, it is a type of a vulnerability attack used to compromise the security of the victim’s accounts.

The keylogger consists of a client side and a server side. The client side is responsible for logging the keystrokes and sending them to the server side, which they would use this info to compromise the victim’s security.

Hardware-based keyloggers

When we refer to “Keylogger” we generally mean a Software-based keylogger. It is worth mentioning that there is also a category of Keyloggers called Hardware-based keyloggers. Basically, they involve some hardware that intercepts the victim’s keyboard to transfer the keys being pressed as a sequence to the attacker. The most known example of these is “Keyboard overlays”, which consist of the attacker placing a keyboard on top of the ATM machine’s keyboard, for example, thus transferring the users’ pin codes while they think they are only pressing the ATM keyboard. Another example of Hardware Keyloggers is Keylogger connectors which are inserted between the keyboard jack and the CPU or in another way that is more discreet.

Software-based keyloggers

Software Keyloggers, on the other hand, can be implemented on various levels in the software stack, starting from the lowest level of Hypervisor-based or Kernel-based keyloggers, which are programs running underneath the Operating System or in the lowest level of it. Higher level software keyloggers are for example API-level keyloggers, which use the Windows API library to hook themselves to keystrokes events as they happen when the targeted application is launched. A more common example that is closer to our topic is the javascript keylogger, which is a malicious javascript tag that is injected into a targeted web page (typical man-in-the-middle attack) and listens to keystrokes events on the site to report them to the attacker.

Browser-based malware

Malware used to be malicious programs that are injected in the victim’s PC. However, the evolution of web technologies allowed malicious code to be injected into web pages, in what is called Man In Browser attack (MITB)

Javascript MITB

The entry point for a javascript MITB is this single line of code.

CSS Keylogger Script

When developers include a script from a third party in their web apps, they are actually giving full control to that 3rd party script. It can potentially do any of the following:

  • change the content
  • add some JS lines
  • read or change the origin of storage of data (for instance, if the script interferes with IndexedDB or the cache storage API, the attack may continue across the whole origin, even after you’ve removed the script)
  • make requests using user’s cookies
  • run heavy-processing code (such as cryptographic processes, i.e web mining…)
  • monitor every user interaction (including key logging of all sorts)
  • replace simple DOM elements with malicious ones

CSS Keylogger

In this article, we are interested to present pure CSS keyloggers, i.e. that don’t depend on javascript or any other scripting language. However, the same concept of man-in-the-middle used in javascript keylogger applies, i.e. the attacker injects the malicious CSS in the page sent by the targeted site to the victim’s browser. Although 3rd party CSS sheets are more secure than 3rd party script, given the advances in CSS itself, together with some creativity from the attacker, we can have an insecure mix.

CSS attribute selectors

To implement a pure CSS keylogger, we basically rely on the attribute selectors feature of CSS. Below is a summary of them.

[attribute=value]  [foo=bar]
Selects all elements with foo="bar"
[attribute~=value]  [foo~=bar]
Selects all elements with a foo attribute containing the word "bar"
[attribute|=value]  [foo|=bar]
Selects all elements with a foo attribute value starting with "bar"
[attribute^=value]  [foo^="bar"]
Selects all elements with a foo attribute value starting with "bar"
[attribute$=value]  [foo$="bar"]
Selects all elements with a foo attribute value ending with "bar"
[attribute*=value]  [foo*="bar"]

Selects all elements with a foo attribute which contains the substring “bar”

Character-image CSS keylogger

The example below uses the value selector of any input whose type is a password to check the char being inserted into the input against a condition meant to identify it. After identifying the char as ‘a’ for example the malicious CSS sends a request to the attacker’s site as if it were requesting an image that represents the character ‘a’. After that, the attacker can review his server logs for the requests that asked for those imaginary “character images” and from them compose the password of the victim.

CSS Script

We skipped the other characters in the malicious CSS for brevity but it is the same concept. The resulting server logs on the attacker server would look something like this - - [25/Jan/2018:22:36:46 -0500] "GET /a HTTP/1.1" 404 22 - - [25/Jan/2018:22:36:46 -0500] "GET /v HTTP/1.1" 404 22 - - [25/Jan/2018:22:36:46 -0500] "GET /d HTTP/1.1" 404 22 - - [25/Jan/2018:22:36:46 -0500] "GET /f HTTP/1.1" 404 22 - - [25/Jan/2018:22:36:46 -0500] "GET /w HTTP/1.1" 404 22

Which are requests for the non-existing char images, resulting in status 404. From these logs, the attacker can reconstruct the password.

One famous implementation of this attack is the Chrome extension created by Max Chehab and hosted on GitHub in this link. It proves the concept by applying it to Instagram as a target site. This same implementation was introduced in Reddit and made a big impact there such that Max archived his repository in GitHub, making it read-only.

Note that you can’t select inputs based on what people type in them but only what’s set on the attribute itself. Max Chehab shows how it is possible, however, because React uses “controlled components” that do this by default. Not to mention you can apply the typed value to the attribute easily like:

const inp = document.querySelector("input");
inp.addEventListener("keyup", (e) => {
  inp.setAttribute('value', inp.value)

Limitations of Character-image CSS keylogger

It is worth mentioning that many sites highlighted limitations in Max’s keylogger. To name a few:

  • It is very specific to what data you can obtain in this method. For example, the input field must be of type password and not be using some other way to mask the chars
  • It only works with an initial value being set on an input, and not per keypress nor after blurring the field.
  • It’s not triggered for values that have been autocompleted by the browser’s credentials manager or a password manager tool.
  • It cannot handle repeat characters, as the browser won’t re-request the background image in that case
  • Due to parallelism, it’s not guaranteed for the requests to be received by the server in the order they were typed in
  • It cannot handle mouse clicks inside the password field to change the entry position or pressing the arrow keys or backspace. All of these scenarios can corrupt the ordering
  • To be really effective it has to inject a huge CSS file (several megabytes). The workaround for that is to host the malicious CSS on a CDN host. But then the defense against that is for the developer of the target site to include a “Content Security Policy” header that will only allow CSS hosting from whitelisted sources.


The CSS Keylogger, which was introduced by Max Chehab earlier 2018 made a big noise online. This is although the attack is not new and the concept was first introduced back in 2012. However many arguments make us rest assured that it is too impractical to be implemented in a real-world attack.

Try Free SQL Trainer - learn by doing!
SQL queries made easy - Natural Questions to SQL Converter.