Vagrant - A Burp Suite VM To Go, Please

Vagrant

A Burp Suite VM To Go, Please

Noah Berner
by Noah Berner
on December 08, 2022
time to read: 16 minutes

Keypoints

An Automated Way of Creating a Burp Suite Virtual Machine

  • Vagrant is an open-source virtual machine management software
  • Different configurations can be automated using a variety of approaches
  • Certain configurations require deep knowledge of the guest operating system
  • Debugging a Vagrantfile can be time consuming but worth it for certain VM needs

If you have ever wished that you could take your web penetration testing virtual machine configuration and spin it up on any other host, then this article is for you! We will be looking at Vagrant, a software tool for automating the configuration of virtual machines. We create a Vagrantfile that lets us start up a Kali Linux VM with Burp Suite installed in minutes.

Vagrant is an open-source software that can create reproducible virtual machines using a description file. This file is called the Vagrantfile and it contains all the necessary information for Vagrant to download, set up, start and provision the exact virtual machine that you configured. The power of Vagrant is, that if you have written a Vagrantfile that describes your ideal virtual machine configuration, you can take it anywhere and spin up this exact VM in minutes with only one command: vagrant up. This also works if you have somehow managed to make your current VM nonoperational. Just destroy the VM with vagrant destroy and bring it back up with vagrant up. It will be as good as new.

What is a Vagrantfile?

The Vagrantfile always starts out more or less looking like this:

Vagrant.configure("2") do |config|
    config.vm.box = "kalilinux/rolling"
end

where the 2 stands for the used configuration version. The only configuration we have added is the box that we want to use. The box can be seen as a blank image that Vagrant will download once and save. When we tell Vagrant to start the VM, it will create a clone of this downloaded box and configure this clone. The advantage is, that Vagrant only has to download the VM image once (until a new version is released), thus reducing the startup time. In our example we will be using Kali Linux as the base box of our VM. Other boxes can be found here.

We might also want to add some shared folders between our host and guest operating system.

require_relative "user_config"

Vagrant.configure("2") do |config|
    ...
    # Enable shared folders
    @user_config[:shared_folders].each do |host, guest|
        config.vm.synced_folder(host, guest)
    end

    # Disable the standard synced "vagrant" folder
    config.vm.synced_folder ".", "/vagrant", disabled: true
end

As you can see, we loop over all the specified shared_folders and use the config.vm.synced_folder directive to tell Vagrant which host folder path should map to which guest folder path. We also disable the standard shared folder to /vagrant since we do not need it for this project. We have moved some of the configuration variables to a separate file called user_config.rb. This can be helpful if you plan on sharing the Vagrantfile with colleagues who might want to adjust some minor settings. The rest of the variables in user_config.rb will be used and explained further on in the article.

# defines all configuration options about the VM that are specific to a certain user's preferences and its host system
@user_config = {
    # This maps folder on your host (the key) to a folder on the VM (the value).
    :shared_folders => {
        "C:\\Users\\host\\Documents\\gitlab" => "/gitlab",
        "C:\\Users\\host\\Pictures\\cats" => "/cute"
    },

    # The display name of the VM
    :vm_name => "labs_vm",

    # Number of processors of the VM
    :num_processors => 4,
    # Amount of RAM in MB of the VM
    :memory_mb => 4096,

    :burp_suite_shell_script_name => "burpsuite_pro_linux.sh",

    # ----- Networking -----
    # The mac address (the key) of your ethernet interface (USB dongle), mapped to your external IP addresses as an array (the value)
    :mac_address_to_ip_address_mapping => {
        "AB:12:CD:34:EF:56" => ["20.30.40.2", "20.30.40.3"],
        "AB:12:CD:34:EF:57" => ["20.30.40.5"]
    },

    :gateway_ip_address => "20.30.40.1",
    :dns_ip_addresses => "20.30.40.10,20.30.40.11", # as a comma seperated list
    :subnet_mask => "26", # in CIDR notation

    # ----- Wallpapers -----
    :desktop_background_name => "desktop-background.png",
    :desktop_grub_name => "desktop-grub.png",
    :desktop_login_background_name => "desktop-login-background.png",
    :user_image_name => "user.png"
}

Provider

To start your VM, Vagrant uses a provider. In our case, we want to use VMware Workstation installed on a Windows host. Vagrant can also work with lots of other providers, such as VirtualBox, Docker and many more. It also has the ability to programmatically configure the settings in VMware that you would normally set using VMware’s UI settings dialog. Note, that these settings are different for every provider and for VMware, they are undocumented and can thus change without prior notice, breaking your Vagrantfile setup. We configure our provider in the following way:

Vagrant.configure("2") do |config|
    ...
    config.vm.provider "vmware_workstation" do |vmware|
        # Display the VirtualBox GUI when booting the machine
        vmware.gui = true

        # Customize the name of the VM:
        vmware.vmx["displayname"] = @user_config[:vm_name]

        # Customize the number of processors of the VM:
        vmware.vmx["numvcpus"] = @user_config[:num_processors]

        # Customize the amount of memory of the VM:
        vmware.vmx["memsize"] = @user_config[:memory_mb]

        # USB Controller Configuration.
        vmware.vmx["usb.present"] = "TRUE"
        vmware.vmx["usb.vbluetooth.startConnected"] = "TRUE"
        vmware.vmx["ehci.present"] = "TRUE"
        vmware.vmx["usb_xhci.present"] = "TRUE"
        vmware.vmx["usb.generic.allowHID"] = "TRUE"
    end
end

We change the name, number of processors and memory of our VM in VMware Workstation to the values provided in user_config.rb. We also configure a USB Controller which will be useful for our next endeavor.

Security and Communication

However, before we talk about that networking endeavor, we will first look at how Vagrant can communicate with the guest OS. It does so by configuring the guest to run an SSH server which Vagrant then uses to connect and run commands on the guest. The first SSH connection is done using an insecure private key, which is known to anyone. On the first vagrant up Vagrant replaces this insecure keypair by a randomly generated one, making the SSH connection secure.

Out of the box, Vagrant uses the password vagrant for both its root and vagrant user. If the machine is used for more than a pet project, these passwords should be changed.

Networking

We now want to configure some USB ethernet dongles to always map to a specific set of IP addresses when they are connected to the VM. This task serves as an example for a more complex networking configuration. To do this, we want to use NetworkManager and its command line interface nmcli, which provides the networking GUI in Kali. These kinds of configurations can be done in Vagrant using different provisioners. For now, we will stick with the simple shell provisioner, which runs shell commands over the SSH connection. This network configuration task is where some of the disadvantages and hardships of configuring a VM using Vagrant show up.

Vagrant by default uses ifupdown, a different network management tool, to configure the eth0 interface as a NAT interface. By using ifupdown, the eth0 interface becomes inaccessible for NetworkManager. We must delete the entries on eth0 in /etc/network/interfaces, which have been placed there by ifupdown. Then we restart NetworkManager to give it control over eth0. Only then will NetworkManager work correctly and we can make our custom configuration using nmcli. As you can imagine, debugging a solution like this can be time consuming because you have to restart the VM every time you try a new fix. Also, if you do not have in-depth knowledge about the inner workings of the guest operating system, this process takes even longer and debugging can become frustrating and inefficient.

Vagrant.configure("2") do |config|
    ...
    # We delete the entries about eth0 in /etc/network/interfaces, which have been placed there by ifupdown.
    # Then we restart NetworkManager to give it control over eth0.
    config.vm.provision "shell", inline:  "sed -i '/auto eth0/d' /etc/network/interfaces && "\
                                          "sed -i '/iface eth0 inet dhcp/d' /etc/network/interfaces && "\
                                          "service NetworkManager restart"

    # Provision the mapping of ethernet interfaces to IP addresses.
    # The first ethernet interface will be mapped to eth1, second to eth2, and so on.
    @user_config[:mac_address_to_ip_address_mapping].each_with_index do |(ethernet_interface_mac_address, ip_addresses), interface_index|
        ip_addresses.each do |ip_address|
        config.vm.provision "shell", inline:  "nmcli connection add "\
                                              "save yes "\
                                              "connection.id \"ip_" + ip_address + " (" + ethernet_interface_mac_address + ")\" "\
                                              "connection.type 802-3-ethernet "\
                                              "connection.interface-name eth" + (interface_index + 1).to_s + " "\
                                              "connection.autoconnect yes "\
                                              "802-3-ethernet.mac-address " + ethernet_interface_mac_address + " "\
                                              "802-3-ethernet.cloned-mac-address random "\
                                              "802-3-ethernet.mtu auto "\
                                              "ipv4.method manual "\
                                              "ipv4.dns " + @user_config[:dns_ip_addresses] + " "\
                                              "ipv4.addresses " + ip_address + "/" + @user_config[:subnet_mask] + " "\
                                              "ipv4.gateway " + @user_config[:gateway_ip_address] + " "\
                                              "ipv4.may-fail no"
    end
end

Installing Burp Suite

The last task on our bucket list is to install Burp Suite so that our web application penetration testing machine is ready to go. This also lets us introduce one more feature of Vagrant: Copying files from the host to the guest OS. Once the copying is done, we make the bash file executable and start the command line installation using the unattended mode. Afterwards, we remove the installation file from the /tmp folder.

Vagrant.configure("2") do |config|
    ...
    # Copy the files in burpsuite/ to a folder in /tmp/
    config.vm.provision "file", source: "burpsuite/" + @user_config[:burp_suite_shell_script_name], destination: "/tmp/burpsuite/" + @user_config[:burp_suite_shell_script_name]

    # Run the Burp Suite installation
    config.vm.provision "shell", inline:  "chmod +x /tmp/burpsuite/" + @user_config[:burp_suite_shell_script_name] + " && "\
                                          "/tmp/burpsuite/" + @user_config[:burp_suite_shell_script_name] + " -q -dir /home/vagrant/BurpSuitePro/"

    # Delete the burpsuite folder
    config.vm.provision "shell", inline:  "rm -rf /tmp/burpsuite/"
end

Conclusion

In this article, we have merely scratched the surface of what can be done using Vagrant. Interested readers can find more information about the possibilities of Vagrant in its documentation. We have seen how some configurations are straightforward and easy to write, while others take a lot of time to research and debug until they are working. So, should you start writing Vagrantfiles for all your VMs that you will ever use? It ultimately depends on how complex the configuration is that you try to achieve and how often you want to share or reset the VM. Sometimes the cost of implementing, debugging and maintaining an automated solution is far greater than setting it up once manually. But for example, if you have a team where all members need to work with the same VM or if you plan on wiping and restoring your VM every week and do not want to rely on snapshots, it might well be worth investing the time to write that Vagrantfile.

Bonus: Wallpapers, Wallpapers Everywhere

In this last code snippet, we show you how you can change the wallpapers of Kali Linux. Remember: With great power comes great responsibility.

Vagrant.configure("2") do |config|
    ...
    # Set the wallpapers (yes, this is important). It serves as a visual queue that the provisioning has finished.
    # Move first to /tmp/ since scp user does not have permission to write to /usr/.
    # Then mv to permanent wallpaper folder (shell provisioners are root by default)
    config.vm.provision "file", source: "wallpapers/" + @user_config[:desktop_background_name], destination: "/tmp/wallpapers/" + @user_config[:desktop_background_name]
    config.vm.provision "file", source: "wallpapers/" + @user_config[:desktop_grub_name], destination: "/tmp/wallpapers/" + @user_config[:desktop_grub_name]
    config.vm.provision "file", source: "wallpapers/" + @user_config[:desktop_login_background_name], destination: "/tmp/wallpapers/" + @user_config[:desktop_login_background_name]
    config.vm.provision "file", source: "wallpapers/" + @user_config[:user_image_name], destination: "/tmp/wallpapers/" + @user_config[:user_image_name]

    config.vm.provision "shell", inline:  "mkdir /usr/share/backgrounds/custom/ && "\
                                          "cp -r /tmp/wallpapers/" + @user_config[:desktop_background_name] + " /usr/share/backgrounds/custom/" + @user_config[:desktop_background_name] + " && "\
                                          "cp -r /tmp/wallpapers/" + @user_config[:desktop_grub_name] + " /usr/share/backgrounds/custom/" + @user_config[:desktop_grub_name] + " && "\
                                          "cp -r /tmp/wallpapers/" + @user_config[:desktop_login_background_name] + " /usr/share/backgrounds/custom/" + @user_config[:desktop_login_background_name] + " && "\
                                          "cp -r /tmp/wallpapers/" + @user_config[:user_image_name] + " /usr/share/backgrounds/custom/" + @user_config[:user_image_name]

    config.vm.provision "shell", inline:  "rm -rf /tmp/wallpapers/"

    # Overwrite the default grub images. This is not a "clean" solution, but we also have not found one up to now.
    config.vm.provision "shell", inline:  "ln -sf /usr/share/backgrounds/custom/" + @user_config[:desktop_grub_name] + " /usr/share/grub/themes/kali/grub-16x9.png && "\
                                          "ln -sf /usr/share/backgrounds/custom/" + @user_config[:desktop_grub_name] + " /usr/share/grub/themes/kali/grub-4x3.png && "\
                                          "ln -sf /usr/share/backgrounds/custom/" + @user_config[:desktop_grub_name] + " /boot/grub/themes/kali/grub-4x3.png && "\
                                          "ln -sf /usr/share/backgrounds/custom/" + @user_config[:desktop_grub_name] + " /boot/grub/themes/kali/grub-16x9.png"

    # Kali uses LightDM to manage the greeter (login panel). We overwrite the lines in  the config file of LightDM that change the background and user icon.
    config.vm.provision "shell", inline:  'sed -i \'s/background.*/background = \/usr\/share\/backgrounds\/custom\/' + @user_config[:desktop_login_background_name] + '/\' /etc/lightdm/lightdm-gtk-greeter.conf && '\
                                          'sed -i \'s/default-user-image.*/default-user-image = \/usr\/share\/backgrounds\/custom\/' + @user_config[:user_image_name] + '/\' /etc/lightdm/lightdm-gtk-greeter.conf && '\
                                          'systemctl restart lightdm.service'

    # Kali uses xfce4 for its desktop rendering. The properties ending in "last-image" define the desktop background images for all monitors and workspaces.
    # We set these properties to our chosen image. Note that some properties have to be created (for monitorVirtual1), because they do not exist before the user goes through the login process.
    # The command would fail if the user is still at the login screen and one would try to set these properties without creating them first.
    # The command has to be issued by the non-privileged vagrant user, as root does not see the same X11 displays as the user vagrant.
    config.vm.provision "shell", inline:  "xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitor0/last-image -s /usr/share/backgrounds/custom/" + @user_config[:desktop_background_name] + " && "\
                                          "xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitor1/last-image -s /usr/share/backgrounds/custom/" + @user_config[:desktop_background_name] + " && "\
                                          "xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitorVirtual1/workspace0/last-image -n -t string -s /usr/share/backgrounds/custom/" + @user_config[:desktop_background_name] + " && "\
                                          "xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitorVirtual1/workspace1/last-image -n -t string -s /usr/share/backgrounds/custom/" + @user_config[:desktop_background_name] + " && "\
                                          "xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitorVirtual1/workspace2/last-image -n -t string -s /usr/share/backgrounds/custom/" + @user_config[:desktop_background_name] + " && "\
                                          "xfconf-query -c xfce4-desktop -p /backdrop/screen0/monitorVirtual1/workspace3/last-image -n -t string -s /usr/share/backgrounds/custom/" + @user_config[:desktop_background_name], privileged: false
end

About the Author

Noah Berner

Noah Berner obtained a bachelor’s degree in computer science and a master’s degree in quantum engineering from ETH Zurich. In addition to information security in complex data processing systems, his research interests include quantum key distribution protocols and physical quantum computing architectures. (ORCID 0000-0003-4320-097X)

Links

Are you interested in a Penetration Test?

Our experts will get in contact with you!

×
Lattice-Based Cryptography

Lattice-Based Cryptography

Noah Berner

You want more?

Further articles available here

You need support in such a project?

Our experts will get in contact with you!

You want more?

Further articles available here