Introducing Axiom - The Dynamic Pwnstation Orchestrator for Red Team & Bug Bounty

Introducing Axiom - The Dynamic Pwnstation Orchestrator

Hello, 0x00ers!

In this article, I’m going to discuss my new tool Axiom, a few of the concepts around the project, and some potential use cases. I’ve tried to build Axiom to be as extendable as possible so that power users can really use this new tool as an abstraction to use cloud computing systems into their workflows.

The repository can be found below:

Before we get into this article, if you like this creation and you appreciate the time I’ve spent developing it and refining it to give it away, please use my Digital Ocean Referral link if you’re new to DigitalOcean, this key has a $100 of free credit along with it, that’s enough to run 4 droplets for 4 months!

If you already have an account, you can always buy me a coffee at:

The Problem

If you’re like me, you might already have a hacking VPS setup, and it will have an array of tools. It might also have a very messy scattering of different nmap files, masscan files, and general enumeration stuff. Hacking VPS’s are usually for catching shells, running port scans and hosting malware/c2.

Now the problem with this is:

  • If you’re hacking and scanning a lot, you can get IP Blacklisted / Burned
  • Your budget is probably not a lot more than $5-$20 a month, so your VPS won’t be particularly powerful
  • You don’t need it a lot of the time, necessarily, you only really need it for the odd scan and shell catch, but you certainly don’t need it to run 24/7
  • You might want to give somebody access, like a coworker, but want to remove it later

Until now, you could solve this problem only by spending months building an automation setup and learning all the new technologies to automate the deployment of cloud instances to do exactly what you need.

The good news is, I’ve done this, and I’ve packaged it up so that you don’t have to (and hopefully we’ll work together and make it better)

Axiom High level

Axiom has three core concepts to consider. You have instances, boxes, and images.

An instance is a digital ocean droplet, it is an initialized VPS hosted in Digital Ocean. This has an IP address, it can be ssh’d into, it can be deleted whenever we want.

An image is a snapshot of a configured DigitalOcean droplet. Axiom uses a tool known as “packer” that will initialize a new DigitalOcean droplet, run a set of predefined scripts to install tools and configure the server, and then snapshot that instance so that we can use it again. This snapshot is universally known as an image.

A box is a backup-lite if you like. Inside ~/.axiom/boxes/backup-file.txt is a list of files that are to be included and excluded from backups. Simple configuration files such as vim configs, zsh history, zsh config, and the go binary directory are all backed up and restored through boxes. Inside this file also, there is wildcard “*” that specifies every file, and then a few exclusions are made (like cache directories).

Boxes are located at ~/.axiom/boxes/, and can be deleted by just deleting the folder. They can also be deleted with axiom-boxes rm box .

Typical Axiom Workflow

At a high level, this is how a typical axiom workflow would work.

Manual Installation

To install Axiom, you just have a few things you need to understand first. Axiom is just bash, and as such, it relies on other dependencies to actually work. This is one of the shortcomings of bash, but it is also it’s greatest strength. You can build something very quickly and publish it.

The key dependenices are jq, fzf, doctl and packer . The exact versions are on the GitHub, and they are important. If you use an old version of jq, packer or doctl, it could break the entire setup. Fzf is not strictly needed but it is recommended if you’re lazy.

The second thing you need to is clone the git repo into ~/.axiom/, this directory contains the entire axiom installation. Ever want to delete axiom? Just delete this directory and it’ll be gone!

The third thing you need to do is set up your axiom.json, this is the config file and is located in ~/.axiom/axiom.json. This file is auto-generated by the axiom-configure script.

The fourth thing you need to do is kick off your first packer build. To do this, just run axiom-build .

Automated Installation

Now if you don’t want to install all this manually, you can run the one-liner, officially this is only supported on Ubuntu Desktop, Linux Mint, and Arch. However, it won’t get you the dependencies for Arch, you’ll have to get those yourself for now.

Just run this one-liner and let it do its thing. You’ll need a DigitalOcean Personal Access Token, you can generate that here:

bash <(curl -s


Once you have axiom installed and setup, you would initialize a new instance. Running axiom-init will start a fresh instance. You’ll notice that it will auto-select a name and a number, this is taken from an array of historical scientists and some infosec people (easter egg :)). I have intentionally made these names short and with no special characters so that it is easy to type quickly.


When you initialize a new instance, Axiom will write the instance name to a ‘selection profile’, this is a JSON file inside ~/.axiom/profile.json, this makes it easier to perform other functions such as restoring or deployment.

Now your instance is live, you can do several things. The first thing you might want to do is connect to it. You can run axiom-ssh colden14 (and replace colden14 with whatever your instance was named), and it will connect you to that instance. If you try and connect too soon after init, you might get a connection refused error, but don’t worry, just wait, sometimes it takes an extra 30 seconds or so to fully boot.


When you first SSH in, you’ll notice that you’re dropped into a ZSH prompt.


This first login will also show you your instances public IP, this can be helpful if you need to link to it.

Exploring the file system.


Once you’re connected to your new instance, you might want to take a little look around. Let’s explain what they are now:

  • hashes

    • ~/hashes/ contains a set of tools that help you capture hashes, typically for catching NTLM hashes over the internet, responder, smbserver, etc
  • c2

    • ~/c2/ is empty by default, this is used for the installation of c2 with deployment profiles (to this shortly!)
  • go/bin

    • This directory has all your go binaries, this is in your $PATH by default and has a lot of goodies!
  • lists

    • This contains two of my most frequently used wordlists, Jason Haddix’s all.txt and SecLists
  • recon

    • This directory contains a few recon tools, DNS enumeration, email generation.
  • work

    • This directory is also a placeholder directory, this is where you would put your work files, or the artifacts left over from your recon. This keeps the home directory nice and tidy. I’m also writing a few enumeration scripts that work with this “work” directory.

    Backing up

    If you’ve run a few commands and made some files, it is recommended that you place them in ~/work/. The backup feature backs up your configuration and preferences. For example, if you have made some changes to your ZSH setup, such as a theme, it will be backed up. All you have to do to back up this instance using the axiom-backup colden-14.



This script basically does a select rsync on the home directory, this includes some dotfiles. You can change which files get included in the backup/restore progress by editing ~/.axiom/boxes/backup-files.txt.

As we have backed up colden14, these files will be present in ~/.axiom/boxes/colden14. If we want to see the files, interact and/or edit them, we can use the command axiom-boxes cd colden14 to enter into that directory.

Now, we can safely kill the instance. To do so, we use the command axiom-rm colden14.


This may take a few moments, but when it is complete, our colden14 instance will be completely deleted. We are no longer being billed for this, and it has no presence on the internet any longer. Any data we had on that instance that we did not backup has now been lost forever. Nice.

Now, we can initialize and restore a new instance. I added in a cheeky bit of shorthand to help the process so you can use just one command. axiom-init --restore=colden14 will do the heavy lifting for us.


Once this new instance kilby19 is initialized, it will wait 65 seconds for all the services to come up, and then it will trigger the restoration of our backup.


Now that our kilby19 has been restored with the colden14 backup, we can connect to it and it will maintain our changes! How awesome is that!

If we wanted to, we could turn this into a one liner. Try this:

alias moto="axiom-init moto --restore=moto && axiom-ssh moto --tmux && axiom-backup moto && axiom-rm moto -f"

And run moto.

This will initialize an instance, called moto, restore from the last moto backup, ssh into moto, and then connect to a tmux session. Once your tmux session ends, aka you detach from it, it will run an axiom-backup against it and delete it. Next time you want to spin up and connect to moto, you just run moto again.

This is illustrative of the power of Axiom. You can script all sorts of operations around instances and refer to them by name (without having to care about their ports and IP’s).

Advanced Usage

Some of the more advanced commands are axiom-deploy, axiom-vpn, and axiom-boxes.

Quick Deployment

If you recall earlier when you initialize a new machine it “remembers” the name of the last instance you spun up. As such, commands like axiom-deploy allow you to omit the instance name and assume that you’re talking about the last server you spun up. If you don’t want to refer to the instance you just spun up, you can use axiom-select name to change your profile selection.

To deploy a profile, you just need to run axiom-deploy profilename and it will deploy whatever profile you specify, at the time of writing profiles include:

  • OpenVPN Server
  • Covenant C2 Platform

To deploy an OpenVPN Server, we can just run axiom-deploy openvpn.


This will connect to kilby19, and run a set of commands to install and setup OpenVPN. As we’re all using the same base image, we can package these little scripts together so that they’re universal between all axiom users. If you’d like to write an axiom deployment profile, please refer to the ~/.axiom/profiles/openvpn/manifest.json to get an idea of how it works! Just copy the OpenVPN file and edit the manifest, and you’re off the races!


In the previous step, we deployed OpenVPN using a deployment profile with the axiom-deploy openvpn command. But now we need to connect to it? To connect and route all your traffic through this instance, just run axiom-vpn kilby19, of course replacing kilby19 with whatever your instance is called. This will download the client OpenVPN configuration and start openvpn client with it. It will ask for your sudo password to start.


Managing Axiom-boxes

The axiom-boxes command is a utility that lets us manage our boxes. To first see what boxes we have available, run axiom-boxes ls


For me, I have quite a few boxes. These are from previous backups and other machines I have used in the past. One of these is pry0cc-lazy, which is a public axiom box I maintain. pry0cc/lazy contains an enumeration script that empowers you to perform automated enumeration against a domain name.

This script is more of a PoC of what Axiom boxes are able of doing, but we’ll show it off now.

To pull a new box, use my example box, run the command axiom-boxes get pry0cc/lazy.


This will clone my lazy box. Want to try it out? Just start a new instance and restore from it.

axiom-init --restore=pry0cc/lazy


Once restored, initialized, and connected to, we can run the <domain> and it will run our automated enumeration for us.

Once we’ve run that, we can exit and run axiom-backup noyce20 on that instance, delete it, and inspect the results in ~/.axiom/boxes/noyce20/ using axiom-boxes cd noyce20



I’ve gone through a few cool things you can do to get set up and going! Hopefully, I explained everything satisfactorily here! If you have any questions, drop a comment and I’ll be happy to help! If you liked, this please share this article or whatever you feel like!

Stay Snappy!


This is great man…thanks so much for this. It was a bit of a hustle to get it running on macos…since homebrew wants to install the latest version of doctl and the script didn’t quite like that…but once I got the versioning right it worked like a charm…

love the box naming schemes too…kudos!


Thanks man! Yeah it’s still young.

1 Like

Been wondering about this since I primarily use macOS at work. I do a lot with Docker, though, since it’s easier than dealing with Python dependencies sometimes. An Axiom Dockerfile would make my life.

1 Like

I run Axiom on my Mac. It runs great on MacOS. You just need the deps.

1 Like

It is very well explained bravo :slight_smile:


Thanks buddy! I appreciate it!

1 Like

Thanks a lot for this! Event tho I am not directly using it as it is (forked it to work with other vps provider and changed some of the configs), I learned a ton of cool bash stuff while modifying it.


Can you please publish your changes?

Hello! I was able to create a digital ocean droplet of ubuntu desktop and run the automation script. Everything installed correctly. I then opened back of the droplet and used the console from digital ocean. I ran the command axiom-init and it created an instance for me. When I ran the axiom-ssh cerf21 for example, it gave the error could not resolve hostname: Name or service not known. I am a bit of a noob so not sure if I need to attach the ssh keys that were created when I initially ran the script. But I also tried to ssh in with putty but it just brings me into the original ubuntu server droplet and the only directories I can find are snap with associated directories but all are empty. I did notice that I have a ton of different programs when I traverse through the snap directory on my ubuntu desktop through the console. Please let me know what I’m doing wrong as this is an awesome tool!!! Thank you.

1 Like

I’m a complete idiot. Sorry figured out what I was doing wow that’s what happens when you get older! Thanks again for this awesome framework and tool.

1 Like

I think you need to run axiom-build again.

Can you tell me what the output of cat /tmp/ is?

Can you tell me what happens when you run axiom-init test, and then axiom-init test twice in a row after the first one fails?

@pry0cc might have to remove the links in the post and re-add them , (broken images)

Hey man can you please move this to, i’m trying to sort this out tonight.

Can you comment on that issue with your operating system, doctl version etc, just for clarity in one thread.

To also cover Pop!_OS (which seems to work nice), please update the installation script as follows:

< 	elif [ $OS == "Ubuntu" ] || [ $OS == "Debian" ] || [ $OS == "Linuxmint" ] || [ $OS == "Parrot" ]; then
> 	elif [ $OS == "Ubuntu" ] || [ $OS == "Debian" ] || [ $OS == "Linuxmint" ] || [ $OS == "Parrot" ] || [ $OS == "Pop" ]; then

Thanks for a really cool utility!

1 Like