(In case you’re wondering, yes! This is a hat tip to the lovely book by Al Sweigart, Automate The Boring Stuff With Python)
In part one, I set up a harness for automating recon activities of a pentest using a container, Terraform and Ansible. Part two focuses on automating scans. For scanning, I’ll spin up a VM on my hypervisor and get it to automatically scan an IP address range.
Sample code
As a reminder, all code related to this post is published here: https://github.com/Inf0Junki3/automate-pentest-example.
Spinning up a VM
As with my past post, I have two folders in my project - one for terraform, and one for ansible:
project
|
+- terraform
| |
| +- main.tf
|
+- ansible
| |
| +- ansible.cfg
| |
| +- site.yml
|
+- config.yml
The terraform piece part takes care of spinning up the VM in my proxmox lab environment. And ansible takes care of installing the packages and running the scan.
# Set up some variables. We'll specify non-confidential information in a config.yml file.
variable "config_file_path" {
type = string
}
locals {
configuration = yamldecode(file(var.config_file_path))
}
# These are confidential variables. You can set them up in GitHub secrets, Jenkins credentials, etc
variable "proxmox_url" {
type = string
sensitive = true
}
variable "proxmox_username" {
type = string
sensitive = true
}
variable "proxmox_password" {
type = string
sensitive = true
}
variable "proxmox_node" {
type = string
sensitive = true
}
variable "proxmox_base_image" {
type = string
sensitive = true
}
terraform {
required_providers {
proxmox = {
source = "telmate/proxmox"
version = "2.9.11"
}
}
}
# This sets up the proxmox provider.
provider "proxmox" {
pm_api_url = var.proxmox_url
pm_user = var.proxmox_username
pm_password = var.proxmox_password
pm_tls_insecure = local.configuration.terraform.proxmox_use_insecure
}
# This allows you to span the assessment machine in your instance.
resource "proxmox_vm_qemu" "assessment_machine" {
name = local.configuration.assessment_name
target_node = var.proxmox_node
clone = var.proxmox_base_image
full_clone = true
os_type = "cloud-init"
cores = local.configuration.terraform.vm_cores
memory = local.configuration.terraform.vm_memory
agent = local.configuration.terraform.vm_install_guest_agent
disk {
size = local.configuration.terraform.vm_disk_size
storage = "local-lvm"
type = "scsi"
}
sshkeys = local.configuration.terraform.ssh_keys
}
# Now, we write the resulting IPv4 address to the inventory.
resource "local_file" "ansible_inventory" {
content = templatefile("inventory.tmpl", {
machine_name = local.configuration.assessment_name
machine_ip = proxmox_vm_qemu.assessment_machine.default_ipv4_address
machine_user = local.configuration.terraform.vm_user
})
filename = "../ansible/inventory.yml"
}
Now that you have the main.tf file written to your terraform directory, you can navigate to that directory and run it:
cd terraform
terraform init
terraform apply -state=acme.state
terraform apply
var.config_file_path
Enter a value: ../config.yml
var.proxmox_base_image
Enter a value: ubuntu-fossa-with-agent
var.proxmox_node
Enter a value: some-node
var.proxmox_password
Enter a value: some-password
var.proxmox_url
Enter a value: https://some-node:8006/api2/json
var.proxmox_username
Enter a value: root@pam
[...]
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
proxmox_vm_qemu.assessment_machine: Creating...
proxmox_vm_qemu.assessment_machine: Creation complete after 1m26s [id=some-node/qemu/111]
local_file.ansible_inventory: Creating...
local_file.ansible_inventory: Creation complete after 0s [id=b6ae1722d1c6fe9cf71f6c1b5fd874de72b29eb2]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
When successful, this spins up a VM and builds your inventory file for you.
Setting yourself up for automated scanning
Our config file from last time is fairly similar but now has some extra non-confidential parameters for proxmox:
terraform:
proxmox_use_insecure: true
vm_cores: 1
vm_memory: 1024
vm_install_guest_agent: 1
vm_user: ubuntu
vm_disk_size: 20G
ssh_keys: ssh-rsa mypubkeyhere
scan_type: passive
scan_timeout_seconds: 7200
assessment_name: acme
domains:
- idontexist.home.arpa
ip_ranges:
- 192.168.1.1/32
Then, I execute the commands in my VM with ansible:
---
- name: Run scanning from a container
hosts: all
tasks:
- name: Grab variables from the config file
ansible.builtin.include_vars:
file: ../config.yml
- name: Install nmap
ansible.builtin.apt:
update_cache: true
name:
- nmap
- name: Run port scan
ansible.builtin.command:
cmd: "nmap -A -oA /tmp/results {{ ip_ranges | join(' ') }}"
register: nmap_scan
changed_when: nmap_scan.rc == 0
- name: Grab the results.xml file
ansible.builtin.fetch:
src: /tmp/results.xml
dest: /tmp/scan_results
To run this, I navigate to my ansible folder:
cd ../ansible
pipenv shell
# This command need only be run once:
pipenv install
ansible-playbook site.yml
Once this is run, I can access my scan results on my local machine in /tmp/scan_results.
Next post, I’ll explain how this approach can help scale up the repetitive parts of a pentest. We’ll see how these scripts can be set up as repo templates and how they can be integrated with a CI/CD system to run automatically.