Cloud IAM Permission Overreach
Description
Cloud IAM roles and policies configured with excessive permissions, violating the principle of least privilege. This includes EC2 instances with broad S3 access, Lambda functions with administrative permissions, or service accounts with wildcard resource access. Often combined with other vulnerabilities (like SSRF) to create catastrophic breaches.
Illustrative Cantor Point
The Cantor Point occurs when defining IAM policies - choosing between restrictive policies that might break functionality versus permissive policies that "just work." The decision to grant broad permissions to avoid troubleshooting creates a divergent path where any application vulnerability becomes a cloud-wide security breach.
Real-World Examples / Observed In
- Capital One (2019): EC2 instance with overly permissive S3 access allowed extraction of 100M+ customer records via SSRF [See: Cases-By-Year/2019 Data Integrity Failures.md#4]
- Common Pattern: S3:* permissions on production buckets, EC2 instances with full AWS access
- Typical Mistake: Using AWS managed policies instead of custom least-privilege policies
Common Consequences & Impacts
Technical Impacts
- - Complete cloud account compromise
- - Lateral movement across services
- - Data exfiltration at scale
- - Cryptocurrency mining abuse
Human/Ethical Impacts
- - Personal data exposure
- - Financial information theft
- - Regulatory violations
- - Loss of customer trust
Business Impacts
- - Massive data breaches
- - Cloud service suspension
- - Unexpected cloud bills
- - Complete infrastructure compromise
Recovery Difficulty & Escalation
ADI Principles & Axioms Violated
- Principle of Trust Insufficiency: Over-trusting application security
- Principle of Boundary Criticality: IAM is the critical security boundary
Detection / 60-Second Audit
```sql
-- Track cloud IAM permissions in database
CREATE TABLE IF NOT EXISTS cloud_iam_audit (
id SERIAL PRIMARY KEY,
service_name VARCHAR(255),
role_arn VARCHAR(500),
policy_document JSONB,
risk_score INTEGER,
last_reviewed DATE
);
-- High-risk permission patterns
SELECT
service_name,
role_arn,
CASE
WHEN policy_document::text LIKE '%"*"%' THEN 'Contains wildcard permissions'
WHEN policy_document::text LIKE '%AdministratorAccess%' THEN 'Has admin access'
WHEN policy_document::text LIKE '%s3:*%' THEN 'Unrestricted S3 access'
ELSE 'Review needed'
END as risk_reason,
risk_score
FROM cloud_iam_audit
WHERE risk_score > 7
OR policy_document::text LIKE '%"*"%'
OR policy_document::text LIKE '%:*"%';
-- Check for service accounts with excessive permissions
SELECT
username,
CASE
WHEN has_superuser THEN 'Database superuser - CRITICAL'
WHEN can_create_db THEN 'Can create databases - HIGH'
WHEN can_create_role THEN 'Can create roles - HIGH'
ELSE 'Review permissions'
END as risk_level
FROM (
SELECT
usename as username,
usesuper as has_superuser,
usecreatedb as can_create_db,
usecreaterole as can_create_role
FROM pg_user
WHERE usename LIKE 'svc_%' OR usename LIKE 'app_%'
) service_accounts;
```
```sql
-- Track cloud IAM permissions in database
CREATE TABLE IF NOT EXISTS cloud_iam_audit (
id INT AUTO_INCREMENT PRIMARY KEY,
service_name VARCHAR(255),
role_arn VARCHAR(500),
policy_document JSON,
risk_score INT,
last_reviewed DATE
);
-- High-risk permission patterns
SELECT
service_name,
role_arn,
CASE
WHEN JSON_CONTAINS(policy_document, '"*"') THEN 'Contains wildcard permissions'
WHEN JSON_SEARCH(policy_document, 'all', 'AdministratorAccess') IS NOT NULL THEN 'Has admin access'
WHEN JSON_SEARCH(policy_document, 'all', 's3:*') IS NOT NULL THEN 'Unrestricted S3 access'
ELSE 'Review needed'
END as risk_reason,
risk_score
FROM cloud_iam_audit
WHERE risk_score > 7
OR JSON_CONTAINS(policy_document, '"*"')
OR JSON_SEARCH(policy_document, 'all', ':*') IS NOT NULL;
-- Check for service accounts with excessive permissions
SELECT
user,
host,
CASE
WHEN super_priv = 'Y' THEN 'Database superuser - CRITICAL'
WHEN create_priv = 'Y' THEN 'Can create databases - HIGH'
WHEN grant_priv = 'Y' THEN 'Can grant privileges - HIGH'
ELSE 'Review permissions'
END as risk_level
FROM mysql.user
WHERE user LIKE 'svc_%' OR user LIKE 'app_%';
```
```sql
-- Track cloud IAM permissions in database
CREATE TABLE cloud_iam_audit (
id INT IDENTITY PRIMARY KEY,
service_name NVARCHAR(255),
role_arn NVARCHAR(500),
policy_document NVARCHAR(MAX),
risk_score INT,
last_reviewed DATE
);
-- High-risk permission patterns
SELECT
service_name,
role_arn,
CASE
WHEN policy_document LIKE '%"*"%' THEN 'Contains wildcard permissions'
WHEN policy_document LIKE '%AdministratorAccess%' THEN 'Has admin access'
WHEN policy_document LIKE '%s3:*%' THEN 'Unrestricted S3 access'
ELSE 'Review needed'
END as risk_reason,
risk_score
FROM cloud_iam_audit
WHERE risk_score > 7
OR policy_document LIKE '%"*"%'
OR policy_document LIKE '%:*%';
-- Check for service accounts with excessive permissions
SELECT
p.name as username,
CASE
WHEN IS_SRVROLEMEMBER('sysadmin', p.name) = 1 THEN 'Database sysadmin - CRITICAL'
WHEN IS_SRVROLEMEMBER('dbcreator', p.name) = 1 THEN 'Can create databases - HIGH'
WHEN IS_SRVROLEMEMBER('securityadmin', p.name) = 1 THEN 'Security admin - HIGH'
ELSE 'Review permissions'
END as risk_level
FROM sys.server_principals p
WHERE p.type = 'S'
AND (p.name LIKE 'svc_%' OR p.name LIKE 'app_%');
Prevention & Mitigation Best Practices
Implement Least Privilege Policies:
// Bad: Overly permissive S3 policy { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": "s3:*", "Resource": "*" }] } // Good: Specific, limited permissions { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": [ "s3:GetObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::my-app-data-bucket", "arn:aws:s3:::my-app-data-bucket/*" ], "Condition": { "StringEquals": { "s3:ExistingObjectTag/Environment": "production" } } }] }Database Schema for IAM Management:
CREATE TABLE iam_role_definitions ( id SERIAL PRIMARY KEY, role_name VARCHAR(255) UNIQUE NOT NULL, service_name VARCHAR(255) NOT NULL, environment VARCHAR(50) NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), created_by VARCHAR(255) NOT NULL, approved_by VARCHAR(255), approval_date DATE ); CREATE TABLE iam_permission_requirements ( id SERIAL PRIMARY KEY, role_id INTEGER REFERENCES iam_role_definitions(id), aws_service VARCHAR(100) NOT NULL, actions TEXT[] NOT NULL, resources TEXT[] NOT NULL, conditions JSONB, justification TEXT NOT NULL, risk_assessment TEXT ); CREATE TABLE iam_permission_reviews ( id SERIAL PRIMARY KEY, role_id INTEGER REFERENCES iam_role_definitions(id), review_date DATE NOT NULL, reviewer VARCHAR(255) NOT NULL, findings TEXT, actions_taken TEXT, next_review_date DATE NOT NULL );Automated Permission Monitoring:
-- Track permission usage CREATE TABLE iam_permission_usage ( id BIGSERIAL PRIMARY KEY, role_arn VARCHAR(500), action VARCHAR(255), resource VARCHAR(500), used_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), source_ip INET, user_agent TEXT ); -- Identify unused permissions CREATE VIEW unused_permissions AS SELECT pr.role_id, pr.aws_service, pr.actions, rd.role_name FROM iam_permission_requirements pr JOIN iam_role_definitions rd ON pr.role_id = rd.id WHERE NOT EXISTS ( SELECT 1 FROM iam_permission_usage pu WHERE pu.role_arn LIKE '%' || rd.role_name || '%' AND pu.action = ANY(pr.actions) AND pu.used_at > NOW() - INTERVAL '90 days' );Service-Specific Roles:
-- Enforce service isolation ALTER TABLE iam_role_definitions ADD CONSTRAINT one_role_per_service_env UNIQUE (service_name, environment); -- Prevent role sharing CREATE OR REPLACE FUNCTION prevent_role_sharing() RETURNS TRIGGER AS $ BEGIN IF EXISTS ( SELECT 1 FROM iam_role_definitions WHERE role_name = NEW.role_name AND service_name != NEW.service_name ) THEN RAISE EXCEPTION 'Role % already assigned to different service', NEW.role_name; END IF; RETURN NEW; END; $ LANGUAGE plpgsql;Additional Best Practices:
- Use AWS Organizations SCPs to prevent dangerous actions
- Implement mandatory MFA for all IAM operations
- Regular access reviews (quarterly minimum)
- Use AWS Access Analyzer for continuous monitoring
- Implement break-glass procedures for emergencies
Real World Examples
# The vulnerability chain:
1. Web Application Firewall (WAF) misconfiguration
2. Server-Side Request Forgery (SSRF) vulnerability
3. EC2 instance with excessive S3 permissions
# The IAM role attached to EC2:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::capital-one-*" // Wildcard!
}]
}
# How it was exploited:
1. Attacker found SSRF: http://app.com/api?url=http://169.254.169.254/
2. Retrieved IAM credentials from metadata service
3. Used credentials to list ALL Capital One S3 buckets
4. Downloaded 106 million credit applications
# Impact:
- 100 million US customers affected
- 6 million Canadian customers affected
- $80M fine from regulators
- $190M total in fines and customer settlements
# Vulnerable configuration:
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-service-account
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/eksServiceRole
# The attached IAM role (too permissive):
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"s3:*",
"dynamodb:*",
"secretsmanager:*"
],
"Resource": "*"
}]
}
# Result: Any pod compromise = full AWS access
// Before (vulnerable):
{
"Statement": [{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}]
}
// After (secure):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListSpecificBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::myapp-data-prod",
"Condition": {
"StringLike": {
"s3:prefix": ["user-data/${aws:userid}/*"]
}
}
},
{
"Sid": "ReadWriteUserData",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::myapp-data-prod/user-data/${aws:userid}/*"
},
{
"Sid": "DenyUnencryptedUploads",
"Effect": "Deny",
"Action": "s3:PutObject",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "AES256"
}
}
}
]
}
# Additional security layers:
- Enable S3 Block Public Access
- Use VPC endpoints for S3
- Enable CloudTrail logging
- Set up AWS Config rules for compliance
- Implement session-based temporary credentials
AI Coding Guidance/Prompt
Prompt: "When creating cloud IAM policies:"
Rules:
- Never use wildcard (*) permissions
- Always specify exact resource ARNs
- Require business justification for each permission
- Flag any policy without conditions
- Suggest time-based or IP-based restrictions
- Require separate roles for each service/function
Example:
# Bad: Kitchen sink permissions
resource "aws_iam_role_policy" "bad_policy" {
role = aws_iam_role.app_role.id
policy = jsonencode({
Statement = [{
Effect = "Allow"
Action = ["s3:*", "dynamodb:*", "lambda:*"]
Resource = "*"
}]
})
}
# Good: Least privilege with conditions
resource "aws_iam_role_policy" "good_policy" {
role = aws_iam_role.app_role.id
policy = jsonencode({
Statement = [
{
Sid = "ReadSpecificS3Bucket"
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:ListBucket"
]
Resource = [
"arn:aws:s3:::my-app-data-prod",
"arn:aws:s3:::my-app-data-prod/*"
]
Condition = {
IpAddress = {
"aws:SourceIp": ["10.0.0.0/8"]
}
DateGreaterThan = {
"aws:CurrentTime": "2024-01-01T00:00:00Z"
}
}
},
{
Sid = "WriteToSpecificDynamoTable"
Effect = "Allow"
Action = [
"dynamodb:PutItem",
"dynamodb:UpdateItem"
]
Resource = "arn:aws:dynamodb:us-east-1:123456789012:table/AppData"
Condition = {
StringEquals = {
"dynamodb:LeadingKeys": ["${aws:userid}"]
}
}
}
]
})
}
Relevant Keywords
cloud permission overreach Symptoms: slow queries, data inconsistency, constraint violations Preventive: schema validation, constraint enforcement, proper typing Tech stack: PostgreSQL, MySQL, SQL Server, Oracle Industry: all industries, enterprise, SaaS