Marketplace/Reviewers/Apps/Guide/Firefox OS Add-on Security

From MozillaWiki
< Marketplace‎ | Reviewers‎ | Apps‎ | Guide
Jump to: navigation, search

Security Guide for Firefox OS Add-on Reviewers

This is a guide for reviewers of Firefox OS add-ons (also known as "new-style add-ons") that focuses on the security aspects. FxOS add-ons have different security properties than apps, but before you start, please familiarize yourself with the general threat model for Firefox OS apps by reading our security guidelines for app developers and our security reviewer training.

FxOS add-ons are distributed and installed just like apps as ZIP archives. Superficially indistinguishable, it's easy to tell them apart once you look inside: If the archive contains a file called manifest.webapp, it's a web app. If it contains a file called manifest.json instead, it's a Web Extension. If it contains both, it's an app and the add-on manifest is ignored.

General considerations

In the scope of Firefox OS, add-ons (also known as new-style add-ons are a modernized variant of Firefox Extensions (also known as old-style add-ons. They adhere to the Web Extensions standard that is also supported in Google Chrome and Opera. While this in theory they are compatible, in practice they tend to use FxOS-specific features or interact with FxOS-specific apps.

The main difference old- and new-style add-ons is the manifest which is structured differently and uses different properties.

FxOS add-ons come in two major varieties: Content Scripts, and Background Pages. (Gecko is currently not supporting Event Pages, a non-persistent variant of Background Pages, but will be.) Content Scripts inject JavaScript files into web pages and apps, and background pages react to system events they register for. FxOS add-ons can also bundle both, but this document focuses on Content Scripts which are the most powerful and by far most common type.

Content Scripts

Content scripts don't live on their own. They are injected into existing apps or web pages. The injection behavior is exclusively defined in the manifest. Here's an example of the manifest.json file of a password manager:

{
  "manifest_version": 2,
  "name": "Password Manager",
  "description": "It is a password manager",
  "version": "0.0.1",
  "role": "addon",

  "content_scripts": [{
	"matches": "<all_urls>",
	"js": ["js/ContentManager.js"]
  },{
	"matches": ["app://system.gaiamobile.org/index.html"],
	"js": ["js/PasswordManager.js"]
  }]
}

This manifest tells us:

  • The add-on only bundles content scripts, two of them.
  • "js/ContentManager.js" is injected into "<all_urls>".
  • "js/PasswordManager.js" is injected into "app://system.gaiamobile.org/index.html", the system app.

Or in other words: Whenever the browser opens any web page (and that includes every app), js/ContentManager.js is executed, and whenever it loads the main page of the system app, js/PasswordManager.js is executed. Pretty much what you'd expect of a password manager.

Matches

For content scripts the main security concern with the manifest hence is the "matches" lines. Where scripts are injects determines the level of access and privileges it can obtain – and in the worst case abuse.

Matches follow the scheme://host/path format. Where scheme can be any of http, https, file, ftp, or app. ? can be used to match an arbitrary character, * matches anything. There is also the <all_urls> alias which is equivalent to * which in turn is equivalent to *://*/*.

If you're interested in the details, take a look at the source code of MatchPattern.jsm.

Execution Timing

Timing of content script execution is controlled by their run_at manifest key. However, the default option document_idle is the only one currently supported in Firefox OS. This means they are all executed after all other scripts have loaded. Keep this in mind, because it can create nasty race conditions when the content script modifies the injectee's behavior by live-patching its init functions.

Execution Context

A content script does not run in the target context it is injected to, but in its private sandbox. Since its ‘this’ object is different from that on the target context, its window and document objects are also different. However, target context’s window and document objects can be accessed through a proxy object that is available in its scope.

A content script can

  • access and modify all the target context’s objects and data.
  • inject JS files into the target context through the target's DOM.
  • access its Runtime API.
  • access the Web Extensions API.
  • can modify the target's web content through the webRequest API.
  • indirectly make API calls with the permissions of the target context by injecting js.
  • bypass the target context's CSP policy.
  • register event listeners. [arbitrarily?]
  • patch function calls in the target context and modify their behavior

A content script can not

  • be accessed from JS code running in the target context.
  • directly call APIs that require permission.

Threat model

Once you know where an add-on injects its content scripts, use the following table as a guide.

Content Scripts Threat Model (Summary)
Injection target Threat Recommendations
<all_urls>, * Global data exfiltration, performance issues Ensure injected script doesn't slow down the system, escalate to experienced sec reviewer
System App Full device compromise Escalate to experienced sec reviewer
SMS app SMS content exfiltration, sending costly premium SMS ...
Contacts app Contact data exfiltration, redirection of calls and messages ...
Specific privileged apps Abuse of app permissions Carefully audit match pattern
Specific certified apps Abuse of app permissions Escalate to experienced security reviewer
All web pages Password stealing, data exfiltration, performance issues
Specific web pages ... ...
any Injecting unsafe scripts into contexts Look for creation of script elements.

Guide

External communication

When an add-on is completely self-contained and doesn't make outside connections, it hardly represents a risk for data exfiltration. But when it does, the most common channel is established by HTTP GET and POST requests. Look for instances of XMLHttpRequest, like:

var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.imgur.com/3/image');
var fd = new FormData();
fd.append("image", file);
[...]
xhr.send(fd);

The corresponding .open() call closeby takes the URI for the request, in this case from a string constant. The call to .send() finally posts the request with the form data which comes from the file variable.

Covert channels

A sneakier communication channels to an external server is locally embedding of an image file from a URL that encodes data in the file name, some GET parameter, or even the host name.

Take-aways

  • Beware of non-static URIs that are constructed from variables.
  • Follow those variables.
  • Is the resulting URI always something you'd expect from the add-on's description?


Obfuscation

If the add-on is completely obfuscated, ask for the cleartext code for review, and affirm that the cleartext you are reviewing is what compiles exactly into the obfuscated add-on in the queue. In doubt, escalate.

There's a sneakier obfuscation technique that tries to hide malicious code in plain sight. Most often you will see a combination of fancy string encoding (e.g. "\x68\x74\x74\x70" instead of "http"), indirect object access (i.e. through a copy in a variable), bracket notation for object access (e.g. using xhr["open"] instead of xhr.open), and helper variables with inconspicuous or misleading names.

Reviews likely happen under some sort of time pressure. To speed things up, you skim the code for the interesting parts, you grep for a few suspicious keywords, but there's hardly time for understanding every single line. In such a scenario, it is easy to miss that

var trigger = 'EL_max_timer_precision';
var timelogger = this;

var msg = "\x73";
msg += trigger[3];
msg += msg[0];

var mode = "\x61\x64\x64" + trigger[0];
mode += "vent" + trigger[1] + "ist";
mode += trigger[10] + "ne" + trigger[11];

var result = timelogger[mode];
result(msg, this);

isn't actually logging time, but covertly installing a listener for sms events. While certainly not the most elaborate example, it might still get across the idea of "hiding in plain sight". For an overview of some advanced obfuscation techniques, take a look at the submissions to the JavaScript Misdirection Contest.

Take-aways

  • Searching for well-known keywords is a good start, but not always enough.
  • Develop an eye for common obfuscation techniques.
  • Any form of partial obfuscation should immediately ring alarm bells.
  • Easiest way to see though is by selective execution of code snippets.