Ju1y's Blog - Building a Bug Bounty Monitoring Tool from Scratch

Building a Bug Bounty Monitoring Tool from Scratch

0x00 Introduction


Recently, while working on vulnerabilities on platforms like HackerOne and Bugcrowd, I realized how critical reconnaissance is. Therefore, I created a simple monitoring tool for Bug Bounty projects. The monitoring script is built with data from ProjectDiscovery Chaos and integrated with a Discord Bot to provide real-time notifications. The tool currently supports the following basic features:

  • Create a monitoring list with options to add or remove targets
  • Monitor targets daily for subdomain count and changes
  • Query specific targets to get subdomain details, Bug Bounty platforms, project URLs, and download all subdomains

0x01 Getting Started


Source:  ProjectDiscovery

On the ProjectDiscovery Chaos project page, I discovered a ready-made Bug Bounty project monitoring list, which includes projects from major Bug Bounty platforms. It regularly updates subdomains and highlights changes. The project list can be viewed on GitHub: ProjectDiscovery Public BugBounty Programs.

01.png

We can find the data request interface at https://chaos-data.projectdiscovery.io/index.json , and the returned data format is:

JSON
{
  "name": "Shopify",
  "program_url": "https://hackerone.com/shopify",
  "URL": "https://chaos-data.projectdiscovery.io/shopify.zip",
  "count": 4970,
  "change": 0,
  "is_new": False,
  "platform": "hackerone",
  "bounty": True,
  "last_updated": "2024-12-06T00:12:37.625395341Z"
}

Subdomains can also be retrieved directly using the Chaos Tool, or by using the Chaos API: dns.projectdiscovery.io

Shell
chaos -d uber.com -silent

restaurants.uber.com
testcdn.uber.com
approvalservice.uber.com
...
Shell
GET /dns/{domain}
Host: dns.projectdiscovery.io
Authorization: API_KEY
Connection: close

{"domain":"domain","count":1337}

Monitor Tool:  Discord Bot

I use Discord Bot as an interactive tool for monitoring. The Discord Bot can automatically perform scheduled tasks and send notifications based on project changes. You can set up Slash Commands for customized interactions.

Discord Bot Configuration

Create a new Application in the Discord Developer Portal

02.png

On the Bot page, create a Bot and give it a cool name. You can use ChatGPT to generate a Bot icon.

03.png

Get and note down the Bot's TOKEN. Ensure that in Privileged Gateway Intents, you check the Message Content Intent.

Use this URL format to invite the Bot to your server: https://discord.com/api/oauth2/authorize?client_id=CLIENT_ID&permissions=PERMISSIONS_INTEGER&scope=bot+applications.commands

  • CLIENT_ID can be obtained from the OAuth2 page.
  • PERMISSIONS_INTEGER can be calculated on the Bot page. The minimum required permissions for this tool can be set to 3072.

0x02 Script Writing


Initial Setup

Install dependencies and complete some initial setup. Replace your own DISCORD_TOKEN, CHANNEL_ID, and SERVER_ID. You can enable Developer Mode in the Discord client settings, then right-click on the respective Server and Channel to copy the corresponding IDs.

Python
import discord
import requests
from datetime import datetime
from discord.ext import commands, tasks

# Constants
DISCORD_TOKEN = 'YOUR_DISCORD_TOKEN'
CHANNEL_ID = 'YOUR_CHANNEL_ID'
ALLOWED_SERVERS = ['YOUR_SERVER_ID']
CHAOS_DATA_URL = "https://chaos-data.projectdiscovery.io/index.json"

# Veribles
monitor_list = []
chaos_data = {}
platform_name = {
    '': 'Self-hosted',
    'hackerone': 'HackerOne',
    'intigriti': 'Intigriti',
    'bugcrowd': 'Bugcrowd',
    'bugbountych': 'BugBounty Switzerland',
    'hackenproof': 'HackenProof',
    'yeswehack': 'YesWeHack'
}

Discord Intents are permissions in the Discord API used to control which events or data can be accessed. The following code configures the Bot to receive server-related and message-related events, as well as to read message content.

Python
# Discord Intents
intents = discord.Intents.default()
intents.guilds = True
intents.messages = True
intents.message_content = True

Main Function

The fetch_chaos(url) function primarily requests chaos data, returns it in JSON format, and stores it in the chaos_data variable.

Python
def fetch_chaos(url):
    try:
        r = requests.get(url=url)
        r.raise_for_status()
        return r.json()
    except requests.exceptions.HTTPError as e:
        print(f"HTTP Error: {e}")
    except requests.exceptions.RequestException as e:
        print(f"Error fetching data: {e}")
    except ValueError as e:
        print(f"Error decoding JSON: {e}")

The query_target(target) function searches the chaos_data and returns the object with the corresponding name.

Python
def query_target(target):
    global chaos_data
    target_found = False
    for item in chaos_data:

        if 'name' in item and item['name'].lower() == target.lower():
            target_found = True
            return item

    if not target_found:
        return None

Create a Bot and set up the on_ready function to check whether the Bot is running in an allowed Server. If not, it will automatically exit. After initializing the Bot, run update_data.start() to update the data for the first time and start the scheduled task.

Python
# Create Bot
bot = commands.Bot(command_prefix='/', intents=intents)

@bot.event
async def on_ready():
    print(f"Logged in as {bot.user.name}")
    print(f"Bot is ready, ID: {bot.user.id}")
    for guild in bot.guilds:
        if str(guild.id) not in ALLOWED_SERVERS:
            await guild.leave()
            print(
                f"Left {guild.name} as it is not in the allowed servers list.")
    await bot.tree.sync()
    print(f"Slash commands synced successfully.")

    update_data.start()
    print("Scheduled update task started.")

Scheduled Task

The task loop for scheduled updates is set as follows: data is updated every 24 hours. If there are changes in the target—indicated by "change" not being 0—change information is appended.

Python
# Scheduled update every 24 hours
@tasks.loop(hours=24)
async def update_data():
    global chaos_data

    result = fetch_chaos(CHAOS_DATA_URL)
    if result:
        chaos_data = result
        company_count = len(result)
        message = f"Chaos Data Updated ! Total companies: {company_count}\n"

        target_changes = []
        filtered_results = [
            item for item in chaos_data if item['name'] in monitor_list]

        for item in filtered_results:
            if item['change'] != 0:
                last_updated = datetime.strptime(
                    item['last_updated'][:26], "%Y-%m-%dT%H:%M:%S.%f")
                formatted_time = last_updated.strftime("%Y-%m-%d %H:%M")

                if item['change'] > 0:
                    change_symbol = f"▲ {item['change']}"
                else:
                    change_symbol = f"▼ {item['change']}"

                target_changes.append(
                    f"- **Target: {item['name']}**\n"
                    f"  - Subdomains: {item['count']}\n"
                    f"  - Change: {change_symbol}\n"
                    f"  - Last updated: {formatted_time}"
                )

        if target_changes:
            message += "\n### Target Changed❗️\n"
            message += "\n\n".join(target_changes)

        message_channel = bot.get_channel(int(CHANNEL_ID))
        if message_channel:
            await message_channel.send(message)
            print("Scheduled update")
        else:
            print("Channel not found.")
    else:
        print("Failed to fetch data or received no data.")
04.png

Slash Command

list Command: Lists the current monitoring targets. It retrieves the content from monitor_list.

Python
# /list: show monitoring targets
@bot.tree.command(name="list", description="Show monitoring targets.")
async def list(interaction: discord.Interaction):
    if monitor_list:
        message = "**Monitoring List:**\n"
        for target in monitor_list:
            message += f"- {target}\n"
        await interaction.response.send_message(message)
    else:
        await interaction.response.send_message("\r\nMonitoring list is empty, use `/add` command to add targets.")

add Command: Adds a monitoring target to the monitoring list. It first uses query_target(target) to check if the input exists in chaos_data. If a corresponding target is found, the target name is extracted and monitor_list is updated.

Python
# /add :  add a target
@bot.tree.command(name="add", description="Add a target to monitoring list.")
async def add(interaction: discord.Interaction, target: str):

    res = query_target(target)
    if res is not None:
        monitor_list.append(res['name'])
        await interaction.response.send_message(f"Added **_{target}_** to monitoring list.")
    else:
        await interaction.response.send_message(f"**_{target}_** not found in chaos data.")
05.png

del Command: Removes a monitoring target from the monitoring list.

Python
# /del : Remove a target
@bot.tree.command(name="del", description="Remove a target from monitoring list.")
async def del_item(interaction: discord.Interaction, target: str):
    if target in monitor_list:
        monitor_list.remove(target)
        await interaction.response.send_message(f"Removed **_{target}_** from monitoring list.")
    else:
        await interaction.response.send_message(f"**_{target}_** not found.")

query Command: Queries a specific target and provides detailed information, including the number of subdomains, platform, project URL, whether it offers a bounty, and the latest update time. Additionally, it provides a download link for all subdomains.

Python
# /query :  query a target
@bot.tree.command(name="query", description="Query information about a specific target.")
async def add(interaction: discord.Interaction, target: str):
    res = query_target(target)

    if res is not None:
        last_updated = datetime.strptime(
            res['last_updated'][:26], "%Y-%m-%dT%H:%M:%S.%f")
        formatted_time = last_updated.strftime("%Y-%m-%d %H:%M")

        message = f"""
### Target: {res['name']}
- **Subdomains**: {res['count']}
- **Platform**: [{platform_name[res['platform']]}]({res['program_url']})
- **Offers Reward**: {res['bounty']}
- **Last Updated**: {formatted_time}
"""
        message += f"[Download all the subdomains of {res['name']}]({res['URL']})"
        await interaction.response.send_message(message)
    else:
        await interaction.response.send_message(f"**_{target}_** not found in chaos data.")
06.png

help Command: Customize the help content as needed to display the functionality of each command.

07.png

0x04 Summary


This script currently implements only the most basic monitoring functions. You can add more features according to your needs, and feel free to share your improvements.

Open Source The complete code for this project has been uploaded to GitHub☀️: https://github.com/JackJuly/bugbounty-monitor-bot

TODO

  • Add project query functionality
  • List specific newly added subdomains
  • Independent subdomain query feature
  • JavaScript file monitoring