Skip to content

Vagrant: crafting an attack VM

Last updated on 2019-05-03


This blog post presents some examples and explains some concepts that can generally be applied to any VM that you’d craft with Vagrant, such as configuring the amount of memory and CPU available to the VM.

That said, the intent of this article is to demonstrate how to craft a VM for the purposes of studying and practicing offensive security skills; therefore, certain configuration choices I’ve made are rooted on that intent, and I will explain along the way why I’ve made them.

The examples in this article were executed on Debian Buster, which was Debian’s testing distribution at the time of writing, and with Vagrant 2.2.3 and VirtualBox 6.0.

Lastly, you may find the following article useful before continuing:

Assigning VM resources

Assuming you’ve installed Vagrant and initialized a project, let’s get to work on Vagrantfile.

The following configuration is pretty self-explanatory, with sensible defaults for a VM without a desktop environment:

The attack VM will indeed not have a desktop environment; we’ll use vagrant ssh to connect to it, and run all of our attacks from the terminal, so the above settings will work just fine.

Network configuration

Host-only interface

With the basics all set, let’s move on to configure a DHCP-enabled private network as follows:

This configuration will cause Vagrant to create a host-only network named vboxnet0 and addressed as

If you have multiple Vagrant VMs configured the same way as shown above, no additional host-only networks will be created. Rather, they will all share vboxnet0.

The 0 in vboxnet0 will increment for each host-only network that already exists and was not created by Vagrant.

Despite their name, host-only networks not only allow communication between the host (your physical machine) and each guest (VM), but also among guests.

Therefore, two distinct VMs configured with network interfaces assigned to the host-only network vboxnet0 will be able to send and receive network communication to and from each other.

The ability for guests to communicate over a host-only network is a feature of VirtualBox and does not depend on Vagrant. If one VM was provisioned with Vagrant and another VM was downloaded from, say,, and both were configured to use vboxnet0, they will be able to communicate.

What host-only networks do not provide is Internet access. If Internet access is desirable, a separate network interface should be configured, preferably as a NAT interface.

For details about networking in VirtualBox, see

NAT interface

Turns out that Internet access is desirable for our attack VM. Also turns out that we don’t need to do anything because Vagrant already configures the first network adapter of every Vagrant-provisioned VM as a NAT adapter.

The host-only adapter that Vagrant will create from "private_network", type: "dhcp" will actually become the second network adapter in the VM.


Provisioning: customizing the VM

Vagrant supports a few different methods of provisioning VMs. Among the simplest, requiring no additional tools, are the file and shell provisioners; the former tells Vagrant to copy supplied files/directories to the VM, while the latter tells it to execute supplied shell scripts within the VM.

We’re going to provision the attack VM by specifying the following provisioning commands within our Vagrantfile:

Each invocation of a provisioner starts with config.vm.provision, followed by the provisioner type specified as a string, and additional parameters.

Each file provisioner will copy the specified file/directory from the host to the specified destination within the VM. Paths for specifying source files are relative to the project directory; i.e., where Vagrantfile resides.

Keep in mind the following limitation of Vagrant’s file provisioner:

The file uploads by the file provisioner are done as the SSH or PowerShell user. This is important since these users generally do not have elevated privileges on their own.


Therefore, the first file provisioner will copy a file called sources.list from the host’s project directory to /tmp/sources.list within the VM – not directly to /etc/apt/sources.list, since this provisioner does not run as root. As you may have guessed, the script will take care of that step.

The second file provisioner will copy the entire dotfiles directory to /home/vagrant/ in the VM. Take note of these caveats when the intent is to copy multiple files or entire directories.

The very first shell provisioner simply executes a quick inline script to install apt-transport-https so that all of our subsequent packages will be downloaded over TLS.

Following the file provisioners are two shell provisioners which will make use of the copied files and perform other provisioning tasks, such as tweaking the operating system as well as downloading, installing and configuring software.

Running as the root user with full privileges is the default behavior of shell provisioners. In order to run a shell provisioner as the built-in vagrant user, who is not all-powerful, specify privileged: false when invoking a shell provisioner.

The contents of and are shown below with inline comments.



Vagrant.configure("2") do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at

  # Every Vagrant development environment requires a box. You can search for
  # boxes at = "debian/buster64"
  # Create a private network, which allows host-only access to the machine
  # using a specific IP. "private_network", type: "dhcp"
  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:

  hostname = "attack-vm"
  config.vm.provider "virtualbox" do |vb|
    # What will the VM be called within VirtualBox? = hostname
    # Display the VirtualBox GUI when booting the machine
    vb.gui = false
    # Customize the amount of CPU cores on the VM:
    vb.cpus = 2
    # Customize the amount of memory on the VM:
    vb.memory = "4096"
    # Customize the amount of video memory on the VM:
    # vb.customize ["modifyvm", :id, "--vram", "64"]

  # View the documentation for the provider you are using for more
  # information on available options.

  # Enable provisioning with a shell script. Additional provisioners such as
  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
  # documentation for more information about their specific syntax and use.
  config.vm.provision "shell", inline: "apt-get install -qq apt-transport-https"
  config.vm.provision "file", source: "sources.list", destination: "/tmp/sources.list"
  config.vm.provision "file", source: "dotfiles", destination: "/home/vagrant/"
  config.vm.provision "shell", path: ""
  config.vm.provision "shell", path: "", privileged: false

  # What will Vagrant call this VM?
  config.vm.define hostname
  # What will be the VM's hostname?
  config.vm.hostname = hostname



deb buster main contrib non-free
deb-src buster main contrib non-free

deb buster/updates main contrib non-free
deb-src buster/updates main contrib non-free

deb buster-updates main contrib non-free
deb-src buster-updates main contrib non-free

#! /bin/bash

# To avoid warnings during provisioning; for more information, see:
export DEBIAN_FRONTEND=noninteractive

cp /tmp/sources.list /etc/apt/sources.list
dpkg --add-architecture i386
apt-get update
apt-get upgrade -qq

#-----          System utilities         -----#

apt-get install -qq apt-file
apt-get install -qq mlocate
apt-get install -qq ntp
apt-get install -qq tree
apt-get install -qq vim

#-----            Programming            -----#

apt-get install -qq git
apt-get install -qq golang
apt-get install -qq python3
apt-get install -qq python3-dev
apt-get install -qq python3-pip
apt-get install -qq python3-venv
apt-get install -qq subversion

#-----   Networking and reconnaissance   -----#

apt-get install -qq arping
apt-get install -qq curl
apt-get install -qq ncat
apt-get install -qq net-tools
apt-get install -qq nikto
apt-get install -qq nmap
apt-get install -qq python3-nmap
apt-get install -qq smbclient
apt-get install -qq sshuttle

#-----     RE and binary exploitation    -----#

# apt-get install -qq radare2

#-----          Virtual display          -----#

# apt-get install -qq fluxbox x11vnc xvfb

#-----          Graphical tools          -----#
# These require a virtual display or a full graphical environment

# apt-get install -qq wine32 wine64
# apt-get install -qq chromium-driver python3-selenium

#-----    Metasploit and searchsploit    -----#

# apt-get install -qq curl git gnupg
# curl -sS > /tmp/msfinstall
# chmod 700 /tmp/msfinstall
# /tmp/msfinstall
# git clone -q /opt/exploitdb
# sed 's|path_array+=(.*)|path_array+=("/opt/exploitdb")|g' /opt/exploitdb/.searchsploit_rc > ~/.searchsploit_rc
# ln -sf /opt/exploitdb/searchsploit /usr/local/bin/searchsploit

#-----        Reporting utilities        -----#

# apt-get install -qq pandoc
# apt-get install -qq build-essential
# apt-get install -qq python3-dev
# apt-get install -qq python3-pip
# apt-get install -qq python3-setuptools
# apt-get install -qq python3-wheel
# apt-get install -qq python3-cffi
# apt-get install -qq libcairo2
# apt-get install -qq libpango-1.0-0
# apt-get install -qq libpangocairo-1.0-0
# apt-get install -qq libgdk-pixbuf2.0-0
# apt-get install -qq libffi-dev
# apt-get install -qq shared-mime-info

which apt-file && apt-file update
which locate   && updatedb

exit 0

Published inUncategorized