Links

Role Abuse: SSM

This page describes how a compromised machine with the default AmazonSSMRoleForInstancesQuickSetup role can allow an attacker to move laterally to all other machines holding this role in the VPC.

Introduction

In this article, we will explore an interesting attack path in AWS that we encountered in the past: The Amazon Systems Manager. This solution allows centralized management of cloud and on-premise resources. It can by activated for an EC2 instance by assigning a role to the endpoint. However, the default instance role assignment, AmazonSSMRoleForInstancesQuickSetup, has an interesting side-effect: Instances with this role can execute commands or start sessions on each other.
We will attempt to move laterally from the compromised ACTIVEMQ EC2 instance with the AmazonSSMRoleForInstancesQuickSetup role to the DOTNETNUKE machine, which also holds this role. This article uses Cobalt Strike. However, any C2 framework could do, given some creativity.
Attack scenario overview

Request AWS Credentials for SSM Role

We start the jouney on the compromised ACTIVEMQ EC2 instance, where we have a Cobalt Strike session. We will use the Curl BOF from Alfie Champion's BOF collection to send an HTTP request to the internal metadata API in AWS.
curl https://169.254.169.254/latest/meta-data/iam/security-credentials/
Cobalt Strike Curl BOF to contact the internal metadata API
This results in an HTTP response, indicating that this machine holds the AmazonSSMRoleForInstancesQuickSetup role.
Result showing AmazonSSMRoleForInstancesQuickSetup
Next, we request AWS credentials for this role.
curl https://169.254.169.254/latest/meta-data/iam/security-credentials/AmazonSSMRoleForInstancesQuickSetup
Requesting credentials for the role
The response comes back with an AccessKeyId, SecretAccessKey and Token value (redacted). This means that we can now impersonate the compromised instance in the context of the AmazonSSMRoleForInstancesQuickSetup role.
Obtained AWS Credentials for the role

Console Access with Pacu

The newly obtained credentials can be used to interact with the AWS APIs programatically, or we can use Rhinosecuritylabs' pacu tool to generate a console link for our convenience. The first step is to create a ~/.aws/credentials file with the credentials:
[default]
aws_access_key_id=<insert AccessKeyId value>
aws_secret_access_key=<insert SecretAccessKey value>
aws_session_token=<insert Token value>
Next, pacu can be launched via docker, while mounting the AWS credentials in the container.
docker run -it -v ~/.aws:/root/.aws rhinosecuritylabs/pacu:latest
Launching pacu
Next, we can import the AWS credentials and validate our role through the AWS console CLI.
import_keys --all
aws sts get-caller-identity
Imported the keys and validated the assumed role
Finally, pacu can generate the console login link to authenticate via a web browser via the following command:
console
Pacu generated a (redacted) console link
Browsing the URL automagically authenticates as the compromised instance with the AmazonSSMRoleForInstancesQuickSetup role.
AWS Console access as the compromised ACTIVEMQ instance with the AmazonSSMRoleForInstancesQuickSetup role

Session Manager

At this point, our role allows us to access the the AWS System Manager via the AWS Console. This service has (at least) two interesting features under Node Management: Session Manager and Run Command.
Run Command allows to run individual commands. For example, imagine a scenario where 30 Linux machines in the AWS VPC have the AmazonSSMRoleForInstancesQuickSetup. Machine 1 has an application running with an SSRF vulnerability, allowing an attacker to request AWS credentials from the internal metadata API and asume the role. He could use this interact with the AWS API to run a command to download and run a Command and Control agent on all 29 other machines.
We will focus on the manual approach via the Session Manager, which allows starting PowerShell and other sessions on the other machines with the default SSM role. The first step is to browse the AWS console and start a new session in the Session Manager.
Since we already compromised ACTIVEMQ, we will start a session on the only other available machine, DOTNETNUKE.
This drops us in a PowerShell session as the privileged ssm-user, where we first perform a trivial AMSI bypass (copy and edit a payload from amsi.fail) and then download and execute a stageless PowerShell beacon in-memory.
AMSI bypass and beacon launch from PowerShell
This results in an active Cobalt Strike C2 connection on DOTNETNUKE as the ssm-user from powershell.exe.
Beacon from DOTNETNUKE

Send-Command

Alternatively, it is also possible to use AWS-RunShellScript (Linux) or AWS-RunPowerShellScript (Windows) via the AWS CLI to execute commands on target instances. This can be useful in case of insufficient privileges to start a session or request a federation token with Pacu.
The first step is to identify other target instances. The following SSM command can achieve this:
aws ssm describe-instance-information --output text --query "InstanceInformationList[*]" --region eu-west-1
If the instance role does not allow you to describe other instances in the environment, look for another source of instance IDs: OSINT, s3 bucket access (e.g Cloudwatch logs contain instance IDs), internal enumeration...)
The next step is to execute the command on the target instance. The following command can be used for the corresponding OS. Replace instance-ID with your target instance ID and change the region if needed.
For Linux:
aws ssm send-command --instance-ids "instance-ID" --document-name "AWS-RunShellScript" --comment "IP config" --parameters commands=ifconfig --output text --region eu-west-1
For Windows (with some quoted-string-kung-fu)
aws ssm send-command --instance-ids "INSTANCEID" --document-name "AWS-RunPowerShellScript" --comment "List programs" --parameters '{"commands":["dir","\"C:\\program\\ files\""]}' --output text --region eu-west-1
Finally, list the output for the executed command via the following command (replace the COMMANID with the output of the previous command)
aws ssm list-command-invocations --command-id COMMANDID --details --region eu-west-1

Conclusion

In this article, we explored how the default Amazon Systems Manager role of one compromised machine can be abused to move laterally to other instances holding the same role.

Mitigation

If you want to manage instances via AWS Systems Manager, use the AmazonSSMManagedInstanceCore policy, instead of following the AmazonSSMRoleForInstancesQuickSetup role creation procedure. This is the recommended way to enable AWS Systems Manager service core functionality.

Detection

Amazon Guardduty has the capability to detect aws credentials from one instance being used from another account/instance. However, this can be circumvented by executing the attack from the original instance.
Therefore, it might be interesting to also baseline the AWS environment and monitor for anomalous SSM documents being applied to instances. Build a whitelist of entities that should be allowed to do this and alert/block the rest.

References