Sandboxing with gVisor

Remco Verhoef
4 min readJun 17, 2018

--

gVisor is a user-space kernel written in Go. gVisor protects the host user system by a custom implementation of the network stack, the filesystem and system calls.

We are very interested in this development, as it gives us an ideal sandbox to execute malware in and to use within Honeytrap (github.com/honeytrap/honeytrap).

Honeybox (github.com/honeytrap/honeybox) is the solution based on gVisor that we’ve created, which gives a web console to a per-session gVisor sandbox. All system calls, network traffic, processes and file activity will be registered and shown within the ui. Honeybox is still a work in progress.

Using gVisor with Docker is easy, but if you want to run it manually you need to do a few things. But first lets start with runsc. You can download the binary from here and place it at /usr/local/bin.

$ wget https://storage.googleapis.com/gvisor/releases/nightly/latest/runsc
$ wget https://storage.googleapis.com/gvisor/releases/nightly/latest/runsc.$ sha512
$ sha512sum -c runsc.sha512
$ chmod +x runsc
$ sudo mv runsc /usr/local/bin

Now that we’ve installed runsc, we can start it:

$ runsc
I0617 12:41:36.698200 20950 x:0] ***************************
I0617 12:41:36.705339 20950 x:0] Args: [runsc]
I0617 12:41:36.705516 20950 x:0] Git Revision: 418b2806bee6c6f2b69447b061bd42e6701ffffc-dirty
I0617 12:41:36.705537 20950 x:0] PID: 20950
I0617 12:41:36.705553 20950 x:0] UID: 0, GID: 0
I0617 12:41:36.705566 20950 x:0] Configuration:
I0617 12:41:36.705580 20950 x:0] RootDir: /run/user/0/runsc
I0617 12:41:36.705607 20950 x:0] Platform: ptrace
I0617 12:41:36.705676 20950 x:0] FileAccess: proxy, overlay: false
I0617 12:41:36.705871 20950 x:0] Network: sandbox, logging: false
I0617 12:41:36.705934 20950 x:0] Strace: false, max size: 1024, syscalls: []
I0617 12:41:36.705998 20950 x:0] ***************************
Usage: runsc <flags> <subcommand> <subcommand args>
Subcommands:
create create a secure container
delete delete resources held by a container
events display container events such as OOM notifications, cpu, memory, and IO usage statistics
exec execute new process inside the container
flags describe all known top-level flags
gofer launch a gofer process that server files over 9P protocol (internal use only)
help describe subcommands and their syntax
kill sends a signal to the container
list list contaners started by runsc with the given root
ps ps displays the processes running inside a container
run create and run a secure container
start start a secure container
state get the state of a container
Subcommands for internal use only:
boot launch a sandbox process (internal use only)
gofer launch a gofer process that server files over 9P protocol (internal use only)
Use "runsc flags" for a list of top-level flags
W0617 12:41:36.707028 20950 x:0] Failure to execute command, err: 2

You can find all the necessary gists and files here:
https://gist.github.com/nl5887/9b26ef8dfa5b7c1247bc09bb46175346

First we need to have a config.json, this is a container definition file in the Open Container Initiative format. Download this file from the gist:

$ wget https://gist.githubusercontent.com/nl5887/9b26ef8dfa5b7c1247bc09bb46175346/raw/config.json

We also need to configure a root file system for the sandbox. Detailed information can be found at https://wiki.ubuntu.com/DebootstrapChroot.

$ apt install -y debootstrap
$ debootstrap --arch i386 sid /chroot http://http.debian.net/debian

Now we’ll configure the network, starting with a bridge, we’ll use the 10.3.0/24 range for this.

$ sysctl net.ipv4.conf.all.forwarding=1
$ brctl addbr br0
$ ip addr add 10.0.3.1/24 dev br0
$ ip link set br0 up

Run the ip.sh, this will create a individual ip namespace for the container to run in.

$ sh ./ip.sh 

Ok, the namespace has been configured, let’s test if we can ping the bridge from within the namespace:

$ ip netns exec NS1 ping 10.0.3.1

The environment has been set, we have both a config.json and a root file system, we can now play with the sandbox.

# create the container called sandbox
$ runsc create sandbox
# start the sandbox container
$ runsc start sandbox
# list all containers
$ runsc list
# interactive shell to sandbox
$ runsc exec sandbox /bin/bash
# delete the running container sandbox
$ runsc delete --force sandbox

If everything works well, you now have an environment to start gVisor sandboxes manually, without Docker.

The following runtime flags can be used while starting the sandbox.

-debug
enable debug logging
-debug-log-dir string
additional location for logs. It creates individual log files per command
-file-access string
specifies which filesystem to use: proxy (default), direct. Using a proxy is more secure because it disallows the sandbox from opennig files directly in the host. (default "proxy")
-log string
file path where internal debug information is written, default is stdout
-log-format string
log format: text (default) or json (default "text")
-log-packets
enable network packet logging
-network string
specifies which network to use: sandbox (default), host, none. Using network inside the sandbox is more secure because it's isolated from the host network. (default "sandbox")
-overlay
wrap filesystem mounts with writable overlay. All modifications are stored in memory inside the sandbox.
-platform string
specifies which platform to use: ptrace (default), kvm (default "ptrace")
-root string
root directory for storage of container state
-strace
enable strace
-strace-log-size uint
default size (in bytes) to log data argument blobs (default 1024)
-strace-syscalls string
comma-separated list of syscalls to trace. If --strace is true and this list is empty, then all syscalls will be traced.

If you need to cleanup the ip namespace and containers, just start the cleanup.sh script.

--

--

Remco Verhoef

Founder @ DutchSec // Linthub.io // Transfer.sh // SlackArchive // Dutchcoders // OSC(P|E) // C|EH // GIAC // Security // DevOps // Pythonista // Gopher.