Howto: Build a Cuckoo sandbox


(Sean Whalen) #1

Introduction

Under renovation!: The previous versions of this guide was written for the cuckoo-modified fork of Cuckoo, which is no longer maintained. I am currently in the process of updating this guide to work with the latest release of the mainstream Cuckoo Sandbox.

A malware sandbox has many components. Building a sandbox requires you to have an understanding of how all these components work together, and how malware might respond. This guide assumes that you have a working, bare metal system, or a large ESX VM with nested virtualization enabled, running Debian Testing with at least 8 GB of RAM (For running analysis tools, and a few windows VMs) and at least 1TB of disk space (for storing samples, analysis results, and possibly memory dumps).

Cuckoo sandbox allows users to choose from a wide variety of different virtualization and networking techniques. While some malware may be able to detect and avoid some technologies used in this guide, these choices provide excellent coverage and analysis with minimal resources. No sandbox is fail-proof.

Preparing the OS

Download the latest Ubuntu Server LTS ISO

You can use UNetbootin to load the ISO onto a FAT32-formatted USB drive. The Debian installer will give you a warning about possible compatibility issues with UNetbootin, but I did not have any problems.

Choose the default text installer instead of the graphical installer. After the network has been configured (likely via DHCP), you should press Esc and select network configuration to set a static IP address.

When prompted for a root password, leave it blank. This will disable root login, and instead create a standard user account with sudo access, which is safer.

When you get to the software selection stage of the installer, uncheck all graphical options and the print server, and check the SSH server.

Make sure your system is up-to-date:

$ sudo apt-get update && sudo apt-get upgrade -y && sudo apt-get upgrade -y && sudo apt-get dist-upgrade -y && sudo apt-get autoremove -y

Installing dependencies

Cuckoo is largely written in Python, so you need to install the Python package manager (pip), and some other dependencies and useful tools:

$ sudo apt-get install -y git mongodb python python-dev python-pip libmagic1 swig libvirt-dev upx-ucl libffi-dev libssl-dev liblzma-dev libjpeg-dev zlib1g-dev liblcms2-dev libfreetype6-dev wget unzip p7zip-full geoip-database libgeoip-dev mono-utils ssdeep libfuzzy-dev exiftool clamav clamav-daemon clamav-freshclam wkhtmltopdf xvfb xfonts-100dpi  curl libguac-client-rdp0 libguac-client-vnc0 libguac-client-ssh0 guacd

Update pip to the latest version, and install virtualenv

$ sudo -H pip install -U pip setuptools virtualenv

The version of Yara that ships in package repositories tends to be outdated, so install from source instead:

$ sudo apt-get install git automake libtool make gcc flex bison libssl-dev libjansson-dev libmagic-dev checkinstall python-pip python3-pip 
$ git clone https://github.com/VirusTotal/yara
$ cd yara
$ ./bootstrap.sh
$ ./configure --enable-cuckoo --enable-magic --enable-dotnet
$ make
$ sudo checkinstall -y --deldoc=yes --pkgversion=3.8.1+git1
$ cd ..
$ rm -rf yara
$ sudo -H pip install -U git+https://github.com/VirusTotal/yara-python
$ sudo -H pip3 install -U git+https://github.com/VirusTotal/yara-python

By default, Cuckoo uses a SQLite database file to track analysis tasks. That technically works, but it is not as robust and production-ready as a full-featured relational database like PostgreSQL. For example, when SQLite is used, task IDs are recycled after a task is deleted, which can lead to confusion when a new analysis task exists at the same URL as an old one, possibly for a completely different sample!

Install PostgreSQL:

$ sudo apt-get install postgresql postgresql-contrib libpq-dev

Install Elasticsearch to to able to search the sandbox results:

$ sudo apt-get install -y apt-transport-https
$ wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
$ echo "deb https://artifacts.elastic.co/packages/5.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-5.x.list
$ sudo apt-get update
$ sudo apt-get install -y default-jre-headless elasticsearch kibana

The default JVM heap size for Elasticsearch is very small (1g), which will cause it to crash under a heavy load. To fix this, increase the minimum and maximum JVM heap sizes in /etc/elasticsearch/jvm.options to more reasonable levels, depending on your server’s resources.

Always set the minimum and maximum JVM heap sizes to the same value. For example, to set a 4 GB heap size, set

-Xms4g
-Xmx4g

See https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html for more information.

$ sudo systemctl daemon-reload
$ sudo systemctl enable elasticsearch.service
$ sudo systemctl enable kibana.service
$ sudo service start elasticsearch
$ sudo service start kibana

Install the Moloch PCAP database and create an admin user:

Note Replace the package URL/filename with the latest stable release from the Moloch downloads page.

$ sudo apt-get install -y wget gdebi
$ wget https://files.molo.ch/builds/ubuntu-18.04/moloch_1.5.3-1_amd64.deb
$ sudo gdebi -n moloch_1.5.3-1_amd64.deb

Configure Moloch:

When asked by the Moloch configuration script, which interfaces to listen on, answer lo

$ sudo /data/moloch/bin/Configure

Initialize Moloch This will delete any existing Moloch data

$ /data/moloch/db/db.pl http://127.0.0.1:9200 init

To upgrade the Moloch schemea, run:

$ /data/moloch/db/db.pl http://127.0.0.1:9200 upgrade

Set the configuration password to whatever you want.

Next, create a Moloch admin user, with a different arbitrary password:

$ /data/moloch/bin/moloch_add_user.sh admin "Admin User" "SomePass!" --admin

Install the Suricata IDS:

$ sudo add-apt-repository ppa:oisf/suricata-stable
$ sudo apt-get update
$ sudo apt-get install suricata
$ sudo apt install python-pip python-yaml
$ sudo pip install --pre --upgrade suricata-update

Configure systemd to run suricata-update every hour:

Create the file /etc/systemd/system/suricata-update.service

[Unit]
Description=suricata-update

[Service]
User=root
Group=root
Type=oneshot
ExecStart=/usr/local/bin/suricata-update

[Install]
WantedBy=multi-user.target

Create the file /etc/systemd/system/suricata-update.timer

[Unit]
Description=Run suricata-update hourly and at boot

[Timer]
OnBootSec=15min
OnUnitActiveSec=1h
unit=suricata-update.service

[Install]
WantedBy=timers.target

Start the new systemd units:

$ sudo systemctl daemon-reload
$ sudo systemctl enable suricata-update.service
$ sudo systemctl enable suricata-update.timer
$ sudo service suricata-update start

Create a copy of the default Suricata configuration file for Cuckoo:

$ sudo cp /etc/suricata/suricata.yaml /etc/suricata/suricata-cuckoo.yaml

Edit /etc/suricata/suricata-cuckoo.yaml:

$ sudo nano /etc/suricata/suricata-cuckoo.yaml

Disable the fast and unified2 log types; we don’t need those.

Locate file-store: (use ctrl+w to search). Set enabled to yes. Set force-md5 and force-filestore to yes. Enable file-log, which should be located right below it.

Locate reassembly: use ctrl+w to search (You’ll need to do this twice; the first reference is just comment documentation about it, you want is the actual, non-commented reassembly:)

Set depth, to 0 (without a unit of measurement)

Set request-body-limit and response-body-limit to 0 (without any measurement unit), under default-config:

Under the vars: address-groups: section, set EXTERNAL_NET to any.

Install KVM and virt-manager

$ sudo apt-get install qemu-kvm libvirt-clients libvirt-daemon virt-manager

Add your user to the kvm and libvirt groups, so you can manage VMs:

$ sudo usermod -a -G kvm $USER
$ sudo usermod -a -G libvirt $USER

You’ll need to log out and log back in for the group membership to take effect.

Configure virtual networking

Because managing QEMU/KVM/libvirt purely by command line can have a steep, time-consuming learning curve for even experienced Linux administrators, we’ll use the virt-manager GUI. X server forwarding allows clients to access graphical applications running on a server, even when that server is headless (i.e. does not use a graphical environment)

For accessing the actual sandbox VMs remotely, we’ll use VNC, which is much faster than the X protocol, especially when viewing a full desktop.

Linux desktop/laptop clients:

No prerequisites are needed other than the OpenSSH client, which is included by default in most Linux distribution installations.

Mac clients:

Systems running Mac OS will need XQuartz installed. After a one-time system reboot, the command is the same as Linux.

Windows 10 Clients:

Follow this guide


From here on, the instructions are the same for all client systems.

Pass the -X argument to ssh for X11 forwarding so virt-manager can be used remotely, and pass a -L option to tunnel the the local client’s 5900 port to the remote VM VNC port 5900.

$ ssh -X -L 5900:127.0.0.1:5900 user@hostname

In the SSH session, run:

$ virt-manager

Those with high-DPI/ 4K displays should prefix this command with GDK_SCALE=2, to scale the UI properly.

You’ll probably one or many warnings in your console, those can be ignored, as long as virt-manager window appears a few seconds later. If you have a long-running SSH session without a GUI running, sometimes the application has trouble making a connection to your X server, especially on Mac OS, If this occurs, try closing your console, and reconnect.

In the View menu of the Virtual Machine Manager window, select Connection Details, then select the Virtual Networks section.

Stop the Default network, then delete it.

Create a new isolated virtual network called internal at 192.168.100.0/24, using the wizard.

Create the cuckoo user

Create a user named cuckoo on the system. This user will run a few services that Cuckoo needs, as well as Cuckoo itself.

$ sudo adduser cuckoo
$ sudo usermod -L cuckoo

Add the cuckoo user to the right groups to manage VMs

$ sudo usermod -a -G kvm cuckoo
$ sudo usermod -a -G libvirt cuckoo

tcpdump needs to be installed and configured to generate packet captures (PCAPs):

$ sudo apt-get install tcpdump libcap2-bin apparmor-utils
$ sudo aa-disable /usr/sbin/tcpdump

Allow the cuckoo user to run tcpdump without root privileges:

$ sudo groupadd pcap
$ sudo usermod -a -G pcap cuckoo
$ sudo chgrp pcap /usr/sbin/tcpdump
$ sudo setcap cap_net_raw,cap_net_admin=eip /usr/sbin/tcpdump

vsftpd

The simplest way to share files between the VMs and host is to use vsftpd as an anonymous FTP server, with separate read-only and read-write directories. That way, the VM can send files without being able to overwrite installers or other key files.

First, create a publicly accessible folder:

$ sudo mkdir -p /home/cuckoo/vmshared/pub
$ sudo chown -R cuckoo:cuckoo /home/cuckoo
$ sudo chmod -R ug=rwX,o=rX /home/cuckoo/vmshared/
$ sudo chmod -R ugo=rwX /home/cuckoo/vmshared/pub

Then, install vsftpd:

$ sudo apt-get install vsftpd

Edit /etc/vsftpd.conf:

$ sudo nano /etc/vsftpd.conf

Change listen to YES

Change listen_ipv6 to NO

Change anonymous_enable to YES

Uncomment the following lines:

write_enable=YES
anon_upload_enable=YES
anon_mkdir_write_enable=YES

Add the lines:

listen_address=192.168.100.1
listen_port=2121
anon_root=/home/cuckoo/vmshared
anon_umask=000
chown_upload_mode=0666
pasv_enable=Yes
pasv_min_port=10090
pasv_max_port=10100

Restart the service:

$ sudo service vsftpd restart

The VMs can now read /home/cuckoo/vmshared and write to /home/cuckoo/vmshared/pub.

You can access the FTP server in from the Windows VMs natively by typing ftp://192.168.100.1:2121 into any Explorer window.

Inetsim

Inetsim is an internet simulator. It has a bunch of fake network services. It can be configured to respond to all DNS requests with it’s own address, which drives traffic to itself, where it can establish fake connections.

It usually provides just enough to allow malware to beacon once, before it becomes confused by the unexpected response. This is useful if you want to see what an initial beacon looks like, but don’t want to allow the sample to access the internet at all, possibly for OPSEC reasons.

To install inetsim:

$echo "deb http://www.inetsim.org/debian/ binary/" | sudo tee /etc/apt/sources.list.d/inetsim.list
$ wget -O - http://www.inetsim.org/inetsim-archive-signing-key.asc | sudo apt-key add -
$ sudo apt-get update
$ sudo apt-get install inetsim

Edit /etc/default/inetsim:

$ nano  /etc/default/inetsim

Set ENABLED to 1.

Edit the configuration:

$ sudo nano /etc/inetsim/inetsim.conf

Set service_bind_address and dns_default_ip to 192.168.100.1

Edit /etc/default/inetsim:

$ sudo nano /etc/default/inetsim

Set ENABLED to 1

Start inetsim:

$ sudo service inetsim restart

Create a Python virtualenv for Cuckoo

$ sudo su cuckoo
$ virtualenv venv
$ . venv/bin/activate
(venv) $ pip install -U pip==9.0.2 setuptools
(venv) $ pip install -U psycopg2 m2crypto backports.lzma libvirt-python weasyprint
(venv) $ pip install -U git+https://github.com/VirusTotal/yara-python
(venv) $ pip install -U git+https://github.com/kbandla/pydeep.git
(venv) $ pip install -U pillow distorm3 pycrypto openpyxl ujson
(venv) $ pip install -U git+https://github.com/volatilityfoundation/volatility.git
(venv)$ pip install -U cuckoo

Run Cuckoo for the first time

This will create the Cuckoo Working Directory (CWD) at ~/.cuckoo.

$ cuckoo

Copy the Cuckoo agent to the vmshared directory:

$ cp /home/cuckoo/.cuckoo/agent/agent.py /home/cuckoo/vmshared/agent.pyw

Create your first sandbox VM

The software in the VM should be as plain and bare-bones as possible. Many first-time sandbox builders make the mistake of using a production image for their sandbox. This can cause many problems:

  • Attackers may learn who you are, and what’s in your image
  • Security controls in the image may interfere with sandboxing
  • It can be more difficult to determine why a sample behaved the way it did

Upload your plain Windows ISOs, and the latest vnet drivers ISO to your server

In order to maintain anonymity, the VM must never touch your LAN or WAN connections. By following this guide, the VM will only be able to communicate with select services on the host and other VMs. Internet access through a VPN can be enabled on a per-submission basis.

This part of the guide will assume that you have a Windows 7 Professional 64bit ISO and a valid license. If you are using a different version of windows, the setup steps may be slightly different.

Note to volume license users: Do not use KMS keys with Windows, Office, or any other Microsoft product. KMS clients require regular check-ins for the license to remain activated, which is not possible for isolated VMs. Use MAK instead. Those keys can be used for independent activation.

Start virt-manager. Create a new Windows virtual machine.

Watch out for an error like this:

Warning: KVM is not available. This may mean the KVM package is not installed, or the KVM kernel modules are not loaded. your virtual machine may preform poorly.

KVM Error Screenshot

If you see this error, it means that virtualization support is not enabled or your host system’s CPU settings. Check your host system’s BIOS/UEFI settings, or, if you are running Cuckoo in a virtualization platform like ESXi, ensure nested virtualization is enabled for the Cuckoo VM. Restart the host system, and try creating the sandbox VM again.

Name the sandbox VM something reasonable, like sandbox-win7-01, you’ll use this name later. Avoid spaces.

Before completing the last stage of the wizard, make the following changes:

  • Make sure number of CPUs are 2
  • Check Customize configuration before install
  • Set the network selection to the internal isolated virtual network you created earlier

Click Finish.

In the VM Installation that comes up, select the NIC, and set the Device Model to virtio for the best performance. the virtio storage bus is also a lot faster than the default IDE bus, but the Windows can’t be installed on a virtio storage bus.

To work around this issue, click the Add Hardware button, then add a storage device, with a VirtIO bus type, and a cache mode of writeback (under advanced options). Leave the default size of 8.0 GB. Windows can’t be installed to VirtIO device, but it can use them once the driver has been installed manually in Device Manager. We’re only using this new drive as a temporary dummy drive of sorts, so that Windows knows how to work with VirtIO drives after it is installed. We’ll go into detail about installing the drivers later on.

Add another video device, and change the display type to VNC. Leave the port on Auto this means that the VM will take the first port it can once it starts, starting with the port we forwarded, 5900, and incrementing each time another VM with VNC enabled with an Auto port is selected. We only forwarded one port through SSH when we connected (5900), so we can access one VM at a time via VNC. To forward more ports, add more -L options.

To access another VM via VNC, you can shut down the VM you were using, freeing up the port for the next one to start. Or, you can add multiple -L options and different ports when connecting over SSH, to forward multiple VNC ports, and use multiple VMs over VNC at once.

Once all that is done, click on begin installation to start the VM for the first time. The window that opens will be too small, so resize it so you can see the full display. If you are working on the same LAN as the server, this console will probably be responsive enough for you. If you find that it is slow, use a VNC client and connect to 127.0.0.1 with the password that you set earlier. and it will be much faster.

Most Linux Distributions come with a “Remote Desktop” viewer that can connect over VNC. On Mac OS, use the built-in “Screen Sharing” app (find with Spotlight), and for Windows, use TightVNC

Choose a custom Windows install, then click next.

After Windows is installed create a username and computer name that is fictitious, but realistic for your industry. Leave the password blank.

When you get to a screen that says “Help protect your computer and improve Windows automatically”, click “Ask me later”.

Configuring the VM

Once Windows has been installed, and you are at the desktop:

  • Set the theme to Classic
  • Set the screen resolution to 1280x720

This will make the VM easier to work with, and consume less resources.

Installing the VirtIO drivers

Open the VM in virt-manager and click on the blue information button in the toolbar to edit the hardware info.

Select the IDE CD-ROM drive, and disconnect the windows ISO. Then, connect the VirtIO drivers ISO that you downloaded earlier.

In the VM, right click on Computer in the Start Menu, then click Properties. In the upper-left of the System window, click Device Manager.

Follow these steps for each of the “Other devices”:

  1. Right-click on the device, and click Update Driver Software
  2. Click browse my computer manually
  3. Select the root of the D drive

Windows will automatically locate the proper driver on the disk.

After all of the drivers have been installed, shut down the system.

Edit the VM hardware options again, and disconnect the ISO from the CD-ROM drive.

Now that the driver for the VirtIO storage bus has been installed in Windows, the dummy drive isn’t needed any more, and you can configure your primary drive to use VirtIO for much-improved performance.

Remove the dummy VirtIO disk.

Edit the IDE disk. Under advanced options, set the bus to VirtIO Under performance, set the cache mode to writeback.

Start the VM.

Configure the IP settings:

  1. Open the Network and Sharing Center, located in the control panel.
  2. Click on the Local Area Connection
  3. Click Proprieties
  4. Double click on the IPv4 protocol

Configure:

IP Address: 192.168.100.101
Subnet Mask: 255.255.255.0
Default Gateway: 192.168.100.1

Primary DNS: 1.1.1.1
Secondary DNS: 1.0.0.1

Ensure that Windows Updates are completely disabled, the activate Windows by phone.

Install and activate your copy of Office. For privacy reasons, do not use Office365… Remember to disable all update checking when Office prompts you about it.

If your installer is in file format rather than an ISO, zip up the files, and upload the zip to /home/cuckoo/vmshared. It can then be download by the VM from ftp://192.168.100.1:2121/

Shut down the VM.

Take a snapshot at this point, in case you ever need a fresh VM. Click on the snapshots button on the toolbar.

Then click on the plus button in the bottom left. Name it something like installed.

Start the VM back up. Use the reg files in this zip to disable security features and noisy network services that will interfere with analysis. Run the reg file for your Windows version and Office version, then reboot the VM.

In order to work with Cuckoo, the sandbox VM will need the following installed on it:

For convince, I have bundled all the files listed below in one zip file, organized by guest OS version, and common files required on all guests. You can upload this file, or or the individual files to /home/cuckoo/vmshared. They can be download by the VM from http://192.168.100.1:8080/ This is much more secure than downloading EXEs over Tor, which may be tampered with.

Install all of the latest supported Microsoft Visual C++ Redistributables (both x86 and x64 if you are using a 64 bit guest OS), which as of this writing are:

To analyze files that use a newer .NET framework, install Microsoft .NET framework 4.5.1.

To analyze more than PE files, the following also need to be installed (older versions are greatly preferred for exploitability and corporate realism):

As you install each of these programs, change their configuration to disable update checks, which can create noisy PCAPs.

Run Adobe Reader, Accept the Terms and Conditions, and maximize the window, so PDFs will open in the future.

Configure Adobe Reader (in preferences) and Java (in the Control Panel) to not check for updates.

To add some realism, find some non-sensitive and non-identifying documents, music, pictures and other files to put into the Windows user’s folder, so you can see how malware might interact with them. Let it seem “lived in”. Be careful to remove or replace any identifying metadata before you send it to the VM. Use Flickr, Jamendo, and Archive.org, to find pictures, music, and more under Creative Commons license. Then, do what you would normally do with those kinds of files, open a few of those files, open them, edit edit them, and import music into Windows Media Player. Browse some known safe, ad-free sites like Wikipedia and secure government sites over SSL/TLS, that way, you build up some browsing history, with minimal risk of your sandbox being popped prematurely in the process. Some malware checks for things like this this sort of activity on the system as a way of evading sandboxes.

Copy the agent.pyw script from the shared folder to the the Windows user’s Startup folder, which is located in the Start Menu, under All Programs. Rename it to something other than agent, so malware is less likely to find it.

Click Start and type msconfig, and press enter. Disable all items in the Startup tab except agent.pyw. In the Services tab, check the box to hid all of the Microsoft services, then disable all of the 3rd party update services.

Restart the system when prompted.

If all is well, you should see pythonw.exe in the list of processes in the task manager.

Shut down the VM.

VM Detection countermeasures

In order to make it hardware for malware to detect that it is running in a VM/sandbox, we must edit the VM’s CPU settings.

Edit the VM’s XML configuration

$ sudo virsh dumpxml win7-sandbox-01

KVM by default will pass through a feature flag, viewable in ECX as the 31st bit
after executing the CPUID instruction with EAX set to 1. Some malware will use this
unprivileged instruction to detect its execution in a VM. One way to avoid this is to modify
your VM definition as follows: find the following line::

<domain type='kvm'>

Change it to:

<domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>

In the cpu element, ** change the value of the check attribute to partial.

Then within the domain element, add the following:

<qemu:commandline>
      <qemu:arg value='-cpu'/>
      <qemu:arg value='host,-hypervisor'/>
</qemu:commandline>

Instead of using “host”, you can also choose a number of other CPU models from the
list displayed with the “qemu-system-i386 -cpu help” command (SandyBridge, Haswell, etc).

Save the XML file (ctrl-o), and exit the editor (ctrl-x).

Create a new snapshot called cuckoo you’ll use this for recovering state and cloning VMs.

Start the VM, wait for windows to fully load, then wait another five seconds.

Take a snapshot; call it running

Power off the VM, then restore the cuckoo snapshot

Your sandbox VM is now ready for Cuckoo.

Set up PostgreSQL

Replace somePassword with a password of your choice. It can be random. You only need to use it in the cuckoo config

$ sudo su postgres
$ psql
# CREATE USER cuckoo WITH PASSWORD 'somePassword';
# CREATE DATABASE cuckoo;
# GRANT ALL PRIVILEGES ON DATABASE cuckoo to cuckoo;

Type \q to exit

Exit the postgres bash shell:

$ exit 

Configure Cuckoo

$ sudo su cuckoo
$ cd /opt/cuckoo
$ nano conf/cuckoo.conf

Change the machinery option to kvm

Change the terminate_processes option to yes. This will make Cuckoo gracefully request the termination of the process that is being analyzed prior to shutting down the VM. This is useful for things like some Office macros that launch on close.

Warning! By default, Cuckoo is configured to do post-analysis DNS lookups using the host! This is to allow comparisons with what the VM resolved. However, it could also potentially tip off an attacker and/or deanonymize you, even if your VM isn’t connected to the internet! To disable this feature, set resolve_dns to off in cuckoo.conf.

Set ip to 192.168.100.1.

Set the connection setting to use PostgreSQL:

connection = postgresql://cuckoo:password@localhost:5432/cuckoo

Where password is the password you set in PostgreSQL for the cuckoo user.

$ nano conf/processing.conf

Under static, uncomment the line with the procyon_path variable, including any leading whitespace.

Set Suricata enabled to yes.

Change the Suricata conf option to /etc/suricata/suricata-cuckoo.yaml

$ nano conf/reporting.conf

Set reporthtmlsummary enabled to yes.

Set reportpdf enabled to yes.

Set mongodb enabled to yes.

Set resubmitexe enabled to yes.

Set malhur enabled to yes.

Edit the Cuckoo KVM configuration file:

$ sudo nano conf/kvm.conf

Set machines to the name of your sandbox VM, such as sandbox-win7-01

Replace cuckoo1 in brackets with the name of your sandbox VM, such as sandbox-win7-01

Set label to the name of your sandbox VM, such as sandbox-win7-01

Uncomment the snapshot option by deleting the # and any leading white space, then set it to the name of your running snapshot, running.

Uncomment the tags option, by deleting the # and any leading white space. Make sure it includes either 32_bit or 64_bit to match the VM OS architecture, so Cuckoo knows which VMs can process 64-bit samples. Add other tags of your choice to describe the VMs, for your own convince. For example:

windows_7,64_bit,acrobat_reader_11,java_6

Finally, uncomment the correct mem_profile for the VM OS, by deleting the # and any leading white space.

Install the community modules

$ sudo su cuckoo
$ cd utils
$ ./community.py -afw
$ exit

Hardening your sandbox host

You should take some basic steps to secure your system, especially when running live malware. Even something simple as strings can have security vulnerabilities

Set up automatic security updates:

$ sudo apt-get install unattended-upgrades apt-listchanges

Prevent outsiders from accessing services through brute force:

$ sudo apt-get install fail2ban

Configure the firewall (replace enp3s0 with your primary network interface, if needed. Use sudo ip a for a list):

$ sudo apt-get install ufw
# Allow access to services from the primary NIC
sudo ufw allow in on enp3s0 to any port 80 proto tcp # HTTP
sudo ufw allow in on enp3s0 to any port 443 proto tcp # HTTPS
sudo ufw allow in on enp3s0 to any port 22 proto tcp # SSH
sudo ufw allow in on enp3s0 to any port 4343 proto tcp # Legacy upstream API

# Allow VMs to access cuckoo/inetsim services
sudo ufw allow in on virbr0 to any port 8080 proto tcp # vmshared files
sudo ufw allow in on virbr0 to any port 2042 proto tcp # Cuckoo result server
sudo ufw allow in on virbr0 to any port 5353 proto udp # TOR DNS
sudo ufw allow in on virbr0 to any port 9040 proto tcp # TOR TCP
sudo ufw allow in on virbr0 to any port 53 proto udp # DNS
sudo ufw allow in on virbr0 to any port 5342 proto udp # inetsim DNS
sudo ufw allow in on virbr0 to any port 80 proto tcp # HTTP
sudo ufw allow in on virbr0 to any port 443 proto tcp # HTTPS
sudo ufw allow in on virbr0 to any port 25 proto tcp # SMTP
sudo ufw allow in on virbr0 to any port 465 proto tcp # SMTPS
sudo ufw allow in on virbr0 to any port 110 proto tcp # POP3
sudo ufw allow in on virbr0 to any port 995 proto tcp # POP3S
sudo ufw allow in on virbr0 to any port 69 proto udp # TFTP
sudo ufw allow in on virbr0 to any port 21 proto tcp # FTP
sudo ufw allow in on virbr0 to any port 2121 proto tcp # Shared FTP
sudo ufw allow in on virbr0 to any port 10090:10100 proto tcp # Shared FTP passive ports
sudo ufw allow in on virbr0 to any port 990 proto tcp # FTPS
sudo ufw allow in on virbr0 to any port 123 proto udp # NTP
sudo ufw allow in on virbr0 to any port 6667 proto tcp # IRC
sudo ufw enable

Configure web services

In this guide we’ll be using the nginx web server as a secure reverse proxy for Cuckoo.

$ sudo apt-get install nginx apache2-utils

Add yourself to the cuckoo group:

$ sudo usermod -a -G cuckoo $USER

Create a SSL certificate

It is very important that communications between the sandbox and its users are secure for a couple of reasons:

  • Prevents credentials from being sniffed
  • Prevents triggering IDS alarms in your network while uploading malware

You can use a self-signed certificate, or one that has been signed by an internal or external Certificate Authority (CA). Using a CA will allow browsers and other HTTPS clients that trust the CA to trust that the connection is secure. Self signed certificates will generate browser warnings.

If you are accessing your sandbox directly via in internal/private IP address, you must use a self signed certificate.

Create a directory to store the certificates and keys:

$ mkdir ~/ssl
$ cd ~/ssl

To create a self-signed certificate, run:

$ openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout cuckoo.key -out cuckoo.crt

Or, to create a Certificate Signing Request (CSR) for a CA, run:

$ openssl req -newkey rsa:4096-nodes -keyout cuckoo.key -out cuckoo.csr

Fill in the prompts. Watch out for Common Name (e.g. server FQDN or YOUR domain name), which is the IP address or domain name that you will be hosting Cuckoo web services on. it is the most important field.

Remove the CSR after you have your certs

$ rm cuckoo.csr

Generate Diffie-Hellman (DH) parameters This takes a long time.

$ openssl dhparam -out dhparam.pem 4096

Move the keys into place:

$ cd
$ sudo mv ssl /etc/nginx

Secure the keys:

$ sudo chown -R root:www-data /etc/nginx/ssl
$ sudo chmod -R u=rX,g=rX,o= /etc/nginx/ssl

Configuring nginx

Disable the default nginx configuration:

$ sudo rm /etc/nginx/sites-enabled/default

Create the Cuckoo web server configuration

$ sudo nano /etc/nginx/sites-available/cuckoo
server {
    listen IP_Address:443 ssl http2;
    ssl_certificate /etc/nginx/ssl/cuckoo.crt;
    ssl_certificate_key /etc/nginx/ssl/cuckoo.key;
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off; # Requires nginx >= 1.5.9
    # Uncomment this next line if you are using a signed, trusted cert
    #add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    root /usr/share/nginx/html;
    index index.html index.htm;
    client_max_body_size 101M;
    auth_basic "Login required";
    auth_basic_user_file /etc/nginx/htpasswd;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /storage/analysis {
       alias /opt/cuckoo/storage/analyses/;
       autoindex on;
       autoindex_exact_size off;
       autoindex_localtime on;
    }

    location /static {
      alias /opt/cuckoo/web/static/;
    }
}

server {
    listen IP_Address:80;
    return 301 https://$server_name$request_uri;
}


server {
  listen 192.168.100.1:8080;

    root /home/cuckoo/vmshared;

     location / {
           try_files $uri $uri/ =404;
           autoindex on;
           autoindex_exact_size off;
           autoindex_localtime on;
     }
}

# Host the upstream legacy API 
server {
    listen IP_Address:4343 ssl http2;
    ssl_certificate /etc/nginx/ssl/cuckoo.crt;
    ssl_certificate_key /etc/nginx/ssl/cuckoo.key;
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off; # Requires nginx >= 1.5.9
   # Uncomment this next line if you are using a signed, trusted cert
    #add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    root /usr/share/nginx/html;
    index index.html index.htm;
    client_max_body_size 101M;

    location / {
        proxy_pass http://127.0.0.1:8001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # Restrict access
       allow IP_Address;
      #allow 192.168.1.0/24;
      deny all;
    }
}

In both the SSL and non-SSL server blocks, replace IP_Address with the IP address of the interface you will access web services through.

Enable the nginx Cuckoo configuration:

$ sudo ln -s /etc/nginx/sites-available/cuckoo /etc/nginx/sites-enabled/cuckoo 

Setup basic authentication:

Cuckoo’s web interface has no authentication mechanism of its own, so we’ll use use nginx's basic auth. To create a user, use

$ sudo htpasswd -c /etc/nginx/htpasswd exampleuser 

Where exampleuser is the name of the user you want to add.

Secure the permissions of the httpasswd file:

$ sudo chown root:www-data /etc/nginx/htpasswd
$ sudo chmod u=rw,g=r,o= /etc/nginx/htpasswd

Restart nginx:

$ sudo service nginx restart

Startup script

As you have seen, there are a lot of services working behind-the-scenes to make Cuckoo work. This script will help you start and restart all of them:

$ sudo pip install gunicorn
$ sudo nano /usr/sbin/cuckooboot
#!/bin/bash

CUCKOO_USER="cuckoo"
CUCKOO_PATH="/opt/cuckoo"
VIRBR_IP="192.168.100.1"
INETSIM_DNS_PORT="5342"
VIRBR_DEV="virbr0"

su $CUCKOO_USER -c "pkill gunicorn" >/dev/null 2>&1
su $CUCKOO_USER -c "pkill python" > /dev/null 2>&1

/usr/bin/curl -s http://$VIRBR_IP:8080/ > /dev/null

# Wait for the virtual bridge to become active before (re)starting services
while [ $? -ne 0 ]
do
sleep 5
/usr/sbin/service nginx restart
/usr/bin/curl -s http://$VIRBR_IP:8080/ > /dev/null
done

# Restart services that bind to the bridge
/usr/sbin/service tor restart
/usr/sbin/service inetsim restart
/usr/sbin/service vsftpd restart

# Start Cuckoo
cd $CUCKOO_PATH
su $CUCKOO_USER -c "./cuckoo.py &"

# Start the Cuckoo web UI
cd web
su $CUCKOO_USER -c "gunicorn --reload -D -w 4 -b 127.0.0.1:8000 web.wsgi"

# Start the legacy upstream API
cd ../utils
su $CUCKOO_USER -c "gunicorn --reload -D -w 4 -b 127.0.0.1:8001 api"

# Redirect libvirt VM DNS quires to inetsim's DNS port
/sbin/iptables -t nat -C  PREROUTING -d $VIRBR_IP -p udp --dport 53 -j REDIRECT --to-ports $INETSIM_DNS_PORT >/dev/null 2>&1
if [ $? -ne 0 ]; then
    /sbin/iptables -t nat -I PREROUTING -d $VIRBR_IP -p udp --dport 53 -j REDIRECT --to-ports $INETSIM_DNS_PORT
fi

# Allow inetsim to accept traffic for any IP address
/sbin/iptables -t nat -C PREROUTING -i $VIRBR_DEV -j REDIRECT >/dev/null 2>&1
if [ $? -ne 0 ]; then
    /sbin/iptables -t nat -A PREROUTING -i $VIRBR_DEV -j REDIRECT
fi
$ sudo chmod +x  /usr/sbin/cuckooboot

Schedule the cuckooboot server to run at system boot

$ sudo crontab -e

Add the line:

@reboot /usr/sbin/cuckooboot > /dev/null 2>&1

Run cuckooboot for the first time:

$ sudo /usr/sbin/cuckooboot

You should now be able to access the Cuckoo web interface at the root of your web server, and upload your first sample. Congratulations!

Updating Cuckoo

Commits are made to the project repositories regularly. It is recommended to follow the cuckoo-modified and community-modified repositories on GitHub to stay advised of the latest bugs and updates.

$ cd /opt/cuckoo
$ git pull

Occasionally there will be conflict between your configuration files and new configuration files.

To view the differences, run:

$ git diff

Make notes on your original configuration settings (in red), especially the connection setting if cuckoo.conf changed, it has your database password!

Once you have made note of your configuration settings, delete the conflicting files:

$ rm conf/example.conf

Run git pull again.

$ git pull

This will replace the files you deleted with the updated version.

Edit each file, and restore your configuration changes.

Then update the Cuckoo community modules.

Warning : This command will overwrite any existing signature files. If you have made any changes, back them up, and consider creating a pull request for the community repository.

$ cd utils
$ ./community.py -afw

Restart services:

$ sudo /usr/sbin/cuckooboot

Scaling Cuckoo

To allow to allow Cuckoo to analyze more than one sample at the same time, you’ll need to create more VMs.

  1. Restore the cuckoo snapshot on your original VM to get it to a malware-free state.
    Clone the VM by right-clicking its name in the list. Name it something like sandbox-win7-02, and so on
  2. Start the VM
  3. Connect to it using VNC
  4. Change the IP address to something else in the 192.168.100.102-192.168.100.254 range
  5. Change the hostname to match the new VM name, and avoid conflict with existing domain
  6. Start the VM. Wait a couple of minutes.
  7. Crate a snapshot called running
  8. Shut down the VM.

Edit the KVM configuration file:

$ sudo nano conf/kvm.conf

Add the VM name to to the machines list, separated by a comma (no spaces).

Copy and paste from [sandbox-win7-01] on down

Replace the name in brackets with the name of the new VM. Do the same with the label variable.

Repeat these steps for each VM. You can build different VMs containing different software or windows versions, and them group them by setting tags in this file. You can specify a tag to use when submitting a sample.

Restart services:

$ sudo /usr/sbin/cuckooboot

Adding YARA rules

Cuckoo looks for YARA rules three sub directories in data/yara, by the type of data to match:

  • binaries
  • memory
  • urls

Place your .yar rule files in the appropriate directories, and then restart Cuckoo.

$ sudo /usr/sbin/cuckooboot

YARA rule indexes will be automatically generated.

Warning! Cuckoo shows the matching strings from memory rules (but not filesystem rules), this may be in violation of sharing rules of groups such as the YARA exchange.

Configure web services

In this guide we’ll be using the nginx web server as a secure reverse proxy for Cuckoo.

$ sudo apt-get install nginx apache2-utils

Add yourself to the cuckoo group:

$ sudo usermod -a -G cuckoo $USER

Create a SSL certificate

It is very important that communications between the sandbox and its users are secure for a couple of reasons:

  • Prevents credentials from being sniffed
  • Prevents triggering IDS alarms in your network while uploading malware

You can use a self-signed certificate, or one that has been signed by an internal or external Certificate Authority (CA). Using a CA will allow browsers and other HTTPS clients that trust the CA to trust that the connection is secure. Self signed certificates will generate browser warnings.

If you are accessing your sandbox directly via in internal/private IP address, you must use a self signed certificate.

Create a directory to store the certificates and keys:

$ mkdir ~/ssl
$ cd ~/ssl

To create a self-signed certificate, run:

$ openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout cuckoo.key -out cuckoo.crt

Or, to create a Certificate Signing Request (CSR) for a CA, run:

$ openssl req -newkey rsa:4096-nodes -keyout cuckoo.key -out cuckoo.csr

Fill in the prompts. Watch out for Common Name (e.g. server FQDN or YOUR domain name), which is the IP address or domain name that you will be hosting Cuckoo web services on. it is the most important field.

Remove the CSR after you have your certs

$ rm cuckoo.csr

Generate Diffie-Hellman (DH) parameters This takes a long time.

$ openssl dhparam -out dhparam.pem 4096

Move the keys into place:

$ cd
$ sudo mv ssl /etc/nginx

Secure the keys:

$ sudo chown -R root:www-data /etc/nginx/ssl
$ sudo chmod -R u=rX,g=rX,o= /etc/nginx/ssl

Configuring nginx

Disable the default nginx configuration:

$ sudo rm /etc/nginx/sites-enabled/default

Create the Cuckoo web server configuration

$ sudo nano /etc/nginx/sites-available/cuckoo
server {
    listen IP_Address:443 ssl http2;
    ssl_certificate /etc/nginx/ssl/cuckoo.crt;
    ssl_certificate_key /etc/nginx/ssl/cuckoo.key;
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off; # Requires nginx >= 1.5.9
    # Uncomment this next line if you are using a signed, trusted cert
    #add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    root /usr/share/nginx/html;
    index index.html index.htm;
    client_max_body_size 101M;
    auth_basic "Login required";
    auth_basic_user_file /etc/nginx/htpasswd;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /storage/analysis {
       alias /opt/cuckoo/storage/analyses/;
       autoindex on;
       autoindex_exact_size off;
       autoindex_localtime on;
    }

    location /static {
      alias /opt/cuckoo/web/static/;
    }
}

server {
    listen IP_Address:80;
    return 301 https://$server_name$request_uri;
}


server {
  listen 192.168.100.1:8080;

    root /home/cuckoo/vmshared;

     location / {
           try_files $uri $uri/ =404;
           autoindex on;
           autoindex_exact_size off;
           autoindex_localtime on;
     }
}

# Host the upstream legacy API 
server {
    listen IP_Address:4343 ssl http2;
    ssl_certificate /etc/nginx/ssl/cuckoo.crt;
    ssl_certificate_key /etc/nginx/ssl/cuckoo.key;
    ssl_dhparam /etc/nginx/ssl/dhparam.pem;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_ecdh_curve secp384r1; # Requires nginx >= 1.1.0
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off; # Requires nginx >= 1.5.9
   # Uncomment this next line if you are using a signed, trusted cert
    #add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;
    root /usr/share/nginx/html;
    index index.html index.htm;
    client_max_body_size 101M;

    location / {
        proxy_pass http://127.0.0.1:8001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # Restrict access
       allow IP_Address;
      #allow 192.168.1.0/24;
      deny all;
    }
}

In both the SSL and non-SSL server blocks, replace IP_Address with the IP address of the interface you will access web services through.

Enable the nginx Cuckoo configuration:

$ sudo ln -s /etc/nginx/sites-available/cuckoo /etc/nginx/sites-enabled/cuckoo 

Setup basic authentication:

Cuckoo’s web interface has no authentication mechanism of its own, so we’ll use use nginx's basic auth. To create a user, use

$ sudo htpasswd -c /etc/nginx/htpasswd exampleuser 

Where exampleuser is the name of the user you want to add.

Secure the permissions of the httpasswd file:

$ sudo chown root:www-data /etc/nginx/htpasswd
$ sudo chmod u=rw,g=r,o= /etc/nginx/htpasswd

Restart nginx:

$ sudo service nginx restart

Feedback welcome

If you have found this guide useful, spot a mistake, or have other suggestions, please contact me on Twitter @SeanTheGeek.