Escaping from Mozilla Firefox in Restricted Environments

By Bastian Kanbach

Software environments in which users should only be able to perform a limited amount of tasks often use Kiosk applications to reduce the opportunities of a user to interact with the system. In many environments, only a few pre-defined applications are published to the user, like web browsers or POS software, depending on what the user needs to do.

An attacker who targets such a restricted environment usually tries to find a way to bypass the boundaries of the published application, in order to execute commands on the underlying operating system. The overall goal of the attacker is usually to establish a foothold on the system and potentially compromise further systems that are connected to it. Consequences can vary between operational and financial damage, including the loss of sensitive data, depending on what the purpose of the attacked system is.

In Windows there are many well-known ways for an attacker to create an interactive shell, such as Sticky Keys, “File open” dialogues, Typing commands in address bars, help menus etc. In Linux environments this is more complicated and it is not sufficient to just type a command in the address bar of a browser.

The following write-up describes a technique than can be used to execute code on the underlying operating system by including a custom module for smartcard-based authentication (PKCS11) into Mozilla Firefox. The write-up focuses on Linux environments but the same can be achieved when the underlying operating system is Microsoft Windows.

Reconnaissance

When performing security assessments the first step is reconnaissance to discover as much about the target system as possible. If Firefox is the only available application, reconnaissance is more difficult, but not impossible. “File open” dialogues can be helpful to determine how the file system is structured. In some cases File open/File save dialogues can be locked down so that neither directories nor files are visible. However, Firefox allows any user to traverse the file system by using the “file:” protocol handler. The screenshot below shows the root directory of the underlying operating system after calling file:///

Examples for directories of interest are the /etc directory which could contain valuable information about the system’s configuration or the /tmp directory which is world-writable and could aid an attacker in dropping files on the system. Not only directories can be browsed, but also files like /etc/os-release that are readable by the current user:

Gathering some information about the operating system is important for all further steps. Further reconnaissance via the “Help” menu and the “About Firefox” view revealed that Firefox runs as a 32-bit process. This information is helpful, since the target architecture of the module has to match with the architecture of the process.

Breakout using a custom PKCS11 module

Options for spawning an interactive shell are limited, however the “Security Devices” feature in Mozilla Firefox allows to load *.so or *.dll files that are stored locally. This feature is mainly used to set up smartcard-based authentication, for example when a webserver requires a client certificate. The implementation is based on the PKCS11 standard that also defines an API to communicate with smartcards.

Now that we have identified the architecture and a location to download files to, we have three steps to create a functional PKCS11 library that spawns a shell and is compatible with Mozilla Firefox:

A call of the system() command was inserted that invokes an external application, in this case /usr/bin/xterm, to spawn an interactive terminal. Please note that the call of “system()” in the code blocks the main thread of the Firefox process and keeps it unresponsive until xterm is closed. However for this Proof-of-concept it is adequate.

For a simple Proof-of-concept this modification is sufficient. The OpenSC project now needs to be compiled for a 32-bit architecture, because the targeted Firefox instance was launched as a 32-bit process. After compiling the project, the resulting shared library that Firefox could open was created with the name "opensc-pkcs11.so" and can be found in src/pkcs11/.libs/. After hosting the file on an accessible web server, it needs to be downloaded to the target system, for example by using the browser’s “Save link as” function. Loading this file into Firefox usually initially fails, indicated by the error message "module load failed". This relates to a missing library that opensc-pkcs11.so depends on, which means the dynamic dependencies need to be adjusted.

Adjust dynamic dependencies of the resulting library

To find out which libraries are missing, the “ldd” command can be used:

As shown above, the compiled module opensc-pkcs11.so depends on a library called “libopensc.so.6”, stored at /root/OpenSC/src/libopensc/.libs/libopensc.so.6. So far only opensc-pkcs11.so was downloaded to the target system and libopensc.so.6 cannot be found by the library, resulting in an error.

The tool “patchelf” can be used to adjust the location, from which libopensc.so.6 is loaded. To change the search path from /root/OpenSC/src/libopensc/.libs/ to /tmp/ the following command can be used:

patchelf --set-rpath /tmp opensc-pkcs11.so

The command above changes the “rpath” to /tmp. To check if the modification was successful, the ldd command can be invoked again:

After modifying the shared object, all payloads are prepared. To spawn a shell, the following actions need to be performed:

Download the modified version of libopensc.so.6 to /tmp/

Download the modified version of opensc-pkcs11.so to an arbitrary location

Add opensc-pkcs11.so as a security device in Firefox

After specifying the path to the shared object, the PKCS11 module gets loaded by Firefox and the code is executed:

The screenshot above shows that xterm was successfully spawned.

Summary

The technique described can be used in situations when code needs to be executed and Firefox is published as a single Kiosk application.
Although this method requires some effort in advance to create a shared object that executes custom code, it is cross-platform and very easy to invoke once the library is downloaded to the target system.