09 continuous monitoring
Skill 0xGhostCAT/claude-ai-cyber-security-skills/skills/09-continuous-monitoring
30 Claude Code Skills + 60+ integrated tools for HackerOne/Bugcrowd bug bounty hunters
npx -y skills add 0xGhostCAT/claude-ai-cyber-security-skills --skill 09-continuous-monitoringAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
What its author says it does
Copied from the file, not written here
Set up nightly monitoring of subdomain changes, JS file diffs, new endpoints, and CVE matches against in-scope assets, with Discord/Slack/Telegram notifications. Use when the user has an active engagement and wants to be alerted to new attack surface without manual re-recon.
SKILL.md
13.2 KB, as published. Nobody here has run it
Continuous Monitoring
Sleep. Sub registered overnight. Discord pings. You hunt it before everyone else.
When to invoke
Trigger phrases:
- "monitor target"
- "alert on new subdomain"
- "watch this program"
- "set up continuous recon"
What we monitor
| Signal | Why it matters | Frequency |
|---|---|---|
| New subdomains | New attack surface, often less tested | Nightly |
| Subdomain takeover candidates | Free critical if found early | Nightly |
| JS file hash changes | New endpoints, removed mitigations | Daily |
| New endpoints in JS | Hidden routes shipped in deploys | Daily |
| HTTP status changes (200 → 401 etc.) | New restrictions / new exposures | Daily |
| New tech in fingerprint | New stack = new vuln classes | Weekly |
| CVE matches against fingerprint | 0-days → 1-day exploitation window | Daily |
| GitHub commits to public repos | Secrets, hints, new features | Hourly |
| Cert transparency new certs | New subdomains often pre-deployed in CT | Hourly |
| Wayback URL diff | New paths historical scanners pick up | Daily |
Two deployment models
A. Cron-based (your VPS, $5/month)
┌────────────────────────────────────────┐
│ DigitalOcean / Linode / Hetzner VPS │
│ - cron jobs │
│ - bash scripts │
│ - state in ~/ccs-state/ │
│ - notify → Discord webhook │
└────────────────────────────────────────┘
B. GitHub Actions (free, no VPS)
┌────────────────────────────────────────┐
│ GitHub Actions │
│ - scheduled workflows │
│ - state in repo artifacts │
│ - notify → Discord webhook │
└────────────────────────────────────────┘
We'll cover both.
Setup A — Cron on a VPS
Step 1: Notify configuration
Use notify — supports Discord/Slack/Telegram/Teams.
mkdir -p ~/.config/notify
cat > ~/.config/notify/provider-config.yaml <<EOF
discord:
- id: "ccs-alerts"
discord_webhook_url: "https://discord.com/api/webhooks/YOUR_WEBHOOK_HERE"
slack:
- id: "ccs-alerts"
slack_webhook_url: "https://hooks.slack.com/services/YOUR/WEBHOOK"
telegram:
- id: "ccs-alerts"
telegram_api_key: "YOUR_BOT_TOKEN"
telegram_chat_id: "YOUR_CHAT_ID"
EOF
# Test
echo "Test alert from claude-cybersecurity-skills" | notify -id ccs-alerts -bulk
Step 2: Subdomain monitoring script
# scripts/monitor-subs.sh
#!/usr/bin/env bash
set -euo pipefail
TARGETS_FILE="$HOME/ccs-state/targets.txt" # one root domain per line
STATE_DIR="$HOME/ccs-state"
mkdir -p "$STATE_DIR/subs"
while read TARGET; do
OLD="$STATE_DIR/subs/$TARGET.txt"
NEW="$STATE_DIR/subs/$TARGET.new.txt"
# Run passive recon
(subfinder -d "$TARGET" -all -silent;
assetfinder --subs-only "$TARGET";
curl -s "https://crt.sh/?q=%25.${TARGET}&output=json" | jq -r '.[].name_value' | sed 's/\*\.//g') | \
sort -u > "$NEW"
if [[ -f "$OLD" ]]; then
# Diff
DIFF=$(comm -13 <(sort "$OLD") <(sort "$NEW"))
if [[ -n "$DIFF" ]]; then
COUNT=$(echo "$DIFF" | wc -l)
echo -e "🆕 New subdomains for **$TARGET** ($COUNT)\n\`\`\`\n$DIFF\n\`\`\`" | \
notify -id ccs-alerts -bulk -silent
# Auto-probe new ones for quick wins
echo "$DIFF" | httpx -silent -tech-detect -title -status-code | \
tee -a "$STATE_DIR/subs/$TARGET.probed.log" | \
notify -id ccs-alerts -bulk -silent
# Check for takeover candidates
echo "$DIFF" | nuclei -t http/takeovers/ -silent -nc -severity high,critical | \
notify -id ccs-alerts -bulk -silent
fi
else
echo "[*] Initial baseline for $TARGET ($(wc -l < "$NEW") subs)"
fi
mv "$NEW" "$OLD"
done < "$TARGETS_FILE"
Step 3: JS file monitoring
# scripts/monitor-js.sh
#!/usr/bin/env bash
set -euo pipefail
URLS_FILE="$HOME/ccs-state/js-urls.txt" # JS file URLs to monitor
STATE_DIR="$HOME/ccs-state/js"
mkdir -p "$STATE_DIR"
while read JS_URL; do
HASH_FILE="$STATE_DIR/$(echo "$JS_URL" | md5sum | cut -d' ' -f1).hash"
CURRENT_HASH=$(curl -s "$JS_URL" | md5sum | cut -d' ' -f1)
if [[ -f "$HASH_FILE" ]]; then
OLD_HASH=$(cat "$HASH_FILE")
if [[ "$OLD_HASH" != "$CURRENT_HASH" ]]; then
# Get the diff in endpoints
OLD_ENDPOINTS_FILE="$STATE_DIR/$(echo "$JS_URL" | md5sum | cut -d' ' -f1).endpoints"
curl -s "$JS_URL" > /tmp/current.js
python3 ~/tools/LinkFinder/linkfinder.py -i /tmp/current.js -o cli 2>/dev/null | sort -u > /tmp/current.endpoints
if [[ -f "$OLD_ENDPOINTS_FILE" ]]; then
NEW_ENDPOINTS=$(comm -13 <(sort "$OLD_ENDPOINTS_FILE") /tmp/current.endpoints)
if [[ -n "$NEW_ENDPOINTS" ]]; then
echo -e "🔧 JS changed: $JS_URL\n**New endpoints:**\n\`\`\`\n$NEW_ENDPOINTS\n\`\`\`" | \
notify -id ccs-alerts -bulk -silent
fi
fi
mv /tmp/current.endpoints "$OLD_ENDPOINTS_FILE"
fi
fi
echo "$CURRENT_HASH" > "$HASH_FILE"
done < "$URLS_FILE"
Step 4: Cert transparency real-time
Subscribe to certstream for near-instant new-cert alerts:
# scripts/certstream-monitor.py
import certstream
import re
import requests
WEBHOOK = "https://discord.com/api/webhooks/YOUR_WEBHOOK_HERE"
TARGETS = ["target.com", "anothertarget.io"]
def callback(message, context):
if message['message_type'] != 'certificate_update':
return
domains = message['data']['leaf_cert']['all_domains']
for d in domains:
for t in TARGETS:
if d.endswith("." + t) or d == t:
msg = f"🆕 New cert: `{d}` (target: {t})"
requests.post(WEBHOOK, json={"content": msg})
return
certstream.listen_for_events(callback, url='wss://certstream.calidog.io/')
Run as systemd service:
# /etc/systemd/system/certstream-monitor.service
[Unit]
Description=Cert Transparency Monitor
After=network.target
[Service]
ExecStart=/usr/bin/python3 /home/user/ccs-state/scripts/certstream-monitor.py
Restart=always
User=user
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now certstream-monitor
Step 5: Crontab
# Subdomain enumeration
0 3 * * * /home/user/ccs-state/scripts/monitor-subs.sh >> /var/log/ccs-monitor.log 2>&1
# JS monitoring (every 6 hours)
0 */6 * * * /home/user/ccs-state/scripts/monitor-js.sh >> /var/log/ccs-monitor.log 2>&1
# Nuclei sweep of in-scope (daily)
0 5 * * * /home/user/ccs-state/scripts/nuclei-sweep.sh >> /var/log/ccs-monitor.log 2>&1
# Weekly tech re-fingerprint
0 6 * * 1 /home/user/ccs-state/scripts/refingerprint.sh >> /var/log/ccs-monitor.log 2>&1
Setup B — GitHub Actions
.github/workflows/monitor.yml
name: BB Continuous Monitoring
on:
schedule:
- cron: '0 3 * * *' # daily 3am UTC
workflow_dispatch: # manual trigger
jobs:
monitor-subs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Install tools
run: |
go install github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest
go install github.com/projectdiscovery/httpx/cmd/httpx@latest
go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
go install github.com/projectdiscovery/notify/cmd/notify@latest
go install github.com/tomnomnom/anew@latest
- name: Configure notify
run: |
mkdir -p ~/.config/notify
cat > ~/.config/notify/provider-config.yaml <<EOF
discord:
- id: "ccs-alerts"
discord_webhook_url: "${{ secrets.DISCORD_WEBHOOK }}"
EOF
- name: Run monitoring
env:
CHAOS_KEY: ${{ secrets.CHAOS_KEY }}
run: |
chmod +x scripts/monitor-subs.sh
./scripts/monitor-subs.sh
- name: Commit state
run: |
git config user.name "ccs-monitor"
git config user.email "monitor@claude-cybersecurity-skills"
git add state/
git diff --staged --quiet || git commit -m "monitor: state update $(date -u +%Y-%m-%dT%H:%MZ)"
git push
Configure secrets in GitHub repo settings:
DISCORD_WEBHOOKCHAOS_KEY- (any other API keys)
Nuclei daily sweep
Hit all alive in-scope hosts with critical/high templates daily:
# scripts/nuclei-sweep.sh
#!/usr/bin/env bash
set -euo pipefail
ALIVE="$HOME/ccs-state/all-alive.txt"
nuclei -list "$ALIVE" \
-severity critical,high \
-rate-limit 50 \
-bulk-size 25 \
-concurrency 25 \
-silent \
-nc \
-json -o "$HOME/ccs-state/nuclei-daily.json"
# Diff against yesterday
NEW_FINDINGS=$(comm -13 \
<(sort "$HOME/ccs-state/nuclei-yesterday.json" 2>/dev/null) \
<(sort "$HOME/ccs-state/nuclei-daily.json"))
if [[ -n "$NEW_FINDINGS" ]]; then
echo -e "🚨 New nuclei findings:\n\`\`\`json\n$NEW_FINDINGS\n\`\`\`" | \
notify -id ccs-alerts -bulk -silent
fi
cp "$HOME/ccs-state/nuclei-daily.json" "$HOME/ccs-state/nuclei-yesterday.json"
GitHub commit monitoring (for OSS targets)
# scripts/github-watch.sh
# Watches commits on target's public GitHub orgs
GH_ORGS=("target-inc" "target-oss")
for ORG in "${GH_ORGS[@]}"; do
LAST_SHA_FILE="$HOME/ccs-state/gh-$ORG.sha"
# Get latest events
LATEST=$(gh api "orgs/$ORG/events" --jq '.[0].id')
if [[ -f "$LAST_SHA_FILE" ]]; then
OLD=$(cat "$LAST_SHA_FILE")
if [[ "$OLD" != "$LATEST" ]]; then
EVENTS=$(gh api "orgs/$ORG/events" --jq '.[] | select(.type=="PushEvent") | "\(.repo.name): \(.payload.commits[].message)"' | head -5)
echo -e "📦 Activity in **$ORG**:\n$EVENTS" | notify -id ccs-alerts -bulk -silent
fi
fi
echo "$LATEST" > "$LAST_SHA_FILE"
done
State structure
~/ccs-state/
├── targets.txt ← one root domain per line
├── subs/
│ ├── target.com.txt ← yesterday's list
│ └── target.com.probed.log
├── js/
│ ├── <hash>.hash ← last-seen hash
│ └── <hash>.endpoints ← last-seen endpoint list
├── js-urls.txt ← JS files to monitor
├── all-alive.txt
├── nuclei-daily.json
├── nuclei-yesterday.json
├── gh-target-inc.sha
└── logs/
Tuning alerts (avoid notification fatigue)
# rules.yaml — what to alert on
alert_rules:
subdomain_new:
threshold: 1 # alert on any new sub
quiet_hours: "22-08" # don't ping at night
takeover_candidate:
threshold: 1
severity: high # always alert
nuclei_finding:
severity: [critical, high] # skip medium/low to reduce noise
js_change:
only_with_new_endpoints: true # don't alert on cosmetic changes
Cross-references
[[subdomain-enum]]— the engine behind monitoring[[js-analysis]]— for endpoint diffs[[subdomain-takeover]]— auto-check on new subs[[scope-analysis]]— only alert on in-scope assets
Common pitfalls
- Monitoring out-of-scope assets. Alerts you can't act on. Filter first.
- Notification fatigue. Tune
severityandquiet_hoursaggressively. - State loss. If
~/ccs-state/is wiped, you re-baseline = many false "new" alerts. - VPS IP blocked. Some targets ban scanner IPs. Rotate or use Cloud Run / Lambda.
- API rate limits. Don't run subfinder every hour — daily is enough for most.
Pro tips
- Separate webhooks per priority (one for "new subs", one for "critical findings").
- Use Telegram for mobile alerts — instant push.
- Tag alerts with target name in the message for fast triage.
- Auto-create a private issue in your private bug-bounty repo for each finding.
- Track your "first to find" rate — measures how quickly you act on monitoring alerts.
Real-world example
[Day 1, 3am UTC]
🆕 New subdomain for target.com (1):
admin-staging.target.com [200] Tech: Spring Boot 2.5.4, Actuator
[Day 1, 9am — you wake up]
You check: /actuator/env → exposed. CRITICAL.
Report submitted by 10am. Triaged within 2h. $5,000 bounty by EOD.
This is the difference between manual recon and automated monitoring.