Vagrant - Eine Burp Suite VM To Go, Bitte

Vagrant

Eine Burp Suite VM To Go, Bitte

Noah Berner
von Noah Berner
am 08. Dezember 2022
Lesezeit: 17 Minuten

Keypoints

Eine automatisierte Art, um eine Burp Suite VM zu erstellen

  • Vagrant ist eine Open-Source-Software um virtuelle Maschinen automatisiert zu konfigurieren
  • Verschiedene Konfigurationen können mit unterschiedlichen Ansätzen automatisiert werden
  • Einige Konfigurationen brauchen ein vertieftes Wissen des Gastbetriebssystems
  • Das Beheben von Fehlern in einem Vagrantfile kann zeitraubend sein, sich aber dennoch lohnen für gewisse Ansprüche an virtuelle Maschinen

Wenn Sie sich schon einmal gewünscht haben, dass Sie die Konfiguration Ihrer virtuellen Maschine für Web Penetration Testing mitnehmen und auf jedem beliebigen Host wieder aufsetzen können, dann ist dieser Artikel etwas für Sie. Wir schauen uns Vagrant an, eine Applikation, welche die Konfiguration von virtuellen Maschinen automatisiert. Dazu werden wir ein Vagrantfile erstellen, dass uns das Aufsetzen einer Kali Linux VM mit Burp Suite in wenigen Minuten erlaubt.

Vagrant ist eine Open-Source-Software, welche virtuelle Maschinen aufgrund einer Beschreibungsdatei reproduzieren kann. Diese Datei wird Vagrantfile genannt und sie erhält sämtliche Informationen, die von Vagrant benötigt werden, um die virtuelle Maschine herunterzuladen, aufzusetzen, zu starten und so einzurichten wie Sie von Ihnen konfiguriert wurde. Die Macht von Vagrant liegt darin, dass sobald dieses Vagrantfile geschrieben wurde, diese VM überall hin mitgenommen werden und mit nur einem Command vagrant up innert Minuten aufgesetzt werden kann. Das funktioniert auch, wenn man es geschafft hat, dass seine VM dysfunktional wurde. Zerstören Sie einfach die VM mit vagrant destroy und starten Sie erneut mit vagrant up. Die VM wird so gut wie neu sein!

Was ist ein Vagrantfile?

Das Vagrantfile sieht am Anfang immer etwa gleich aus:

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

Die 2 steht für die verwendete Konfigurationsversion. Die einzige Konfiguration, welche wir bis jetzt vorgenommen haben, ist die Auswahl einer Box. Die Box kann man sich als eine leere Vorlage vorstellen, welche Vagrant einmal herunterlädt und dann speichert. Wenn wir Vagrant anweisen, eine VM zu starten, wird es einen Klon dieser heruntergeladenen Box erstellen und diesen Klon konfigurieren. Der Vorteil dieser Herangehensweise ist, dass Vagrant nur einmal die VM herunterladen muss (sofern keine neue Version herausgebracht wird) und dadurch die Zeit beim Start reduziert wird. In unserem Beispiel verwenden wir Kali Linux als Basisbox für unsere VM. Weitere Boxen können hier gefunden werden.

Wir möchten vielleicht auch einige Ordner zwischen unserem Host- und Gastbetriebssystem teilen.

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

Wir iterieren über alle spezifizierten shared_folders und benutzen die config.vm.synced_folder Direktive um Vagrant mitzuteilen welche Pfade auf dem Host auf welche Pfaden auf dem Gast abgebildet werden sollen. Wir deaktivieren zudem noch den geteilten Ordner /vagrant, welcher standardmässig mit dem Gast geteilt, von uns aber nicht gebraucht wird. Einige der Konfigurationsvariablen haben wir in die separate Datei user_config.rb verschoben. Das kann helfen, wenn das Vagrantfile mit Teammitgliedern geteilt werden soll, welche einige wenige Anpassungen machen möchten. Die restlichen Variablen in user_config.rb werden weiter unten im Artikel verwendet und erklärt.

# 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

Um eine VM zu starten verwendet Vagrant so genannte Provider. In unserem Fall wollen wir VMware Workstation auf einem Windows-Host verwenden. Vagrant kann auch mit vielen weiteren Providern, wie zum Beispiel VirtualBox, Docker und anderen verwendet werden. Es hat auch die Möglichkeit programmatisch Einstellungen in VMware vorzunehmen, welche normalerweise über das VMware-Einstellungs-GUI vorgenommen würden. Es gilt zu beachten, dass diese Einstellungen für jeden Provider verschieden und für VMware zusätzlich auch noch undokumentiert sind und deshalb auch ohne Vorwarnung geändert werden können. Dies kann dazu führen, dass das Vagrantfile nach einem Update von VMware nicht mehr vollständig funktioniert. Wir konfigurieren unseren Provider wie folgt:

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

Wir setzen den Namen, die Anzahl Prozessoren und das Arbeitsspeicherkontingent unserer VM in VMware Workstation auf die Werte welche in user_config.rb definiert wurden. Wir konfigurieren auch noch einen USB-Controller, welchen wir für unseren nächsten Schritt benötigen.

Sicherheit und Kommunikation

Bevor wir uns jedoch dieser nächsten Aufgabe widmen, sollten wir einen kurzen Blick darauf werfen wie Vagrant mit dem Gast-OS kommunizieren kann. Es startet dazu einen SSH-Server auf dem Gast, mit welchem es sich verbindet und Befehle auf dem Gast ausführt. Die erste SSH-Verbindung geschieht mit einem unsicheren privaten Schlüssel, welcher allgemein bekannt ist. Bei dem ersten vagrant up ersetzt Vagrant dieses unsichere Schlüsselpaar mit einem zufällig generierten Schlüsselpaar, was die Sicherheit der SSH-Verbindung stärkt.

Vagrant verwendet das Passwort vagrant für die beiden Nutzer root und vagrant. Wenn die VM für mehr als nur Experimente verwendet wird sollten diese Passwörter geändert werden.

Netzwerkkonfiguration

Wir wollen nun einige USB-Ethernet-Dongles so in der VM konfigurieren, dass Sie immer auf ein spezifisches Set an statischen IP-Adressen gebunden werden. Diese Augabe soll als Beispiel für einen komplexe Netzwerkkonfiguration dienen. Um unser Ziel zu erreichen wollen wir den NetworkManager und sein Command-Line-Interface nmcli verwenden. Der NetworkManager ist das Tool, welche in Kali Linux die grafische Netzwerkkonfiguration zur Verfügung stellt. Diese Art von Konfiguration kann in Vagrant auf mehrere Arten durchgeführt werden. Dazu werden sogenannte Provisioners verwendet. Für den Moment bleiben wir bei den simplen shell-Provisioners, welche einen Shell-Command über die SSH-Konfiugration ausführen. Diese Netzwerkkonfigurationsaufgabe zeigt einige der Nachteile und Frustrationspunkte von Vagrant auf.

Vagrant benutzt standardmässig ifupdown, ein anderes Netzwerkkonfigurationswerkzeug, um das Interface eth0 als ein NAT-Interface zu konfigurieren. Durch das Benutzen von ifupdown wird das Interface eth0 für den NetworkManager unzugänglich. Wir müssen zuerst alle von ifupdown platzierten Einträge über eth0 in /etc/network/interfaces löschen und dann den NetworkManager neu starten um ihm die Kontrolle über eth0 zurückzugeben. Nur dann wird der NetworkManager korrekt laufen und wir können unsere spezifische Netzwerkkonfiguration mittels nmcli einfügen. Wie man sich vorstellen kann, ist das Finden von Problemen in einer solchen Umgebung zeitaufwändig, weil wir nach jeder Änderung die VM neu starten müssen, um das Resultat zu überprüfen. Zudem brauchen einige Konfigurationen tiefgründiges Wissen über das Gastbetriebssystem was den Prozess zusätzlich verlängert und die Fehlerbehebung frustrierend und ineffizient machen kann.

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

Burp Suite installieren

Die letzte Aufgabe auf unserer Liste ist die Installation von Burp Suite, damit man gleich nach dem Aufstarten der VM mit dem Web-Application-Penetration-Testing beginnen kann. So können wir auch noch ein weiteres Feature von Vagrant vorstellen: Das Kopieren von Dateien vom Host auf das Gast-OS. Wenn das Kopieren beendet ist, machen wir die Bash-Datei ausführbar und starten die Command-Line-Installation im unattended Modus. Danach löschen wir die Installationsdatei aus dem /tmp Ordner.

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

Zusammenfassung

In diesem Artikel haben wir nur einen kleinen Teil der Funktionalität von Vagrant angeschaut. Interessierte Leser können mehr Informationen in der Dokumentation von Vagrant finden. Wir haben gesehen, dass einige der Konfigurationen unkompliziert zu implementieren sind, während andere viel Zeit für die Nachforschung und das Debugging brauchen bis sie laufen. Sollten wir nun also immer ein Vagrantfile für all unsere VMs schreiben? Letzten Endes kommt es auf die Komplexität der Konfiguration an und wie oft man die VM zurücksetzen oder teilen möchte. Manchmal überwiegen die Kosten der Implementation, der Fehlerbehebung und der Unterhaltung einer automatisierten Lösung gegenüber dem einmaligen manuellen Aufsetzen. Aber wenn man zum Beispiel ein Team hat, in dem alle Mitglieder auf der gleichen VM-Konfiguration arbeiten müssen oder wenn man plant die VM jede Woche neu aufzusetzen und sich nicht auf Snapshots verlassen möchte, kann es gut sein, dass es sich lohnt dieses Vagrantfile zu schreiben.

Bonus: Wallpapers, Wallpapers Everywhere

In diesem letzen Code-Ausschnitt, zeigen wir Ihnen, wie Sie die Wallpaper von Kali Linux konfigurieren können. Denken Sie dran: 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

Über den Autor

Noah Berner

Noah Berner schloss sein Studium an der ETH Zürich mit einem Bachelor of Science in Informatik und einem Master of Science in Quantum Engineering ab. Neben der Informationssicherheit in komplexen Datenverarbeitungssystemen gehören Quantum-Key-Distribution-Protokolle und physische Quantencomputer-Architekturen zu seinen Forschungsinteressen. (ORCID 0000-0003-4320-097X)

Links

Haben Sie Interesse an einem Penetration Test?

Unsere Spezialisten kontaktieren Sie gern!

×
Gitterbasierte Kryptographie

Gitterbasierte Kryptographie

Noah Berner

Sie wollen mehr?

Weitere Artikel im Archiv

Sie brauchen Unterstützung bei einem solchen Projekt?

Unsere Spezialisten kontaktieren Sie gern!

Sie wollen mehr?

Weitere Artikel im Archiv