Unattended Kali Linux installation

This post is all about setting up an automatic Kali Linux installation with packer and/or vagrant

General Information

The following article assumes the use of Kali Linux & Virtualbox. Vmware is not included, so changes for this have to be done manually.

Setting up a Vagrantfile as configuration for your pentesting environment is particularly useful to set up a machine on the get-go. When a machine is compromised, multiple pentesting environments with a default configuration are needed or you just want to be able to spawn new ready-to-go pentesting machines on other devices, you can just copy the Configuration file(s), run (packer &) vagrant and you're all set.

When no configuration for the installation process of the Kali image is necessary you may as well proceed right to the Vagrantfile configuration step

Technically you could do all the configuration when creating a base image with packer, but this article focuses on creating a base image with custom installation options and then modifying the system afterwards with vagrant. This allows you to have a custom base installation, while being able to automate the configuration with vagrant afterwards. So if you have to change some configuration, but need the same base installation, you don't have to run through the whole installation process again.

vagrant: tool to import boxes from either the official repositories or self-made

packer: tool to create self-made boxes (from an iso image)

Creating a custom Kali Linux image with packer

This template will be useful in order to pass custom instructions to the installation of Kali Linux, which will be automatically executed when the vm starts.

The following configuration will create a basic Kali Linux installation, you can adjust the settings as needed. Just refer to the mentioned template and add the options accordingly.

packer.json: configuration for the packer binary

preseed.cfg: configuration for the installer (here: Kali Linux)

After using packer to create a box, note that the vagrant user will have sudo permissions without requiring a password. This will be revoked when using the pre-configured Vagrantfile from this article. However if another Vagrantfile is used, you should take into account that you'll have to revoke this permission in order to have a save system.

Folder structure

The folder structure matters when the installer trys to load the preseed.cfg from the localhosts http server (that will be created automatically).

According to packer when working with a preseed file the structure should be as follows:

project_dir/packer.json
project_dir/http/preseed.cfg

Packer will be execute from project_dir.

preseed.cfg

# Preseeding only locale sets language, country and locale.
d-i debian-installer/locale string en_US

# Keyboard selection.
d-i console-setup/ask_detect boolean false
d-i keyboard-configuration/xkb-keymap select us

choose-mirror-bin mirror/http/proxy string

### Clock and time zone setup
d-i clock-setup/utc boolean true
d-i time/zone string UTC

# Avoid that last message about the install being complete.
d-i finish-install/reboot_in_progress note

# This is fairly safe to set, it makes grub install automatically to the MBR
# if no other operating system is detected on the machine.
d-i grub-installer/only_debian boolean true

# This one makes grub-installer install to the MBR if it also finds some other
# OS, which is less safe as it might not be able to boot that other OS.
d-i grub-installer/with_other_os boolean true

### Mirror settings
# If you select ftp, the mirror/country string does not need to be set.
d-i mirror/country string manual
d-i mirror/http/hostname string http.kali.org
d-i mirror/http/directory string /kali
d-i mirror/http/proxy string

### apt setup
d-i apt-setup/kali-rolling boolean true
d-i apt-setup/non-free boolean true
d-i apt-setup/contrib boolean true
d-i apt-setup/main boolean true

### Partitioning
d-i partman-auto/method string lvm
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true

# This makes partman automatically partition without confirmation.
d-i partman-md/confirm boolean true
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true

### Account setup
d-i passwd/user-fullname string vagrant
d-i passwd/user-uid string 1000
d-i passwd/user-password password vagrant
d-i passwd/user-password-again password vagrant
d-i passwd/username string vagrant

# The installer will warn about weak passwords. If you are sure you know
# what you're doing and want to override it, uncomment this.
d-i user-setup/allow-password-weak boolean true
d-i user-setup/encrypt-home boolean false

### Package selection
tasksel tasksel/first standard
d-i pkgsel/include string openssh-server coreutils build-essential
d-i pkgsel/install-language-support boolean false

# disable automatic package updates
d-i pkgsel/update-policy select none
d-i pkgsel/upgrade select none

# enable sudo without password for vagrant and start ssh for further configuration done by the Vagrantfile
d-i preseed/late_command string \
in-target systemctl enable ssh ; \
in-target usermod -G sudo vagrant ; \
echo "%vagrant ALL=NOPASSWD:ALL" > /target/etc/sudoers.d/vagrant ; \
echo "Defaults:vagrant !requiretty" >> /target/etc/sudoers.d/vagrant ; \
in-target chmod 440 /etc/sudoers.d/vagrant

packer.json

Consider changing the following options:

  • iso_urls => which kali image should be used (local path is also possible); images can be found here

  • iso_checksum => checksum of the iso file (e.g. sha1, sha256 etc.)

  • disk_size => size of the virtual disk to be created

  • vboxmanage => hardware configuration for virtualbox (e.g. vram, ram, cpus etc.)

{
  "builders": [
    {
      "boot_command": [
        "<esc><wait>",
        "/install.amd/vmlinuz<wait>",
        " auto<wait>",
        " console-setup/ask_detect=false<wait>",
        " console-setup/layoutcode=us<wait>",
        " console-setup/modelcode=pc105<wait>",
        " debconf/frontend=noninteractive<wait>",
        " debian-installer=en_US<wait>",
        " fb=false<wait>",
        " initrd=/install.amd/initrd.gz<wait>",
        " kbd-chooser/method=us<wait>",
        " netcfg/choose_interface=eth0<wait>",
        " console-keymaps-at/keymap=us<wait>",
        " keyboard-configuration/xkb-keymap=us<wait>",
        " keyboard-configuration/layout=USA<wait>",
        " keyboard-configuration/variant=USA<wait>",
        " locale=en_US<wait>",
        " netcfg/get_domain=vm<wait>",
        " netcfg/get_hostname={{.Name}}<wait>",
        " grub-installer/bootdev=/dev/sda<wait>",
        " noapic<wait>",
        " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg auto=true priority=critical",
        " -- <wait>",
        "<enter><wait>"
      ],
      "boot_wait": "10s",
      "guest_os_type": "Debian_64",
      "http_directory": "http",
      "disk_size": 40960,
      "iso_checksum": "ef83bafe1f19088666a8080d7ea07bd4f8da2fda0fcb3ef5f2ce2658f349c119",
      "iso_urls": [
          "kali-linux-2021.2-installer-amd64.iso",
          "https://cdimage.kali.org/kali-2021.2/kali-linux-2021.2-installer-amd64.iso"
      ],
      "output_directory": "virtualbox-iso",
      "shutdown_command": "echo 'vagrant' | sudo -S shutdown -P now",
      "ssh_username": "vagrant",
      "ssh_password": "vagrant",
      "ssh_wait_timeout": "10000s",
      "type": "virtualbox-iso",
      "vm_name": "packer-kali",
      "vboxmanage": [
        ["modifyvm", "{{.Name}}", "--memory", "2048"],
        ["modifyvm", "{{.Name}}", "--cpus", "1"],
        ["modifyvm", "{{.Name}}", "--vram", "64"]
      ]
    }
  ],
  "provisioners": [
      {
        "type": "shell",
        "inline": [""]
      }
  ],
  "post-processors": [
      {
        "output": "builds/{{.Provider}}-kali.box",
        "type": "vagrant",
        "only": ["virtualbox-iso"]
      }
  ]
}

Building the box

With the configuration provided here, this will create a new directory and export the installed image to this directory (which can be found at builds/virtualbox-kali.box)

packer build packer.json

Configure the Kali Linux image with vagrant

Configuration options for Vagrantfiles can be found here. Something I find particularly useful are triggers, which allow you for example to execute commands before and after vagrant up.

Vagrantfile

The following configuration pulls the latest official vagrant-kali image, upgrades it and installs some packages that can be defined in the $installsoftware variable.

Consider changing the following option:

  • config.vm.provider "virtualbox" => hardware configuration for virtualbox (e.g. vram, ram, cpus etc.)

$upgrade = <<-SCRIPT
DEBIAN_FRONTEND=noninteractive apt update
# the following command omits the grub-pc prompt when upgrading (might be version specific)
echo "set grub-pc/install_devices /dev/sda" | debconf-communicate
DEBIAN_FRONTEND=noninteractive apt -y upgrade
SCRIPT

$installsoftware = <<-SCRIPT
# additional software to be installed goes here
SCRIPT

$vagrantcleanup = <<-SCRIPT
echo "#!/bin/bash\nrm /etc/sudoers.d/vagrant\nsystemctl disable ssh\nrm VBoxGuestAdditions.iso\nrm cleanup.sh && shutdown -P now" > cleanup.sh
chown root:root cleanup.sh  
chmod +sx cleanup.sh   
./cleanup.sh
SCRIPT

Vagrant.configure("2") do |config|
    config.vm.box = "kalilinux/rolling"
    config.vm.hostname = "kali"
    
    config.vm.provider "virtualbox" do |vb|
        vb.gui = true
        vb.cpus = 1
        vb.name = "kali"
        vb.memory = "2048"
    end
    
    config.ssh.username = "vagrant"
    config.ssh.password = "vagrant"
    
    config.vm.provision "shell", inline: $upgrade
    config.vm.provision "shell", inline: $installsoftware
    
    # do this one last
    config.vm.provision "shell", inline: $vagrantcleanup
    
    # Additional VBoxManage configuration
    # more information about VBoxManage are in the documentation (link in the references).
    # To set some options you have to shutdown the machine first.
    #
    # via trigger
    #
    # config.trigger.after :up do |trigger|
    #     trigger.info = "Setting up some configuration"
    #     trigger.ruby do |env,machine|
    #         # the following command is used to unlock the machine state, by stopping it ungracefully (workaround)
    #         # you can also do this by using a shutdown command and implement a small delay here 
    #         puts `VBoxManage startvm #{machine.id} --type emergencystop`
    #         puts `VBoxManage modifyvm #{machine.id} --some-option`
    #     end
    # end
    #
    # or via vagrant (note that this won't be necessarily executed at the end of the configuration;
    # use a trigger for this)
    #
    # config.vm.provider "virtualbox" do |vb|
    #     vb.customize ["modifyvm", :id, "--some-option"]
    # end
end

Installation

In order to have a "save" installation the default password of user vagrant has to be changed.

Installing an already existing box

When using the configuration from here, this will create a pre-configured kali linux image from the official website.

The Vagrantfile has to be in the current directory or any subdirectory starting at VAGRANT_CWD

vagrant up

Importing a self-made box and installing it

This will be necessary when a custom box has been created at this step

change the name of the box in the Vagrantfile

config.vm.box = "custom/kali"

add and install the box

vagrant box add --name custom/kali builds/virtualbox-kali.box
vagrant up

Automating the process

The following script is a quick & dirty solution to automate the installation process based on the files in the current directory and the boxes already imported into vagrant

  • if overwrite yes => run packer (and overwrite base image $boxpath (file) and $boxname (vagrant import) )

  • if $vagrantfile exists => run vagrant

    • if there's already an image in virtualbox with the same name/id, vagrant will most likely fail here

#!/bin/bash
boxpath="builds/virtualbox-kali.box"
boxname="custom/kali"
packerfile="packer.json"
vagrantfile="Vagrantfile"

echo "Overwrite $boxpath (file) and $boxname (vagrant import), if they exist."
echo -n "Proceed? [y/n]: "
read answer
[[ $answer == 'y' ]] && {
    packer build -force $packerfile
    boxes=$(vagrant box list)
    [[ $boxes == *$boxname* ]] && vagrant box remove -f --all $boxname
    [[ -e $boxpath ]] && vagrant box add $boxpath --name $boxname
}

[[ -e $vagrantfile ]] && vagrant up

References

Last updated