| name | scope-check |
| description | Validate targets against bug bounty program scope, verify domain/IP ownership, check wildcard rules, and identify out-of-scope exclusions. Use when validating new targets, checking discovered assets, or before testing any endpoint. |
| tags | ["security","scope","bug-bounty","target-validation","domain"] |
| triggers | ["check scope","bug bounty scope","validate target","in scope","scope validation"] |
scope-check
When to Use
- Before testing any new target or endpoint
- When discovered subdomains need scope validation
- Validating IP addresses against allowed CIDR ranges
- Checking if third-party services are in scope
- Verifying wildcard domain coverage
- Identifying out-of-scope exclusions
Quick Start
target="api.example.com"
echo "$target" | grep -qE "^[a-z0-9-]+\.example\.com$" && echo "IN SCOPE" || echo "OUT"
dig +short "$target" | head -1
python3 -c "import ipaddress; print(ipaddress.ip_address('192.168.1.50') in ipaddress.ip_network('192.168.1.0/24'))"
Core Concepts
Scope Types
| Type | Format | Example |
|---|
| Exact Domain | domain.com | api.example.com |
| Wildcard | *.domain.com | *.example.com |
| CIDR Range | ip/prefix | 10.0.0.0/24 |
| Single IP | ip | 203.0.113.50 |
Scope Decision Matrix
| Check | IN SCOPE | OUT OF SCOPE |
|---|
| Domain matches wildcard | ✅ Proceed | ❌ Stop |
| IP in CIDR range | ✅ Proceed | ❌ Stop |
| Explicitly excluded | ❌ Stop | N/A |
| Third-party service | ⚠️ Verify | ❌ Likely out |
| Private IP (RFC-1918) | ⚠️ Verify | ❌ Usually out |
Step-by-Step Process
Phase 1: Parse Scope Definition
cat > scope.json << 'EOF'
{
"in_scope": {
"domains": ["*.example.com", "api.example.com"],
"ips": ["203.0.113.0/24"]
},
"out_of_scope": {
"domains": ["*.staging.example.com", "mail.example.com"],
"third_party": ["*.cloudfront.net", "*.amazonaws.com"]
}
}
EOF
jq -r '.in_scope.domains[]' scope.json
jq -r '.out_of_scope.domains[]' scope.json
Phase 2: Domain Validation
#!/bin/bash
check_domain() {
local target="$1"
local exclusions="mail.example.com staging"
for exc in $exclusions; do
if [[ "$target" == *"$exc"* ]]; then
echo "OUT_OF_SCOPE: Matches exclusion"
return 1
fi
done
if [[ "$target" =~ ^[a-z0-9-]+\.example\.com$ ]]; then
echo "IN_SCOPE: Matches *.example.com"
return 0
fi
echo "OUT_OF_SCOPE: No match"
return 1
}
check_domain "api.example.com"
Phase 3: Wildcard Matching
def wildcard_match(target, pattern):
"""Check if target matches wildcard pattern"""
if pattern.startswith('*.'):
base = pattern[2:]
if target.endswith(f'.{base}'):
subdomain = target[:-len(base)-1]
return '.' not in subdomain
return target == pattern
print(wildcard_match("api.example.com", "*.example.com"))
print(wildcard_match("deep.api.example.com", "*.example.com"))
Phase 4: IP/CIDR Validation
import ipaddress
def ip_in_cidr(ip_str, cidr_str):
"""Check if IP is within CIDR range"""
try:
ip = ipaddress.ip_address(ip_str)
network = ipaddress.ip_network(cidr_str, strict=False)
return ip in network
except ValueError:
return False
def is_private_ip(ip_str):
"""Check RFC-1918 private ranges"""
try:
return ipaddress.ip_address(ip_str).is_private
except ValueError:
return False
print(ip_in_cidr("203.0.113.50", "203.0.113.0/24"))
print(is_private_ip("10.0.0.1"))
Phase 5: DNS Verification
#!/bin/bash
verify_dns() {
local target="$1"
local ip=$(dig +short A "$target" | head -1)
if [ -z "$ip" ]; then
echo "❌ DNS failed: No A record"
return 1
fi
echo "✓ Resolves to: $ip"
local cname=$(dig +short CNAME "$target")
if [ -n "$cname" ]; then
echo "⚠️ CNAME: $cname"
case "$cname" in
*cloudfront.net*|*cloudflare*|*akamai*)
echo "⚠️ Third-party CDN - verify scope!"
;;
esac
fi
}
verify_dns "api.example.com"
Phase 6: Full Validation
import json
import ipaddress
class ScopeValidator:
def __init__(self, scope_file):
with open(scope_file) as f:
self.scope = json.load(f)
def validate(self, target):
try:
ipaddress.ip_address(target)
return self._check_ip(target)
except ValueError:
return self._check_domain(target)
def _check_domain(self, domain):
for exc in self.scope.get('out_of_scope', {}).get('domains', []):
if self._wildcard_match(domain, exc):
return {'status': 'OUT_OF_SCOPE', 'reason': f'Excluded: {exc}'}
for pattern in self.scope.get('in_scope', {}).get('domains', []):
if self._wildcard_match(domain, pattern):
return {'status': 'IN_SCOPE', : }
{: , : }
():
ip_obj = ipaddress.ip_address(ip)
ip_obj.is_private:
{: , : }
cidr .scope.get(, {}).get(, []):
ip_obj ipaddress.ip_network(cidr, strict=):
{: , : }
{: , : }
():
pattern.startswith():
base = pattern[:]
target.endswith() target.count() == pattern.count()
target == pattern
Examples
Example 1: Wildcard Domain Check
Scope: *.example.com (excluding *.staging.example.com)
targets=("api.example.com" "staging.api.example.com" "mail.example.com")
for t in "${targets[@]}"; do
if [[ "$t" == *".staging."* ]]; then
echo "❌ $t - excluded"
elif [[ "$t" =~ ^[a-z0-9-]+\.example\.com$ ]]; then
echo "✓ $t - in scope"
else
echo "❌ $t - out of scope"
fi
done
Example 2: CIDR Range Check
import ipaddress
scope = "192.168.1.0/24"
excluded = "192.168.1.1"
network = ipaddress.ip_network(scope)
for ip in ["192.168.1.50", "192.168.1.1", "10.0.0.1"]:
if ip == excluded:
print(f"❌ {ip} - excluded")
elif ipaddress.ip_address(ip) in network:
print(f"✓ {ip} - in scope")
else:
print(f"❌ {ip} - out of scope")
Example 3: Pre-Test Validation
#!/bin/bash
pretest_check() {
local target="$1"
echo "=== PRE-TEST: $target ==="
ip=$(dig +short A "$target" | head -1)
[ -z "$ip" ] && echo "❌ DNS failed" && return 1
echo "✓ Resolves: $ip"
if [[ "$ip" =~ ^(10\.|192\.168\.|172\.(1[6-9]|2|3[01])\.) ]]; then
echo "⚠️ Private IP - verify authorization"
fi
cname=$(dig +short CNAME "$target")
[ -n "$cname" ] && echo "⚠️ CNAME: $cname"
[[ "$target" == *".example.com" ]] && echo "✓ In scope" || echo "❌ Out of scope"
}
pretest_check "api.example.com"
Example 4: Third-Party Detection
target="cdn.example.com"
cname=$(dig +short CNAME "$target" | head -1)
third_parties="cloudfront.net cloudflare akamai fastly amazonaws.com github.io"
for tp in $third_parties; do
if [[ "$cname" == *"$tp"* ]]; then
echo "⚠️ Third-party detected: $tp"
echo " Verify if testing is authorized!"
break
fi
done
Example 5: Bulk Asset Validation
#!/bin/bash
validate_assets() {
local input_file="$1"
local scope_pattern="$2"
local in_count=0
local out_count=0
while read -r target; do
[ -z "$target" ] && continue
if [[ "$target" =~ $scope_pattern ]]; then
echo "✓ IN: $target"
((in_count++))
else
echo "❌ OUT: $target"
((out_count++))
fi
done < "$input_file"
echo "Summary: $in_count in-scope, $out_count out-of-scope"
}
validate_assets "targets.txt" "^[a-z0-9-]+\.example\.com$"
Example 6: Pre-Exploitation Check
import socket
import ipaddress
def pre_exploit_check(target, scope_domains, scope_cidrs):
"""Complete pre-exploitation verification"""
result = {"target": target, "checks": [], "safe_to_proceed": False}
domain_ok = any(
target.endswith(f".{d.lstrip('*.')}")
for d in scope_domains if d.startswith('*.')
) or target in scope_domains
result["checks"].append({
"name": "domain_scope",
"passed": domain_ok
})
try:
ip = socket.gethostbyname(target)
result["resolved_ip"] = ip
result["checks"].append({"name": "dns_resolution", "passed": True})
except socket.gaierror:
result["checks"].append({"name": "dns_resolution", "passed": False})
return result
ip_obj = ipaddress.ip_address(ip)
ip_ok = any(ip_obj in ipaddress.ip_network(c, strict=False) for c in scope_cidrs)
result["checks"].append({"name": "ip_scope", : ip_ok})
ip_obj.is_private:
result[].append({: , : })
result[] = (c[] c result[])
result
check = pre_exploit_check(, [], [])
()
Example 7: Redirect Chain Validation
import requests
from urllib.parse import urlparse
def check_redirect_scope(url, scope_domains):
"""Follow redirects and verify all destinations are in scope"""
result = {"original_url": url, "all_in_scope": True, "out_of_scope": []}
try:
response = requests.get(url, allow_redirects=True, timeout=10)
for r in response.history:
location = r.headers.get('Location', '')
domain = urlparse(location).netloc
in_scope = any(
domain.endswith(d.lstrip('*.')) or domain == d
for d in scope_domains
)
if not in_scope and domain:
result["all_in_scope"] = False
result["out_of_scope"].append(domain)
result["final_url"] = response.url
except requests.RequestException as e:
result["error"] = str(e)
return result
result = check_redirect_scope("https://example.com/redirect", ["*.example.com"])
print(f"All in scope: {result['all_in_scope']}")
Error Handling
| Issue | Cause | Resolution |
|---|
| DNS resolution fails | Domain doesn't exist | Verify target spelling |
| Invalid CIDR format | Malformed network | Use ip/prefix format |
| Scope file parse error | Invalid JSON | Validate JSON syntax |
| Private IP detected | Internal network | Confirm VPN authorization |
| Third-party CNAME | External hosting | Get explicit permission |
Output Format
{
"target": "api.example.com",
"status": "IN_SCOPE",
"reason": "Matches *.example.com",
"resolved_ip": "203.0.113.50",
"warnings": [],
"safe_to_test": true
}
CIDR Quick Reference
| CIDR | Hosts | Range Example |
|---|
| /32 | 1 | Single IP |
| /24 | 254 | x.x.x.1-254 |
| /16 | 65,534 | x.x.0.1-255.254 |
| /8 | 16.7M | x.0.0.1-255.255.254 |
RFC-1918 Private Ranges
| Range | CIDR | Class |
|---|
| 10.0.0.0 - 10.255.255.255 | 10.0.0.0/8 | A |
| 172.16.0.0 - 172.31.255.255 | 172.16.0.0/12 | B |
| 192.168.0.0 - 192.168.255.255 | 192.168.0.0/16 | C |
Platform Scope Formats
HackerOne
Asset Type: Domain
Identifier: *.example.com
Eligible for Bounty: Yes
Bugcrowd
Target: *.example.com
Type: Website
Priority: P1
Synack
Scope: example.com and subdomains
IP Range: 203.0.113.0/24
Validation Checklist
Advanced Techniques
Deep Wildcard Matching
def deep_wildcard_match(target, pattern):
"""Handle ** patterns for any subdomain depth"""
if pattern.startswith('**.'):
base = pattern[3:]
return target == base or target.endswith(f'.{base}')
elif pattern.startswith('*.'):
base = pattern[2:]
if target.endswith(f'.{base}'):
subdomain = target[:-len(base)-1]
return '.' not in subdomain
return target == pattern
print(deep_wildcard_match("a.b.c.example.com", "**.example.com"))
print(deep_wildcard_match("api.example.com", "**.example.com"))
print(deep_wildcard_match("a.b.example.com", "*.example.com"))
print(deep_wildcard_match("api.example.com", "*.example.com"))
IP Range Enumeration
import ipaddress
def enumerate_scope(cidr, excluded=None):
"""Get all IPs in CIDR excluding specified addresses"""
excluded = set(excluded or [])
network = ipaddress.ip_network(cidr, strict=False)
return [str(ip) for ip in network.hosts() if str(ip) not in excluded]
hosts = enumerate_scope("203.0.113.0/28", ["203.0.113.1"])
print(f"Enumerated {len(hosts)} hosts")
ASN Validation
#!/bin/bash
check_asn() {
local ip="$1"
local expected_asn="$2"
asn=$(whois -h whois.cymru.com " -v $ip" | tail -1 | awk '{print $1}')
if [ "$asn" = "$expected_asn" ]; then
echo "✓ IP $ip belongs to ASN $expected_asn"
return 0
else
echo "❌ IP $ip belongs to ASN $asn (expected $expected_asn)"
return 1
fi
}
check_asn "8.8.8.8" "AS15169"
Common Patterns
Exclusion Keywords
exclusion_patterns=(
"staging"
"dev"
"test"
"internal"
"corp"
"admin"
"vpn"
"mail"
"mx"
"ns"
)
Third-Party Indicators
| Provider | CNAME Pattern | Service Type |
|---|
| CloudFront | *.cloudfront.net | CDN |
| Cloudflare | *.cloudflare* | CDN/WAF |
| Akamai | *.akamai* | CDN |
| AWS | *.amazonaws.com | Cloud |
| Azure | *.azure* | Cloud |
| GitHub | *.github.io | Hosting |
| Heroku | *.herokuapp.com | PaaS |
| Netlify | *.netlify* | Hosting |
References