Staging Subdomain Hunt logo

Staging Subdomain Hunt

CommunityPopular
uphiago
staging-subdomain-hunt

Hunt staging via crt.sh when production is WAF-hardened.

Overview

Publisheruphiago
Repositoryrecon-skills
Skill namestaging-subdomain-hunt
Stars
1.3K
Forks
213
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by uphiago on GitHub. Read the source before you install it.

Installation

Install the Staging Subdomain Hunt AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/uphiago/recon-skills.git /tmp/recon-skills
mkdir -p .claude/skills
cp -r /tmp/recon-skills/recon/staging-subdomain-hunt .claude/skills/staging-subdomain-hunt
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Staging Subdomain Hunt in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Staging Subdomain Hunt on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Staging Subdomain Hunt is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Staging & Subdomain Hunt Skill

Discover staging, development, and internal subdomains through certificate transparency (crt.sh), DNS brute force, and web probing. Exploit the staging security gap — staging environments consistently have weaker security than production (no WAF, debug enabled, install pages accessible). Proven on 7 US targets where staging subdomains exposed phpinfo, WordPress install pages, and internal APIs not visible on production.

When to Use

  • Running deep-invade Phase 5 on a high-value target.
  • Production target is well-secured (WAF, no leaks) — pivot to staging.
  • Target has a large attack surface (e-commerce, SaaS, franchise model).
  • You need additional entry points when the main site is hardened.
  • After subdomain-enumeration produces a list of subdomains.

Prerequisites

  • terminal with curl, httpx, jq.
  • Target domain (e.g., example.com).
  • For DNS brute force: wordlist at ./tools/subdomains.txt.

How to Run

bash
DOMAIN="example.com"

# crt.sh discovery
curl --max-time 30 --connect-timeout 10 -sk "https://crt.sh/?q=%25.$DOMAIN&output=json" | jq -r '.[].name_value' | sed 's/\*\.//g' | sort -u > subs.txt

# Probe for live hosts
httpx -silent -l subs.txt -threads 50 -status-code -tech-detect -o alive_subs.txt

# Check for staging indicators
grep -iE 'staging|stage|dev|test|uat|beta' alive_subs.txt

Quick Reference

IndicatorWhat It MeansAction
/wp-admin/install.php returns 200Fresh WordPress — no site configuredInstall takeover
/wp-admin/upgrade.php returns 200WP needs DB upgradeDB info disclosure
info.php / phpinfo.php on stagingDebug enabledPHPInfo analysis (see phpinfo-to-rce)
Staging has no Cloudflare/WAFDirect origin accessRun full deep-invade on origin IP
CORS on staging but not productionStaging has weaker CORS policyCORS attack from staging context
.env on stagingDev credentials exposedCredential theft, pivot to production

Procedure

Step 1 — Certificate Transparency Enumeration

bash
DOMAIN="$1"
OUTDIR="$OUTDIR/staging/$DOMAIN"
mkdir -p "$OUTDIR"

echo "[*] crt.sh enumeration for *.$DOMAIN"

# Primary crt.sh query
curl -sk --max-time 30 --connect-timeout 10 "https://crt.sh/?q=%25.$DOMAIN&output=json" 2>/dev/null | \
  jq -r '.[].name_value' 2>/dev/null | sed 's/\*\.//g' | sed 's/^www\.//' | sort -u > "$OUTDIR/crtsh_subs.txt"

# Also try without wildcard prefix
curl -sk --max-time 30 --connect-timeout 10 "https://crt.sh/?q=$DOMAIN&output=json" 2>/dev/null | \
  jq -r '.[].name_value' 2>/dev/null | sed 's/\*\.//g' | sed 's/^www\.//' | sort -u >> "$OUTDIR/crtsh_subs.txt"

sort -u "$OUTDIR/crtsh_subs.txt" -o "$OUTDIR/crtsh_subs.txt"

sub_count=$(wc -l < "$OUTDIR/crtsh_subs.txt")
echo "[+] crt.sh: $sub_count unique subdomains"

# Categorize by pattern
echo ""
echo "[*] Staging/dev subdomains:"
grep -iE 'staging|stage|dev\.|development|test|uat|beta|sandbox|demo|preview|qa' "$OUTDIR/crtsh_subs.txt"

echo ""
echo "[*] Admin/internal subdomains:"
grep -iE 'admin|portal|internal|dashboard|manage|cp\.|control|panel|cpanel|webmail|mail\.' "$OUTDIR/crtsh_subs.txt"

echo ""
echo "[*] API subdomains:"
grep -iE 'api|rest|graphql|ws\.|websocket' "$OUTDIR/crtsh_subs.txt"

echo ""
echo "[*] Infrastructure subdomains:"
grep -iE 'cdn|static|assets|media|img|images|files|download|origin|proxy' "$OUTDIR/crtsh_subs.txt"

echo ""
echo "[*] Franchise/location subdomains:"
grep -iE 'franchise|location|store|shop|branch|office' "$OUTDIR/crtsh_subs.txt"

Step 2 — Live Host Discovery

bash
DOMAIN="$1"
OUTDIR="$OUTDIR/staging/$DOMAIN"

echo "[*] Probing $(wc -l < "$OUTDIR/crtsh_subs.txt") subdomains..."

httpx -silent -l "$OUTDIR/crtsh_subs.txt" -threads 50 -status-code -tech-detect -title \
  -o "$OUTDIR/alive_subs.txt"

alive=$(wc -l < "$OUTDIR/alive_subs.txt")
echo "[+] $alive live hosts"

# Prioritize staging/dev
echo ""
echo "[*] Staging/dev LIVE:"
grep -iE 'staging|stage|dev\.|development|test|uat' "$OUTDIR/alive_subs.txt" | head -20

Step 3 — WordPress Install Page Check (Staging Takeover)

Staging sites frequently have WordPress installed but not configured:

bash
DOMAIN="$1"
OUTDIR="$OUTDIR/staging/$DOMAIN"

echo "[*] Checking for WordPress install pages on staging..."

for sub in $(grep -iE 'staging|stage|dev' "$OUTDIR/alive_subs.txt" | awk '{print $1}' | head -10); do
  echo "--- $sub ---"

  # Check install.php (fresh WP, no config)
  install_code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 10 --connect-timeout 10 "$sub/wp-admin/install.php")
  if [[ "$install_code" == "200" ]]; then
    echo "  [TAKEOVER] /wp-admin/install.php — fresh WP install, can configure site!"
    # Extract form fields
    curl -sk --max-time 10 --connect-timeout 10 "$sub/wp-admin/install.php" | grep -Eo 'name="[^"]+"' | sort -u
  fi

  # Check upgrade.php (needs DB upgrade)
  upgrade_code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 10 --connect-timeout 10 "$sub/wp-admin/upgrade.php")
  if [[ "$upgrade_code" == "200" ]]; then
    echo "  [INFO] /wp-admin/upgrade.php — DB upgrade page accessible"
  fi

  # Check setup-config.php (no wp-config)
  config_code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 10 --connect-timeout 10 "$sub/wp-admin/setup-config.php")
  if [[ "$config_code" == "200" || "$config_code" == "409" ]]; then
    echo "  [INFO] /wp-admin/setup-config.php — wp-config missing or accessible"
  fi

  # Check for exposed info.php
  info_code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 --connect-timeout 5 "$sub/info.php")
  [[ "$info_code" == "200" ]] && echo "  [CRITICAL] /info.php exposed on staging!"

  # Check for .env on staging
  env_code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 --connect-timeout 5 "$sub/.env")
  [[ "$env_code" == "200" ]] && echo "  [CRITICAL] /.env exposed on staging!"
done

Step 4 — Production vs Staging Security Gap Analysis

bash
DOMAIN="$1"
PROD="https://$DOMAIN"
STAGING=$(grep -iE 'staging|stage' "$OUTDIR/staging/$DOMAIN/alive_subs.txt" | head -1 | awk '{print $1}')

if [[ -n "$STAGING" ]]; then
  echo "[*] Comparing $PROD vs $STAGING"

  # Compare HTTP headers
  echo "=== Production Headers ==="
  curl --max-time 30 --connect-timeout 10 -skI "$PROD" 2>/dev/null | head -20

  echo ""
  echo "=== Staging Headers ==="
  curl --max-time 30 --connect-timeout 10 -skI "$STAGING" 2>/dev/null | head -20

  # Check for common staging weaknesses
  echo ""
  echo "[*] Staging-specific checks:"

  # Directory listing
  listing=$(curl -sk --max-time 5 --connect-timeout 5 "$STAGING/wp-content/uploads/" | grep -i "Index of")
  [[ -n "$listing" ]] && echo "  [WEAK] Directory listing enabled on uploads"

  # Debug mode
  debug=$(curl -sk --max-time 5 --connect-timeout 5 "$STAGING/" | grep -i "wp_debug\|debug mode\|error_reporting")
  [[ -n "$debug" ]] && echo "  [WEAK] Debug output visible"

  # CORS on staging
  cors=$(curl -skI --max-time 5 --connect-timeout 5 "$STAGING/wp-json/wp/v2/users" -H "Origin: https://evil.com" | grep -i "access-control-allow-credentials: true")
  [[ -n "$cors" ]] && echo "  [WEAK] CORS credential reflection on staging"

  # XMLRPC on staging
  xmlrpc=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 --connect-timeout 5 -X POST "$STAGING/xmlrpc.php" \
    -d '<?xml version="1.0"?><methodCall><methodName>demo.sayHello</methodName></methodCall>')
  [[ "$xmlrpc" == "200" ]] && echo "  [WEAK] XMLRPC open on staging"
fi

Step 5 — Franchise/Multi-Location Subdomain Enumeration

For franchise or multi-location businesses:

bash
DOMAIN="$1"
OUTDIR="$OUTDIR/staging/$DOMAIN"

echo "[*] Franchise/location subdomains:"

# Extract location-based subdomains
grep -iE 'franchise|location|store|shop|branch|office|city|state' "$OUTDIR/alive_subs.txt" | while read -r line; do
  sub=$(echo "$line" | awk '{print $1}')
  echo "--- $sub ---"

  # Check if it's a WordPress site
  wp=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 --connect-timeout 5 "$sub/wp-login.php")
  [[ "$wp" =~ ^(200|301|302)$ ]] && echo "  WordPress detected"

  # Check for WPSL (WP Store Locator) data
  wpsl=$(curl --max-time 30 --connect-timeout 10 -sk "$sub/wp-json/wpsl/v1/" 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d) if isinstance(d,list) else 'no')" 2>/dev/null)
  [[ "$wpsl" != "no" && "$wpsl" != "0" ]] && echo "  WPSL: $wpsl locations"

  # Check for store-specific data
  users=$(curl --max-time 30 --connect-timeout 10 -sk "$sub/wp-json/wp/v2/users" 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d) if isinstance(d,list) else 0)" 2>/dev/null)
  [[ "$users" -gt 0 ]] && echo "  Users: $users"
done

Pitfalls

  • crt.sh rate limiting. crt.sh may return empty JSON if rate-limited. Use 2-3s delays between queries or query the PostgreSQL dump directly at crt.sh/?d=.
  • Wildcard certs hide subdomains. If *.example.com is the only cert, individual subdomains won't appear in crt.sh. Use subfinder DNS brute force as fallback.
  • Staging may require VPN. Some staging environments are IP-restricted. Test only from source addresses approved by the engagement.
  • WordPress install.php on production. Some poorly maintained production sites also have this accessible. It's not always staging-specific. Check for "Welcome to WordPress" title text to confirm it's a fresh install.
  • CORS can differ between production and staging. Test the same bounded endpoint matrix in both environments before claiming a security-control gap.

Verification

  • Every staging subdomain MUST be probed with httpx to confirm it's live.
  • WordPress install.php MUST return HTTP 200 with "WordPress" + "installation" in body (not a redirect or SPA).
  • Staging weakness MUST be compared against production to confirm a security gap (e.g., production has WAF but staging doesn't).
  • Internal subdomain leaks from crt.sh must be verified to be the target's infrastructure (not unrelated domains in the same cert).
  • Discovered credentials or configuration values must be handled as sensitive evidence. Test credentials only with explicit authorization and approved identities; never assume staging credentials may be tried in production.

Frequently asked questions

What does the Staging Subdomain Hunt AI skill do?

Hunt staging via crt.sh when production is WAF-hardened.

Why use Staging Subdomain Hunt on TypingMind?

Because you install it once and use it with any model. Staging Subdomain Hunt is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Staging Subdomain Hunt in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/uphiago/recon-skills/tree/main/recon/staging-subdomain-hunt. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Staging Subdomain Hunt?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Staging Subdomain Hunt?

As many as you like. As long as a model supports skills, you can use Staging Subdomain Hunt with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Staging Subdomain Hunt AI skill free?

Yes. It is published on GitHub by uphiago under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇