Automating Bug Bounty with n8n
- Ben Lampere
- 43 minutes ago
- 9 min read

I was recently introduced to an automation tool called n8n (read nodemation). It’s a no-code automation platform that uses nodes to build workflows and seamlessly integrates with AI. What sets n8n apart from many other automation tools is that it’s open-source, can be self-hosted, and offers nearly limitless integration options.
While this tool is great for all sorts of automation I wanted to focus on using n8n to automate some of the repetitive steps during bug bounties. There were 3 steps of the enumeration process that we needed to automate: Subdomain enumeration, directory enumeration, and screenshot capture.
Another goal of this project was to allow for ease of use and collaboration so instead of running everything in the n8n platform I decided to execute everything from a discord bot and print all the results to a channel.
I have separated this article into 5 different sections. We will create the n8n server, then create the working server, setup the n8n workflows, reviewing the bash scripts for automation, and setting up the Discord Bot.
Creating the n8n server:
To get started we needed to find a place to host our instance of n8n. I already use a Digital Ocean instance to do most of my bug bounty work so I decided to just start a new instance for n8n.
Create a Digital Ocean account. https://cloud.digitalocean.com/registrations/new
Choose Droplet from the menu on the left hand side. Then choose Create Droplet.
Under Choose an image select Marketplace and search “Docker”. Select Docker latest on Ubuntu.
Because we are just running basic commands we don’t need anything fancy. The cheapest option will work just fine.

Note: At this point you can run n8n just like this but because we are connecting this to Discord we need a domain so we can make the connection.
Purchase a domain from any of the major domain providers. Hostgator, Cheapname, Dreamhost, or GoDaddy are all great options.
Point your domain name to the IP address of your Digital Ocean instance. In this example I used Cloudflare and just made an A record that points to the IP of my Digital Ocean n8n instance.
After a few hours everything should propagate and you should be able to access the n8n dashboard from your domain.
Now we are going to actually install n8n onto our Digital Ocean instance. I could put all the commands you need to run but n8n literally has a page specifically for this. Just follow the steps. https://docs.n8n.io/hosting/installation/server-setups/digital-ocean/#log-in-to-your-droplet-and-create-new-user
Once that is completed go to your domain and you should see the following screen. Sign up with your account to access your n8n instance.

Creating the Working Server:
The working server is where all the enumeration is actually going to run from. If you already have a setup on another service that works too. You just have to be able to establish a SSH connection from the internet.
I already assume you have the Digital Ocean account from the previous section. So go in and create another Droplet.
This part is really up to personal preference but I just selected a simple Ubuntu image.
You need something slightly more powerful to run everything, I picked the 2GB, 2 CPU instance for 18 dollars a month. Once again play around with what works well for you.

Enter a root password and remember it, we will need this later.
Once the machine has spun up connect to the server using SSH or Digital Oceans browser terminal.
As a security expert I have to tell you to be responsible and create a new user for your working server and not run everything as root, but I'm also not your mom so do what you want.
Create a working directory that all your enumeration will take place in. You will see from the workflow I put everything in a folder called discord.
Install the following tools, some of these will vary based on your enumeration style but this is a good start.
- Subfinder (https://github.com/projectdiscovery/subfinder)
- PureDNS (https://github.com/d3mondev/puredns)
- massdns (https://github.com/blechschmidt/massdns)
Creating the n8n workflow:
I'm going to go on in this article and explain how the n8n workflow works and all the files involved. If you're not interested in that and just want the files just scroll down to the bottom and the Github link containing everything will be there. If you are new to n8n and automation I encourage you to read on.
By default n8n doesn't have a Discord trigger so we are going to need to download a community node for this.
Go to setting and select community nodes
Press Install and paste “@jordanburke/n8n-nodes-discord”

On the dashboard press Create Workflow.
Press the 3 dots and from the drop down select Import from File
Download the workflow off the Github and upload it to your n8n instance.
https://github.com/Sheepwiz/bugbounty_n8n_workflow/blob/main/bugbounty_workflow.json
Using the workflow:
At the bottom of this article (and right above this), you will have the ability to download the JSON version of the workflow. Before that, I will walk through each section so you gain a clear understanding of what this template actually does.

The workflow begins with a single Discord trigger that we previously downloaded. This trigger listens for a mention and an apex domain. When it is activated, Discord sends a confirmation message indicating it received the input. From there, the apex domain is processed: periods are replaced with underscores, and the result is saved as a variable. For example, https://google.com becomes google_com. Next, the workflow checks whether a corresponding folder already exists. If it does, the user is prompted to decide whether to skip subdomain enumeration and go straight to directory enumeration, or rerun subdomain enumeration again. Using the folder name variable, the workflow then creates a new folder on the server to keep everything organized.
At this stage, the subdomain enumeration process begins. The .sh file containing the enumeration script is copied into the working folder. This ensures that if the main script is updated in the future, the exact version used for this domain is preserved. The next node executes the .sh file, which can take some time to complete. Once finished, the httpx_results.txt file is downloaded. This file contains all the valid subdomains that were identified.
Finally, the user is sent the contents of httpx_results.txt and notified that the subdomain enumeration step has been completed.

Now we move into directory enumeration. Many of the steps are similar to the subdomain section, so this walkthrough will remain at a higher level. First, the user is notified that directory enumeration has started. We then copy the .sh file into the working directory and execute it. This step, once again, is one of the longest processes. Since we are using Gobuster for directory enumeration, each subdomain will generate its own text file containing directory results.
After directory enumeration completes, we display all the files stored in gobuster_results. Because the output is captured as standard out, the results still include escape characters for line breaks. To clean this up, we run a JavaScript script that places the filenames into an array and removes the escape characters. Another JavaScript script is then used to count the total number of files returned.
At this point, we need to present the results to the user. The challenge is that large domains may generate hundreds of files. To handle this, we check whether the file count is greater than 10. If it is, the user is prompted to choose whether they want each file printed individually to Discord, or whether they would prefer the results zipped together. If the user declines the zip option, or if there are fewer than 10 files, the workflow iterates through the results and prints each file directly to Discord.

The final step involves taking all of the 200 status responses identified during the directory enumeration process and running Gowitness to capture screenshots.
We begin by asking the user whether they would like screenshots at all. If they do, we copy our formatter script into the working directory. This script compiles all the Gobuster results, extracts the full URLs of the 200 responses, and consolidates them into a single file. We then execute Gowitness. Similar to the directory enumeration step, we run a JavaScript script to generate an array of the screenshot files and calculate the total count.
If the number of screenshots exceeds 10, the user is prompted to choose whether they want each screenshot printed individually to Discord or packaged into a zip file. If there are fewer than 10, or if the user declines the zip option, the workflow iterates through each screenshot and prints it directly to Discord. Otherwise, the zip file is sent instead.
Once this step is complete, whether by printing or zipping screenshots, we notify the user that the workflow has finished and instruct them to check the output channel for the results.
Creating the Bash Scripts:
#!/bin/bash
# Check if domain was provided
if [ -z "$1" ]; then
echo "Usage: $0 domain.com"
exit 1
fi
DOMAIN="$1"
DOMAIN_NO_DOTS=$(echo "$DOMAIN" | tr '.' '_')
echo "[*] Running subfinder..."
/root/go/bin/subfinder -d "$DOMAIN" -o "${DOMAIN_NO_DOTS}_results_subfinder"
echo "[*] Resolving with PureDNS..."
cat "${DOMAIN_NO_DOTS}_results_subfinder" | puredns resolve -r /root/puredns/massdns/lists/resolvers.txt > "${DOMAIN_NO_DOTS}_puredns_resolve.txt"
awk '/^Found [0-9]+ valid domains:/{flag=1; next} flag' "${DOMAIN_NO_DOTS}_puredns_resolve.txt" > "${DOMAIN_NO_DOTS}_puredns_valid.txt"
echo "[*] Combining and deduplicating all subdomains..."
cat "${DOMAIN_NO_DOTS}_results_subfinder" "${DOMAIN_NO_DOTS}_puredns_valid.txt" | sort -u > "${DOMAIN_NO_DOTS}_combined.txt"
echo "[*] Running Subwiz..."
source ~/discord/bugbounty/bin/activate
subwiz -i "${DOMAIN_NO_DOTS}_combined.txt" -n 1000 -o "${DOMAIN_NO_DOTS}_subwiz.txt"
cat "${DOMAIN_NO_DOTS}_combined.txt" "${DOMAIN_NO_DOTS}_subwiz.txt" | sort -u > "${DOMAIN_NO_DOTS}_combined_subwiz.txt"
echo "[*] Running httpx..."
/root/go/bin/httpx -l "${DOMAIN_NO_DOTS}_combined_subwiz.txt" -title -status-code -fr | grep 200 > "httpx_results.txt"
cut -d' ' -f1 httpx_results.txt > cleaned_urls.txt
rm "${DOMAIN_NO_DOTS}_puredns_valid.txt"
echo "[*] Done! Results saved to:"
echo " - Subfinder: ${DOMAIN_NO_DOTS}_results_subfinder"
echo " - Combined: ${DOMAIN_NO_DOTS}_combined_subwiz.txt"
echo " - Httpx: httpx_results.txt"
The subdomain enumeration script uses a set of basic techniques to gather subdomains. Once the list is collected, all discovered subdomains are merged into a single file to maximize coverage. After creating a deduplicated list, the subdomains are tested with httpx to ensure that only valid, responsive results are kept.
Additional cleanup is then performed to produce a streamlined list containing only the valid subdomains. This refined list is then passed along to the directory enumeration.
# cat direnum.sh
#!/bin/bash
# Path to wordlist and input file
wordlist=CHANGE_ME
input_file="valid_domains.txt"
output_dir="gobuster_results"
# Create output directory if it doesn't exist
mkdir -p "$output_dir"
# Loop over each domain
while IFS= read -r domain; do
echo "Scanning $domain ..."
# Replace dots with underscores to make a valid filename
safe_name=$(echo "$domain" | tr '.' '_')
# Run gobuster and save output
gobuster dir -u "https://$domain/" \
-w "$wordlist" \
-a "Mozilla/5.0 (Windows; Windows NT 6.3; x64) AppleWebKit/601.32 (KHTML, like Gecko) Chrome/49.0.1631.261 Safari/603" \
-k > "$output_dir/${safe_name}.txt"
done < "$input_file"
Above is the basic directory enumeration script that I use. Starting from the top we define your favorite wordlist, accept your valid subdomains list, and finally create a directory called gobuster_results. Gobuster will run one subdomain at a time and scan through your directory worklist. The output is placed into the gobuster_results folder as the subdomain name.
import os
import re
folder_path = '.' # Change if needed
results = []
for filename in os.listdir(folder_path):
if filename.endswith('.txt'):
with open(os.path.join(folder_path, filename), 'r', encoding='utf-8', errors='ignore') as f:
lines = f.readlines()
base_url = None
for line in lines:
if line.startswith('[+] Url:'):
base_url = line.split(":", 1)[1].strip()
break
if not base_url:
continue # Skip if no base URL found
for line in lines:
match = re.search(r'\[2K(.+?)\s+\(Status:\s*200\)', line)
if match:
endpoint = match.group(1).strip()
full_url = base_url.rstrip('/') + '/' + endpoint.lstrip('/')
results.append(full_url)
# Print or return the final list
for r in results:
print(r)
The formatter file takes all the separate files in the gobuster_results and puts it into a single file and list the full URLs for each 200 response.
Setting up the Discord Bot:
Lastly we need to setup the Discord bot. Obviously you need a discord account, once that is done go to the Discord developer portal.
Create a new application and name it what you want
In the left menu of the Discord select Bot and scroll down till you see the Privileged Gateway Intents. Make sure they are all checked.
Also under Bot and select reset Token. Save that value we will need it for later.
We also need to grab the n8n API key. Go back your n8n instance and go to Setting > n8n API Key. Create a new API Key. Set the expiration date to "No Expiration" and save the API as we will also need that later.
After the bot is set up we need to setup some channels. I have just 3 basic channels. The first is general which is what the Discord bot is listening in. We also have an output channel were all the results from the enumeration is posted. The last channel is just called setup. This is were I just store all the documentation.

Running the Discord Bot:
Now that everything is set up make sure the n8n workflow is activated. In the general channel post your apex domain, in this example we do https://ups.com/ and mention the bot name, in this example my bot is called @subdomain emulation.

The results will post in the output channel.

After quite a bit of setup you finally have your enumeration automation working. I found myself being able to check in on my bug bounty enumeration while I'm on the go and have the ability to kick off new scans before I'm sitting at my computer. If you have other ideas for bug bounty enumeration feel free to contribute to the Github or reach out to collaborate. Good luck hunting!
Downloads:
The repo containing the JSON of n8n workflow and the bash scripts contained in this article.