Building a Kali Box with Vagrant+Ansible (Part One)

Every information security specialist is going to have a preferred penetration testing distro and toolset. But it’s important to be able to quickly deploy a fresh image for each engagement.

One approach is to build a base image and then use snapshots to manage various machine states. I have used this approach in the past, but it does nothing to solve the problem of sharing images between hosts. It also makes it difficult to keep the base image updated.

In this article I’m going to walk through an alternative approach for building and managing a custom penetration testing image using Vagrant and Ansible.

Future articles will go into greater detail on using the power of Ansible to customize your image.

Overview of Steps

  1. Install Homebrew / Chocolatey
  2. Install Virtualbox and Vagrant
  3. Create Vagrantfile
  4. Customize Vagrantfile
  5. Share Vagrantfile
  6. Get Hacking

Install Homebrew / Chocolatey

Installing a package manager for MacOS or Windows will greatly simplify installing Vagrant and VirtualBox.

Homebrew (MacOS)

/usr/bin/ruby -e "$(curl -fsSL"

For troubleshooting and details on Homebrew, go to

Chocolatey (Windows)

From an administrative command shell (cmd.exe)…

@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString(''))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"

For troubleshooting and details on Chocolatey, go to

Install Vagrant


brew cask install virtualbox
brew cask install vagrant


apt-get install virtualbox
apt-get install vagrant


choco install virtualbox
choco install vagrant

Create Vagrantfile

To start, we need to identify the base box we want to use. For a list of Kali Linux boxes available from HashiCorp (the creators of Vagrant), check out For this article, we are going to use the official Kali Linux Light box from Offensive Security: offensive-security/kali-linux-light

Creating the initial Vagrantfile is simple. Navigate to the base directory for the new box. Then run…

vagrant init offensive-security/kali-linux-light

This will create an initial Vagrantfile with a lot of comments, but the gist of it will look like…

Vagrant.configure("2") do |config| = "offensive-security/kali-linux-light"

At this point, you can run…

vagrant up

Wait patiently while vagrant download and configures the base box (1.1 GB)…

 kali-light ryanjo$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Box 'offensive-security/kali-linux-light' could not be found. Attempting to find and install...
    default: Box Provider: virtualbox
    default: Box Version: >= 0
==> default: Loading metadata for box 'offensive-security/kali-linux-light'
    default: URL:
==> default: Adding box 'offensive-security/kali-linux-light' (v2019.1.0) for provider: virtualbox
    default: Downloading:
    default: Download redirected to host:
==> default: Successfully added box 'offensive-security/kali-linux-light' (v2019.1.0) for 'virtualbox'!
==> default: Importing base box 'offensive-security/kali-linux-light'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'offensive-security/kali-linux-light' version '2019.1.0' is up to date...
==> default: Setting the name of the VM: vagrant-article_default_1552931692498_58563
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address:
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Mounting shared folders...
    default: /vagrant => /Users/ryanjo/git/kali-light

After the machine boots, VirtualBox will start the GUI or you can ssh into the box by typing…

vagrant ssh

Customize Vagrantfile

So far we have a basic Kali Light box. If that’s all you want then you can stop here, but the real power comes from being able to configure and provision the base box.


First, let’s change how the network interface is configured. By default, the VirtualBox provider creates a single network interface using NAT. We can add an additional public network by adding…

Vagrant.configure("2") do |config| = "offensive-security/kali-linux-light" "public_network"

Whenever we make changes to our Vagrantfile, we run…

vagrant provision

Vagrant creates a second bridged network interface and is assigned an IP address from DHCP. If the host machine has more than one interface, it will prompt you to choose one. You can also specify an IP address by changing the additional line to…

Vagrant.configure("2") do |config| = "offensive-security/kali-linux-light" "public_network",ip: ""


Now let’s specify some additional software. Because Kali Light is a stripped down build, it doesn’t come with the full Kali toolset. We’ll use the Ansible Local provisioner, which will install Ansible on the Kali guest so that we don’t have to install it on our host machine.

Modify your Vagrantfile to add an ansible_local provisioner…

Vagrant.configure("2") do |config| = "offensive-security/kali-linux-light" "public_network",ip: ""

config.vm.provision "ansible_local" do |ansible|
ansible.playbook = "playbook.yml"

Next, create a file called playbook.yml in the same directory as your Vagrantfile. To start, it should look like this…

- hosts: all
become: yes
- name: Update repositories cache
    update_cache: yes

This playbook will apply to all hosts (in this case, our single Kali box), run with sudo, and execute a single play that executes apt-get update.

Now, if we run…

vagrant provision

…we’ll see that the new Ansible playbook runs.

 ==> default: Running provisioner: ansible_local...

    default: Running ansible-playbook...

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [default]

TASK [Update repositories cache] *********************
ok: [default]

PLAY RECAP *********************************************************************
default                    : ok=2    changed=0    unreachable=0    failed=0   

This shows that our new playbook ran successfully. Now we can start adding additional tasks to our playbook. For example, to add a task that installs dirb and sqlmap, we would add to our playbook.yml…

- hosts: all
become: yes
- name: Update repositories cache
    update_cache: yes

- name: Install a list of packages
    name: "{{ packages }}"
    - dirb
    - sqlmap

There is a great deal more that can go into creating an Ansible playbook, which I will cover in more detail in a future post.

Share Vagrantfile

This step is optional, but it’s also one of the biggest benefits of using Vagrant and Ansible: you can share your Vagrantfile (and linked playbook.yml) between hosts and collaborators, streamlining the process of spinning up identical working environments.

GitHub is a great way to share the files, just remember to include both the Vagrantfile and playbook.yml. As you begin creating more advanced setups, you’ll want to make sure you also include your inventory and ansible.cfg files (along with any additional playbooks).

Get Hacking

Once you have your playbook set up the way you want and installing all of the packages you need, you can use the resulting virtual machine just like you normally would. In future posts, I’ll get into more advanced configuration and exporting fully configured boxes. Until next time, happy hacking!

Sample files from Part 1 can be found here:

Part Two:

Leave a Reply

Your email address will not be published. Required fields are marked *