8 minute read

A series of notes on setting up a lab using ludus + proxmox to replace the open crto lab sub I had, since they dont offer the 365 day sub anymore. It was a nice little place to test out whatever little thing Id read in the latest DFIR report or something fresh from the badsectorlabs weekly round up.

This post is most likely not helpful to anyone at all as the ludus docs do step you through this, but I wanted to collate it all for future me, incase I need to redeploy from scratch again and dont wish to reread the doco.

This is all being put onto a beefy little NUC I got before stepping out of my life for a fortnight to go on holidays; was a nice little project to ease back into the world with.

Download proxmox, and install it.

I used a program called etcher to put the iso onto a usb and make it bootable

The Proxmox isntallation was painless, SUPER painless. Like easier than my last esxi install. Which is almost slightly disappointing, I was almost looking forward to reusing the esxi revert scripts from long ago. Funny in a way though, that Ludus pretty much does the same kind of thing but to another degree.

When the server comes up, ssh in

The first thing we need to clean the apt lists, cause its gimped as shit

rm /etc/apt/sources.list.d/pve-enterprise.list grep -r 'enterprise.proxmox.com' /etc/apt/ rm whatever comes up from the grep

Install ludus with this very suss looking script. It is perfectly fine however. curl -s https://ludus.cloud/install | bash

We need to PAY ATTENTION during the install and set some things!

Set the vm storage pool to local-lvm, not local! Local is the iso datastore! The format needs to be changed to raw, because local-lvm is a thin provisioned drive and does not support qcow2.

YES we DO want users to be able to add ansible roles! Without this, the seperate range runners will not be able to deploy the specific exchange and sccm playbooks.

Once finished run ludus-install-status to retreive your admin api key

Get the output key and LUDUS_API_KEY='YOUR_ROOT_KEY_HERE' ludus user add --name "james" --userid james --admin --url https://127.0.0.1:8081

This will output the api key for your admin user. write it down, because i havent found how to retreive them, only to reset the key. Im sure its in the docs somewhere, but I am not looking for it.

We now want to create seperate users for each of the ranges you wish to deploy. We will make the admin user created be the “manager”, and the seperate users control their own ranges. This is because each user can only deploy one range at a time, and cobbling together a huge runbook with exchange + sccm + goad meant none of them would succesfully deploy.

LUDUS_API_KEY='YOUR_ROOT_KEY_HERE' ludus --url https://127.0.0.1:8081 user add -n 'SCCM Range' -i SCCM

LUDUS_API_KEY='YOUR_ROOT_KEY_HERE' ludus --url https://127.0.0.1:8081 user add -n 'Exchange Range' -i EXCH

LUDUS_API_KEY='YOUR_ROOT_KEY_HERE' ludus --url https://127.0.0.1:8081 user add -n 'GOAD Range' -i GOAD

Note that these users are not beign created as admins; this is why we needed to set the allow users to add ansible roles during installation, as we want the range deployers to be of lower privs.

This is because ludus is built on top of proxmox, meaning that all the ludus users you create are infact proxmox users. We can get a users creds with ludus user creds get

Be sure to provide your api key, either setting as a var or preppended to the command export LUDUS_API_KEY='KEY_YOU_WANT_TO_USE'

We must edit the dnsmasq config file to give the vmbr1000 interface the ability to hand out dhcp addresses (do check on your host with ip a it has been set to 1000 and not 1001, if so just amend the following config). if we do not do this, prepping of the vm templates will fail and you will be very upset.

file is located at /etc/dnsmasq.conf

interface=vmbr1000 #change number if yours has incremented but it is the adapter sharing the ip address of the server dhcp-range=192.0.2.100,192.0.2.200,12h

Restart dns with systemctl restart dnsmasq It is insanely important to do this, otherwise the config from above wont be loaded, and all your template vms will not be able to get an address.

Log into proxmox as root and go to datacenter, storage, click on local-lvm and add permissions for your users to admin the datastore. Picture may be coming later, I dont know if I can be bothered.

Ignore that and receive the attached as an issue (yes that is for a different datastore and that is how i know we need to change it to local-lvm!)

We will now add some base template machines that can be cloned and deployed when needed. It is best to from here on out set the key for your admin user so these commands dont get out of hand.

git clone https://gitlab.com/badsectorlabs/ludus cd ludus/templates ludus templates add -d win2019-server-x64 ludus templates add -d win2016-server-x64

This will add the as you may guess, windows 2016 and 2019 templates. We need this for exchange and goad.

ludus templates list will show you what templates youve got

ludus templates build (run this as your admin, not root and not as the other users)

It will take ages. This is it getting isos and preparing your “base” vms that the deployments will clone and configure. It is solving an issue I had with the AD Lab in that deploying and configuring together was a nightmare; by cloning the deployment and solely doing configuration work, all goes smoothly.

You can check on the progress of the template deployment with ludus templates logs -f

Once the templates are done (ludus templates list) we can start configuring the ranges per user.

Ranges are defined with .yml files.

EXCHANGE

This right here is why we needed to allow users to add ansible roles

ludus --user EXCH ansible roles add aleemladha.ludus_exchange

Add the following to the exchange config file. I creatively called mine exchange.yml

ludus:

  • vm_name: “-EXC-DC01” hostname: “-DC01” template: win2019-server-x64-template vlan: 20 ip_last_octet: 2 ram_gb: 8 cpus: 4 windows: sysprep: true domain: fqdn: ludus.domain role: primary-dc roles:
    • aleemladha.ludus_exchange

This comes from a bloke whose got a few other nice little ranges

Once the config file is made, we need to set it. Each user can only have one config set, and only one range per user. Thats why we broke each range out to a different manager!

ludus --user EXCH range config set -f exchange.yml

and start the process with ludus --user EXCH range deploy

check on it with ludus --user EXCH range logs -f

exchange takes approx 90 mins

SCCM

Very similar. add the role with the sccm user

ludus --user SCCM ansible roles add synzack.ludus_sccm

Create the config from https://docs.ludus.cloud/docs/environment-guides/sccm/

I once again using my big creative brain called it the creative name of sccm.yml

set and deploy

ludus --user SCCM range config set -f sccm.yml

ludus --user SCCM range deploy

This takes approx 2.5 hours

GOAD

A little different this one. No ansible roles to add however.

Create the big config file from https://docs.ludus.cloud/docs/environment-guides/goad

run with the goad user

ludus --user GOAD range config set -f goad.yml

ludus --user GOAD range deploy

Now assuming you did greate your goad user as goad, the updating commands will be as follows

ludus --user GOAD ludus testing update -n GOAD-GOAD-SRV02 that looks silly but it is what it is

ludus --user GOAD ludus range logs -f keep the logs open and await to see somethign like it says in the doc:

localhost : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
GOAD-GOAD-SRV02 : ok=8 changed=5 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

Install ansible and its requirements for GOAD on the proxmox server

python3 -m pip install ansible-core python3 -m pip install pywinrm git clone https://github.com/Orange-Cyberdefense/GOAD cd GOAD/ansible ansible-galaxy install -r requirements.yml

Create the inventory file in the ansible folder you landed in to install the ansible requirements

Copy it from the ludus page. where it says RANGENUMBER, look up the ip address of your machine via a console session in proxmox. I dont trust the rangenumber var command listed as we are doing a multiuser deployment, something these docs are not quite equipped to handle.

Edit the build.yml file to change the keyboard to en-US, as its french by default.

time to configure

export ANSIBLE_COMMAND="ansible-playbook -i ../ad/GOAD/data/inventory -i ./inventory.yml" export LAB="GOAD" ../scripts/provisionning.sh

The docs reckon a few hours, it took 50 min last time, maybe i had already cached some of what it was downloading

GET ACCESS TO THE RANGES

ludus range access grant --target SCCM --source james

ludus range access grant --target EXCH --source james

ludus range access grant --target GOAD --source james

GETWIREGUARD VPN

The deployments include a kali, I dont use it, I like having these ranges set up so I can quickly try shit I come across in a dfir report or badsectorlabs writeup, so we going to get the vpn config to drop onto your “main” testing machine

ludus user wireguard

add this to /etc/wireguard as a .conf file on the machine you want to add to the vpn

wg-quick up NAME_WITHOUT_CONF_SUFFIX It appends the conf for you. Helpful.

There are steps to snapshot, but Ill be honest Id rather redeploy from scratch then revert vms. By the time its cooked enough to need a revert, theres probs a new version of goad etc.

OSX

Or mac os, I dont know what its called these days. There is an ez to deploy script available from here

A note the github doesnt cover - unless you are using an m1/m2 machine as a base (which would be a mac anyway) you need to use montery as your base, v12, option 5. Any higher and itll sit on the grey apple screen forever and not load, because this is the last version that supports intel cpus.

And thats it, its actually SUPER painless to get it working. this is in stark contrast to that time in like 2010/2011 I sat through putting together a hackintosh laptop. It was not worth the effort thats for sure. Anyway that is all, join me as I steamroll through the todo list on my whiteboard I have ignored for approx 18 months due to monster depression!

mcbain