Skip to main content
  1. Blog/

Figment Major Update: Building a CAPEv2 Sandbox in Minutes

9 mins

Introduction #

After releasing the prebuilt FlareVM and REMnux boxes (see my previous blog post), I immeditately started thinking of other targets to automate and to make available for the broadest set of systems. The first thing that came to mind was building a base image of CAPEv2 Sandbox. The second thing that I considered was also adding QEMU/KVM builds for those people who are not running VMWare Workstation / VirtualBox. Apart from that I’m running a Proxmox homelab and would like to run these boxes, so it’s a win win situation 😄.

With those two goals in mind, I started working on a major update for my project. At this moment, all FlareVM and REMnux boxes have been updated to their latest versions, FlareVM supports both Win10 and Win11, and all targets support QEMU as the hypervisor. In this post, I will elaborate on how to setup the CAPEv2 Sandbox and to get a base installation up as fast as possible.

Note that the CAPEv2 box has only the minimal configuration necessary to get a base installation up and running, you can tweak the rest of the configuration as you desire. I wanted this installation to be minimalistic, as everyone has their own preferences. Let’s dive in!

Using Vagrant #

The preferred way is to use Vagrant to get these boxes up and running as quickly as your download speed permits. I will demonstrate this with the VMWare Desktop provider, but the process is similar for Vitualbox and QEMU.

I’m assuming you have installed Vagrant as well as the necessary utilities / plugins, for instance the VMWare Plugin and the Vagrant VMWare Utility.

vagrant plugin install vagrant-vmware-desktop

Then clone the Git repository and change directory to vagrant/cape-server:

git clone git@github.com:stoyky/figment.git
cd figment/vagrant/cape-server

Review the Vagrant settings and ensure they are to your liking:

Vagrant.configure("2") do |config|
  # cape-server
  config.vm.define "cape-server" do |cape_server|
    cape_server.vm.box = "figment/cape-server"
    cape_server.vm.hostname = "cape-server"
    cape_server.ssh.username = "ubuntu"
    cape_server.ssh.password = "password"
    cape_server.ssh.insert_key = false 

    cape_server.vm.provider "vmware_desktop" do |vmware|
      vmware.vmx["ethernet0.present"]        = "TRUE"
      vmware.vmx["ethernet0.connectionType"] = "nat"
      vmware.vmx["ethernet0.virtualDev"]     = "e1000"
      vmware.vmx["ethernet0.connect"]        = "connected"
      vmware.vmx["ethernet0.startConnected"] = "TRUE"
      vmware.vmx["ethernet0.displayName"]    = "nat"

      vmware.vmx["ethernet1.present"]        = "TRUE"
      vmware.vmx["ethernet1.connectionType"] = "hostonly"
      vmware.vmx["ethernet1.virtualDev"]     = "e1000"
      vmware.vmx["ethernet1.connect"]        = "connected"
      vmware.vmx["ethernet1.startConnected"] = "TRUE"
      vmware.vmx["ethernet1.displayName"]    = "hostonly"
      
      vmware.memory = "8192"
      vmware.gui    = true
      vmware.cpus   = 4
    end

    cape_server.vm.provider "virtualbox" do |virtualbox|
      virtualbox.customize ["modifyvm", :id, "--nic1", "nat"]
      virtualbox.customize ["modifyvm", :id, "--macaddress1", "080027000001"]
      virtualbox.customize ["modifyvm", :id, "--nic2", "hostonly"]
      virtualbox.customize ["modifyvm", :id, "--macaddress2", "080027000002"]
      virtualbox.customize ["modifyvm", :id, "--hostonlyadapter2", "vboxnet0"]

      virtualbox.customize ["modifyvm", :id, "--accelerate3d", "on"]
      virtualbox.customize ["modifyvm", :id, "--vram", "128"]
          
      virtualbox.memory = "8192"
      virtualbox.gui    = true
      virtualbox.cpus   = 4
    end

    cape_server.vm.provider "libvirt" do |libvirt, override|
      libvirt.mgmt_attach    = false
      libvirt.nested = true 
      libvirt.cpu_mode = "host-passthrough"
      libvirt.cpus = 4

      override.vm.synced_folder ".", "/vagrant", disabled: true
      
      override.vm.network "forwarded_port",
        guest: 22,
        host: 2223,
        auto_correct: true

      override.vm.network "private_network",
        type: "dhcp",
        libvirt__model_type: "e1000",
        libvirt__network_name: "default",
        mac: "52:54:00:00:00:03"

      override.vm.network "private_network",
        type: "dhcp",
        libvirt__model_type: "e1000",
        libvirt__network_name: "hostonly",
        mac: "52:54:00:00:00:04"

      libvirt.memory = 8192
    end
  
  end
end

By default, all Vagrant boxes receive 8GB of RAM and 4 CPU’s. You might need to tweak these settings for the hypervisor you are using and the host system you are running the server on. By default, the cape-server deployment contains 2 nested VMs for dynamic analysis; a Windows 10 and Windows 11 under KVM running the CAPEv2 agent. In the next section I will show how to build your own cape-server boxes using Packer, where you have the freedom to choose the number of VMs for analysis (or none at all, if you are not nesting the VMs).

Running this box should be as simple as:

vagrant up --provider=vmware_desktop --provision

The video below shows a sped-up timelapse of this command setting up the box. After the box is up, login with ubuntu:password, and then browse to

localhost:8000

to access the CAPEv2 Web GUI.

Navigate to the Submit tab to submit testing malware. The testing malware Pafish / Pafish64 can be found in the ~/Downloads folder under the home directory:

/home/ubuntu/Downloads/

After uploading succeeds, open up virt-manager so you can inspect the nested VMs that are running for dynamic analysis. If you upload in quick succession, both the Windows 10 and 11 machines will come up to analyze the samples. After the analysis has finished, you can review the analysis results in the Web GUI.

A timelapse video of this process can be found below:

Using the Packer Template #

The Packer templates are provided to ensure you can build CAPE yourself. First review the most important parts:

cape-server.pkr.hcl 

and

cape-server.pkrvars.hcl

under figment/packer/cape-server/.

In the main Packer config cape-server.pkr.hcl you can change the settings of the VM:

source "vmware-iso" "cape-server" {
  cd_files             = ["packer/cape-server/cloud-init/user-data", "packer/cape-server/cloud-init/meta-data"]
  cd_label             = "cidata"
  output_directory     = "temp/cape-server-vmware"
  iso_url              = var.source_path_vmware
  iso_checksum         = var.checksum_vmware
  vm_name              = var.vm_name
  display_name         = var.display_name
  ssh_username         = var.ssh_username
  ssh_password         = var.ssh_password
  ssh_timeout          = var.ssh_timeout
  network_adapter_type = "e1000"
  disk_size            = 102400
  memory               = 16384
  cpus                 = 4

  vhv_enabled      = true
  shutdown_timeout = "30m"
  shutdown_command = "sudo shutdown -h now"
  boot_wait        = "5s"
  boot_command = [
    "<wait>",
    "e<wait>",
    "<down><down><down><end>",
    " autoinstall ds=nocloud-net;s=file:///cdrom/",
    "<f10>"
  ]

  vmx_remove_ethernet_interfaces = false
  skip_compaction                = true
  headless                       = false

  vmx_data = {
    "ide1:0.present"        = "TRUE"
    "ide1:0.startConnected" = "TRUE"
  }

}

The Packer config contains a mechanism to automatically install nested VMs.

# Source Image Paths
source_path_vmware = "assets/ubuntu-24.04.4-desktop-amd64.iso"
temp_path_vmware     = "temp/cape-server/cape-server.vmx"
checksum_vmware = "SHA256:3a4c9877b483ab46d7c3fbe165a0db275e1ae3cfe56a5657e5a47c2f99a99d1e"

source_path_virtualbox = "assets/ubuntu-24.04.4-desktop-amd64.iso"
checksum_virtualbox = "SHA256:3a4c9877b483ab46d7c3fbe165a0db275e1ae3cfe56a5657e5a47c2f99a99d1e"

source_path_qemu = "assets/ubuntu-24.04.4-desktop-amd64.iso"
checksum_qemu = "SHA256:3a4c9877b483ab46d7c3fbe165a0db275e1ae3cfe56a5657e5a47c2f99a99d1e"

# VM Identity
vm_name      = "cape-server"
display_name = "cape-server"

# SSH / Boot Settings
ssh_username = "ubuntu"
ssh_password = "password"
ssh_timeout  = "20m"
boot_wait    = "30s"

# CAPE Settings
cape_commit              = "e451de454137e0d44ab1ce1f72eae2e2bccfa78a"
cape_nested_virt         = true
cape_machinery           = "kvm"
cape_machinery_interface = "virbr1"

cape_guests = [
  {
    name              = "cape-guest-win10"
    platform          = "windows"
    arch              = "x64"
    replicas          = 1
    hostonly_offset   = 101
    mac_base_hostonly = "52:54:00:10:20"
    mac_base_nat      = "52:54:00:20:10"
  },
  {
    name              = "cape-guest-win11"
    platform          = "windows"
    arch              = "x64"
    replicas          = 1
    hostonly_offset   = 151
    mac_base_hostonly = "52:54:00:20:30"
    mac_base_nat      = "52:54:00:30:20"
  }
]

# Guest Host-Only Network
guest_hostonly_subnet          = "192.168.55.10/24"

# VMware PCI Slots
eth0_pcislot_vmware = "33"
eth1_pcislot_vmware = "36"

# VirtualBox PCI Slots
eth0_pcislot_virtualbox = "3"
eth1_pcislot_virtualbox = "8"

eth0_pcislot_qemu = "2"
eth1_pcislot_qemu = "3"

# VMWare valid MAC
mac_nat_vmware      = "00:0c:29:00:00:03"
mac_hostonly_vmware = "00:0c:29:00:00:04"

# Virtualbox valid MAC
mac_nat_virtualbox      = "080027000003"
mac_hostonly_virtualbox = "080027000004"

mac_nat_virtualbox_norm      = "08:00:27:00:00:03"
mac_hostonly_virtualbox_norm = "08:00:27:00:00:04"

# QEMU valid MAC
mac_nat_qemu      = "52:54:00:00:00:03"
mac_hostonly_qemu = "52:54:00:00:00:04"

# Export Settings
export_vagrant = true

Some important settings are:

  • cape_commit : You can pin the build to a specific commit hash to ensure consistent builds for this particular version of CAPE
  • cape_nested_virt: This determines whether KVM will be installed and whether you want to use nested Virt
  • cape_machinery: Specify the machinery here (KVM by default)
  • cape_machinery_interface: Specify the virtual bridge interface here that you wish to use for the machinery

Finally, by changing the settings below, you can specify the types of guests you want (for now only cape-guest-win10 and cape-guest-win11).

Note: I have experimented with older OS versions such as Windows 7, as well as with Linux, but no success on the current commits. I will try again in due time.

You can specify the settings and number of replicas to increase throughput of the VMs. These will be copied to the cuckoo.conf file. The mac_base_hostonly and mac_base_nat will be used to automatically assign network interfaces to these VMs. Specify the hostonly_offset to ensure there is no overlap when adding multiple replicas close on the same network range.

cape_guests = [
  {
    name              = "cape-guest-win10"
    platform          = "windows"
    arch              = "x64"
    replicas          = 1
    hostonly_offset   = 101
    mac_base_hostonly = "52:54:00:10:20"
    mac_base_nat      = "52:54:00:20:10"
  },
  {
    name              = "cape-guest-win11"
    platform          = "windows"
    arch              = "x64"
    replicas          = 1
    hostonly_offset   = 151
    mac_base_hostonly = "52:54:00:20:30"
    mac_base_nat      = "52:54:00:30:20"
  }
]

Note that considerable effort has been made into assigning consistent network interfaces for these machines. This is necessary to ensure connectivity between the machines. If you have better suggestions for how to go about this, I would love to hear from you! Please raise a Git issue and let us have a discussion!

Finally, building the box should be as simple as navigating back to the root folder and creating a venv:

python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Then issuing the following make commands, optionally with the debug flag for easier debugging:

make clean
make <debug> cape-server-<vmware_desktop/virtualbox/qemu>

Then Packer will start building the artifacts. The main VM files will end up in the /temp folder under the figment root folder. You can import these back into your hypervisor. You may find the resulting Vagrant boxes in the /boxes folder.

Note how I was able to also build the VM in under 30 minutes. Can’t beat that, right?

Troubleshooting #

Should you have any issues with uploading samples or analysis VMs that won’t spin up, please review the log output of the cape user for more information.

journactl -f -u cape

This will provide helpful information. For instance, the default free disk space configuration is quite stringent by default and may prevent you from analyzing samples:

INFO: max_vmstartup_count for BoundedSemaphore = 5
Jul 31 08:02:45 cape-server poetry[10253]: 2026-07-31 08:02:45,768 [lib.cuckoo.core.scheduler] INFO: Waiting for analysis tasks
Jul 31 08:02:45 cape-server poetry[10253]: 2026-07-31 08:02:45,768 [lib.cuckoo.common.cleaners_utils] ERROR: Not enough free disk space! (Only 49347 MB!). You can change limits it in cuckoo.conf -> freespace

The config files can be found under

/opt/CAPEv2/conf/

After you have changed the config files, be sure to save the changes and restart the cape processes:

sudo systemctl restart cape*

This will restart all cape services. The proper expected state after restarting is something like this:

Jul 31 08:12:08 cape-server poetry[11161]: /usr/bin/tcpdump
Jul 31 08:12:08 cape-server poetry[11119]: 2026-07-31 08:12:08,465 [lib.cuckoo.core.machinery_manager] INFO: Using MachineryManager[kvm] with max_machines_count=10
Jul 31 08:12:08 cape-server poetry[11119]: 2026-07-31 08:12:08,466 [lib.cuckoo.core.scheduler] INFO: Creating scheduler with max_analysis_count=unlimited
Jul 31 08:12:08 cape-server poetry[11119]: 2026-07-31 08:12:08,482 [lib.cuckoo.core.machinery_manager] INFO: Loaded 2 machines
Jul 31 08:12:08 cape-server poetry[11119]: 2026-07-31 08:12:08,503 [lib.cuckoo.core.machinery_manager] INFO: max_vmstartup_count for BoundedSemaphore = 5
Jul 31 08:12:08 cape-server poetry[11119]: 2026-07-31 08:12:08,504 [lib.cuckoo.core.scheduler] INFO: Waiting for analysis tasks

FlareVM Feature: Reset Network Settings using Scheduled Task #

Now back to FlareVM and REMnux. To have consistency in network connectivity between FlareVM and REMnux, the choice was made to hardcode the network inteface MAC addresses for the host-only network. This ensures the FlareVM IP/gateway and REMnux IP are in sync and are able to connect out of the box. While REMnux stays consistent across reboots, I noticed subtle issues where FlareVM would lose the assigned IP after a reboot.

To fix this, a Scheduled Task is now automatically run on boot that searches for the MAC address of the host-only network interface and sets the IP correctly. You can always run this scheduled task yourself to reset the settings if the interface loses its IP address for any reason, either through the GUI or the command line.

schtasks.exe /run /tn "Configure HostOnly Adapter"

Or open Start -> Task Scheduler -> Task Scheduler Library -> “Configure HostOnly Adapter”

Conclusion #

Thank you for taking the time to read this through. I’d love to hear from you if this project has been helpful to you! Have a nice day!