| Title | devopspolis secrets-replicator 0.5.0 Incorrect Permission Assignment |
|---|
| Description | secrets-replicator is a serverless application that replicates AWS Secrets Manager secrets across regions and AWS accounts. The following four security issues were identified and can be chained together:
VULN-001 – Overly Broad IAM Permissions on Lambda Execution Role
The Lambda execution role's IAM policy uses Resource: "" for both secretsmanager: and sts:AssumeRole actions. This permits the function to read any secret in the source account and write to or assume any role in destination accounts that have a trust relationship configured.
VULN-002 – Caller-Controlled Input on Manual Invocation
The manual invocation path accepts a caller-supplied secretId or secretIds array in the payload. Any principal with lambda:InvokeFunction permission can leverage the high-privilege role to read and replicate arbitrary secrets.
VULN-003 – Overly Broad EventBridge Trigger Pattern
The EventBridge rule matches CreateSecret, PutSecretValue, and UpdateSecret events from the aws.secretsmanager source. Any principal with write permissions to a source secret can inadvertently or maliciously trigger replication to all configured destinations.
VULN-004 – ExternalId Not Propagated to STS AssumeRole
This is the only vulnerability formally acknowledged and fixed by the maintainer. Although the destination configuration supports an account_role_arn field, the code does not pass the optional external_id parameter to the underlying STS AssumeRole call. As a result, when the destination account's IAM role trust policy requires sts:ExternalId, the AssumeRole operation fails, rendering this confused-deputy protection mechanism ineffective. SECURITY.md previously claimed External ID as "Required," but it was never used in the code; this description was corrected to "Supported" in the fix commit.
IMPACT
Severity: High
An attacker with lambda:InvokeFunction permissions or write permissions (secretsmanager:PutSecretValue, etc.) on a source secret can:
Use the Lambda role to read arbitrary secrets from the source account.
In cross-account deployments, replicate any source secret to a destination account if that account's role trusts the Lambda execution role.
This chain can lead to sensitive credential exposure, privilege escalation, and unintended cross-account data leakage.
Attack chains:
VULN-002 -> VULN-001: Direct Lambda manual trigger causes secret replication.
VULN-003 -> VULN-001: Real Secrets Manager PutSecretValue event triggers EventBridge and causes replication.
(VULN-003 -> VULN-001) -> VULN-004: Cross-account destination role is assumed and replication extends to another AWS account.
While VULN-004 has been acknowledged and fixed, the remaining issues were classified as "design trade-offs" by the maintainer and have not been remediated. However, even as design trade-offs, the chaining effect of VULN-001 with VULN-002 and VULN-003 significantly amplifies the actual risk, allowing any principal who can invoke the Lambda or trigger matching Secrets Manager events to perform unauthorized secret replication using the high-privilege Lambda role.
AFFECTED VERSIONS
Confirmed affected: 0.4.0 (deployed from AWS Serverless Application Repository)
Earlier versions are likely affected as well, as they share the same design patterns.
STEPS TO REPRODUCE
Detailed reproduction steps are available in the original report. The key attack chains are summarized below.
Reproducing VULN-002 -> VULN-001 (Manual Trigger Chain)
After configuring a destination region, invoke the Lambda manually with an arbitrary secret name (e.g., Salesforce) to trigger replication:
aws lambda invoke
--region us-east-1
--function-name secrets-replicator
--cli-binary-format raw-in-base64-out
--payload '{"source":"manual","secretIds":["Salesforce"],"region":"us-east-1"}'
output.json
Observed Result: The secret Salesforce is successfully replicated to us-west-2, even if it is not on any allowlist. Logs show "Successfully synced 1 secret(s)."
Reproducing VULN-003 -> VULN-001 (Event-Driven Trigger Chain)
Before performing put-secret-value, ensure CloudTrail is enabled and delivering events to EventBridge. Performing an update operation on any secret triggers the EventBridge rule and invokes the Lambda, causing replication:
aws secretsmanager put-secret-value
--region us-east-1
--secret-id Salesforce
--secret-string '{"stage":"chain-test"}'
sleep 120
aws logs tail /aws/lambda/secrets-replicator --region us-east-1 --since 10m
Observed Result: Logs show "Event parsed successfully: event_name=PutSecretValue, secret_id=Salesforce." The secret is replicated to all configured destination regions.
Reproducing VULN-004 (ExternalId Not Propagated)
In the destination account, create an IAM role with a trust policy requiring sts:ExternalId:
cat > trust-with-externalid.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::SOURCE_ACCOUNT_ID:role/SOURCE_LAMBDA_ROLE"},
"Action": "sts:AssumeRole",
"Condition": {"StringEquals": {"sts:ExternalId": "test-external-id"}}
}
]
}
EOF
aws iam create-role --role-name SecretsReplicatorDestRole --assume-role-policy-document file://trust-with-externalid.json
In the source account, configure the cross-account destination and trigger replication:
aws secretsmanager put-secret-value --region us-east-1 --secret-id secrets-replicator/config/destinations --secret-string '[{"region":"us-west-2","account_role_arn":"arn:aws:iam::DESTINATION_ACCOUNT_ID:role/SecretsReplicatorDestRole"}]'
aws secretsmanager put-secret-value --region us-east-1 --secret-id Salesforce --secret-string '{"stage":"externalid-test"}'
Observed Result: AssumeRole fails. Logs show "not authorized to perform: sts:AssumeRole."
Remove the ExternalId condition from the trust policy and trigger again. Replication succeeds. This confirms that the ExternalId parameter is not being passed to the STS AssumeRole call.
SUGGESTED FIX
For VULN-004 – The maintainer has already fixed this issue in commit b422394. The fix adds an external_id field to DestinationConfig and passes it to create_secrets_manager_client for STS AssumeRole. Deploy this fix or a later version.
For VULN-001, VULN-002, and VULN-003 – The following mitigations are recommended:
Scope IAM Permissions: Tighten the Lambda execution role's Resource from "*" to a specific list of source secret ARNs and destination role ARNs. Also restrict sts:AssumeRole to approved destination role ARNs only.
Enforce an Allowlist: Before reading any secret, check it against a mandatory allowlist (e.g., secret name prefix or tag). This check should happen early in the execution flow, regardless of the invocation source (manual or event-driven), rather than after event or manual input parsing.
Strengthen Manual Invocation Authorization: Require a shared secret or HMAC signature in the payload for manual invocations, rather than relying solely on lambda:Invoke permissions. Also consider further restricting invocation sources via IAM condition keys such as lambda:SourceArn.
Narrow EventBridge Rules: Add more granular filtering, such as matching only secrets with specific name prefixes or requiring a specific tag on the secret, to avoid every PutSecretValue event entering the replication path.
Documentation and Deployment Safety: Clearly document that cross-account destination roles should require sts:ExternalId, and make deployments fail closed if account_role_arn is configured without a corresponding external_id, rather than silently ignoring it.
DISCLOSURE TIMELINE
Vulnerability discovered and initial contact attempted via SECURITY.md: June 2026
Maintainer acknowledged VULN-004 and released commit b422394: June 15, 2026
Multiple follow-ups regarding remaining issues and new version release: June – September 2026
Maintainer completely unresponsive, no further replies: June 2026 – present
|
|---|
| Source | ⚠️ https://github.com/devopspolis/secrets-replicator/commit/b422394 |
|---|
| User | changli (UID 99220) |
|---|
| Submission | 09/09/2026 04:44 (25 days ago) |
|---|
| Moderation | 10/04/2026 10:00 (25 days later) |
|---|
| Status | Accepted |
|---|
| VulDB entry | 413397 [devopspolis secrets-replicator up to 0.4.0 AssumeRole src/handler.py process_single_secret external_id permission assignment] |
|---|
| Points | 20 |
|---|