· PathShield Team · Tutorials · 8 min read
What Is an Attack Path? Why It Matters for Your AWS Setup
Understand attack paths in AWS and why they're the key to real cloud security. Learn how attackers chain misconfigurations to breach your systems.
What Is an Attack Path? Why It Matters for Your AWS Setup
You’ve secured your S3 buckets. Your databases are private. IAM policies look good. Yet attackers can still reach your crown jewels through a chain of seemingly innocent misconfigurations. This is the reality of attack paths—and why traditional security scanning isn’t enough.
Attack Paths: The Hacker’s Roadmap
An attack path is a sequence of steps an attacker can take to move from an initial foothold to your sensitive data. Think of it as connecting the dots between misconfigurations that individually seem harmless but together create a highway to your data.
A Real Attack Path Example
Here’s an actual attack path found in a startup’s AWS environment:
1. Public S3 bucket contains old Lambda deployment package
↓
2. Lambda package has hardcoded IAM credentials
↓
3. IAM credentials have read access to "dev" resources
↓
4. Dev EC2 instance has metadata service v1 (vulnerable)
↓
5. EC2 instance role can assume production database role
↓
6. Production database contains all customer data
Individual security scans said: “Everything looks fine” Attack path analysis revealed: “6 steps to complete compromise”
Why Attack Paths Are Your Biggest Blind Spot
Traditional Scanning Misses the Connections
# What traditional scanners see
scan_results = {
"s3_bucket_public": "LOW - Contains only old code",
"iam_credentials": "MEDIUM - In development file",
"ec2_metadata": "LOW - Internal service",
"role_assumption": "OK - Part of deployment process",
"database_access": "OK - Production role has access"
}
# What attack path analysis sees
attack_path = {
"risk": "CRITICAL",
"steps": 5,
"time_to_exploit": "< 30 minutes",
"data_at_risk": "100% of customer records"
}
Real Breaches Follow Attack Paths
The Capital One breach? An attack path:
- SSRF vulnerability in web application
- Access EC2 metadata service
- Retrieve IAM role credentials
- Access S3 buckets
- Exfiltrate 100 million records
Each step was a “minor” issue. Together, they cost $80 million.
Anatomy of AWS Attack Paths
Common Attack Path Patterns
1. The Credential Chain
GitHub repo → AWS keys → Dev account → Cross-account role → Production
2. The Public Resource Path
Public S3 → Lambda function → Secrets Manager → RDS database
3. The Over-Privileged Service Path
Compromised container → ECS task role → Administrative permissions → Account takeover
4. The Supply Chain Path
Third-party integration → API Gateway → Lambda → DynamoDB
How Attackers Find These Paths
# Attacker's reconnaissance process
def find_attack_paths():
# Step 1: Enumerate public resources
public_assets = find_public_s3_buckets() + find_open_ports()
# Step 2: Look for credentials
for asset in public_assets:
creds = search_for_credentials(asset)
if creds:
accessible_resources = test_credential_access(creds)
# Step 3: Map privilege escalation
for resource in accessible_resources:
roles = find_assumable_roles(resource)
for role in roles:
map_role_permissions(role)
# Step 4: Find shortest path to data
paths = calculate_paths_to_sensitive_data()
return sorted(paths, key=lambda x: x.steps)
Real Attack Paths We’ve Found in Startups
Case 1: The Logging Nightmare
CloudWatch Logs (public read)
→ Application logs with debug info
→ Database connection strings
→ Direct database access
→ All customer data compromised
Fix required: 1 IAM policy change Time to exploit: 15 minutes Data exposed: Everything
Case 2: The CI/CD Pipeline Path
Public GitHub repo
→ .github/workflows/deploy.yml
→ References secret name
→ Enumerate GitHub Actions logs
→ Find AWS role ARN
→ Attempt role assumption
→ Success: Deploy role has admin access
Fix required: Proper OIDC configuration Time to exploit: 2 hours Impact: Complete infrastructure control
Case 3: The Backup Backdoor
Old EC2 snapshot (public)
→ Mount and examine filesystem
→ Find .aws/credentials file
→ Credentials still valid (never rotated)
→ Access current production S3 buckets
→ Download all backups
Fix required: Snapshot encryption + key rotation Time to exploit: 45 minutes Data exposed: 2 years of backups
Visualizing Attack Paths
Simple Text Representation
Internet → ALB → Web App → API Gateway → Lambda → DynamoDB
↓
EC2 Metadata
↓
Task Role
↓
Secrets Manager
↓
RDS Admin
Graph Representation
# Attack path as a graph
attack_graph = {
"nodes": [
{"id": "internet", "type": "external"},
{"id": "s3-public", "type": "storage", "risk": "high"},
{"id": "lambda-old", "type": "compute", "risk": "medium"},
{"id": "iam-creds", "type": "credential", "risk": "high"},
{"id": "rds-prod", "type": "database", "risk": "critical"}
],
"edges": [
{"from": "internet", "to": "s3-public", "method": "public access"},
{"from": "s3-public", "to": "lambda-old", "method": "download code"},
{"from": "lambda-old", "to": "iam-creds", "method": "extract from env"},
{"from": "iam-creds", "to": "rds-prod", "method": "assume role"}
]
}
Finding Attack Paths in Your AWS
Manual Attack Path Discovery
# Step 1: Find all public resources
aws s3api list-buckets --query 'Buckets[*].Name' | \
xargs -I {} aws s3api get-bucket-acl --bucket {} | \
grep -B 2 "AllUsers"
# Step 2: Check for credential exposure
find . -type f -name "*.env" -o -name "*.yml" -o -name "*.yaml" | \
xargs grep -l "AKIA\|AWS_ACCESS_KEY_ID"
# Step 3: Map IAM permissions
aws iam get-account-authorization-details > iam-full-report.json
# Step 4: Analyze role trust policies
aws iam list-roles --query 'Roles[*].[RoleName,AssumeRolePolicyDocument]' | \
grep -A 10 -B 10 '"Service":\|"AWS":'
Automated Attack Path Detection
# attack_path_finder.py
import boto3
import networkx as nx
from collections import defaultdict
class AttackPathFinder:
def __init__(self):
self.graph = nx.DiGraph()
self.sensitive_resources = []
def build_resource_graph(self):
"""Build graph of all AWS resources and their relationships"""
# Add public entry points
self.add_public_resources()
# Add IAM relationships
self.add_iam_relationships()
# Add network paths
self.add_network_paths()
# Add resource policies
self.add_resource_policies()
def add_public_resources(self):
"""Find all publicly accessible resources"""
s3 = boto3.client('s3')
# Check S3 buckets
for bucket in s3.list_buckets()['Buckets']:
bucket_name = bucket['Name']
try:
acl = s3.get_bucket_acl(Bucket=bucket_name)
for grant in acl['Grants']:
if 'URI' in grant['Grantee'] and 'AllUsers' in grant['Grantee']['URI']:
self.graph.add_edge('Internet', f's3:{bucket_name}',
access_type='public_read')
except:
pass
def add_iam_relationships(self):
"""Map IAM roles and their trust relationships"""
iam = boto3.client('iam')
# Get all roles
for role in iam.list_roles()['Roles']:
role_name = role['RoleName']
trust_policy = role['AssumeRolePolicyDocument']
# Parse trust relationships
for statement in trust_policy['Statement']:
if statement['Effect'] == 'Allow':
principals = statement['Principal']
if isinstance(principals.get('Service'), list):
for service in principals['Service']:
self.graph.add_edge(service, f'role:{role_name}',
access_type='assume_role')
def find_attack_paths(self, target_resource):
"""Find all paths from public resources to target"""
paths = []
public_nodes = [n for n in self.graph.nodes()
if n == 'Internet' or self.graph.in_degree(n) == 0]
for public_node in public_nodes:
try:
all_paths = nx.all_simple_paths(self.graph,
public_node,
target_resource,
cutoff=10)
paths.extend(list(all_paths))
except nx.NetworkXNoPath:
continue
return paths
def calculate_risk_score(self, path):
"""Calculate risk score for an attack path"""
base_score = 10 - len(path) # Shorter paths are higher risk
# Adjust based on path elements
for i in range(len(path) - 1):
edge_data = self.graph.get_edge_data(path[i], path[i+1])
if edge_data.get('access_type') == 'public_read':
base_score += 3
elif edge_data.get('access_type') == 'assume_role':
base_score += 2
return max(0, min(10, base_score))
Preventing Attack Paths
1. Defense in Depth
# Never rely on single security control
security_layers:
- network_isolation: "VPC + Security Groups"
- identity_controls: "IAM + MFA"
- encryption: "At rest + In transit"
- monitoring: "CloudTrail + GuardDuty"
- response: "Automated remediation"
2. Principle of Least Privilege
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-bucket/public/*",
"Condition": {
"StringEquals": {
"s3:ExistingObjectTag/public": "true"
}
}
}]
}
3. Regular Attack Path Analysis
# Weekly attack path audit
def weekly_security_audit():
finder = AttackPathFinder()
finder.build_resource_graph()
critical_resources = [
'rds:customer-database',
's3:customer-documents',
'secrets:production-keys'
]
for resource in critical_resources:
paths = finder.find_attack_paths(resource)
if paths:
alert_security_team(f"Found {len(paths)} attack paths to {resource}")
for path in paths[:5]: # Top 5 riskiest
risk = finder.calculate_risk_score(path)
print(f"Risk {risk}/10: {' → '.join(path)}")
4. Break Common Attack Chains
Credential Chains
- Rotate keys automatically
- Use temporary credentials
- Never commit credentials
Network Paths
- Use private subnets
- Implement micro-segmentation
- Deploy WAF rules
Permission Chains
- Avoid role chaining
- Use permission boundaries
- Implement SCPs
Attack Path Remediation Playbook
Immediate Actions (Do Today)
# 1. Find and fix public resources
aws s3api list-buckets | jq -r '.Buckets[].Name' | \
parallel -j 10 aws s3api put-public-access-block \
--bucket {} \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
# 2. Enable GuardDuty for runtime detection
aws guardduty create-detector --enable
# 3. Review cross-account roles
aws iam list-roles \
--query "Roles[?contains(AssumeRolePolicyDocument.Statement[].Principal.AWS, ':root')].RoleName" \
--output table
Short-term Fixes (This Week)
- Implement IMDSv2 everywhere
aws ec2 modify-instance-metadata-options \
--instance-id i-1234567890abcdef0 \
--http-tokens required \
--http-endpoint enabled
- Add alerting for suspicious paths
# CloudWatch pattern for role chaining
{
($.eventName = AssumeRole) &&
($.requestParameters.roleSessionName = *-AssumedRole)
}
Long-term Strategy (This Quarter)
- Deploy continuous attack path monitoring
- Implement automated remediation
- Regular purple team exercises
- Security architecture reviews
The Future of Attack Path Security
AI-Powered Path Discovery
Machine learning models that predict novel attack paths based on:
- Historical breach patterns
- Your specific architecture
- Emerging threat intelligence
Automated Remediation
# Future: Self-healing infrastructure
def auto_remediate_attack_path(path):
risk_score = calculate_risk(path)
if risk_score > 8:
for i in range(len(path) - 1):
edge = (path[i], path[i+1])
safe_remediations = get_safe_remediations(edge)
apply_remediation(safe_remediations[0])
verify_business_continuity()
Conclusion
Attack paths are how real breaches happen. While individual misconfigurations might seem minor, attackers excel at chaining them together. The good news? Once you can see these paths, they’re usually simple to break.
Start by understanding that security isn’t about perfection—it’s about making exploitation paths longer and more complex than attackers are willing to traverse. Focus on breaking the most common chains, and you’ll prevent 95% of real-world attacks.
Your next steps:
- Map your critical resources (customer data, credentials, admin access)
- Identify paths from the internet to these resources
- Break the easiest/shortest paths first
- Implement continuous monitoring for new paths
Remember: Attackers think in graphs, not lists. Your defense should too.
Want to visualize attack paths in your AWS environment? Tools like PathShield automatically map these hidden connections and show you exactly where to break the chains before attackers find them.