一个S3账户ID枚举与存储桶发现工具
给定一个可访问的S3存储桶,bucky会提取其所属的12位AWS账户ID,然后通过模糊测试存储桶名称(使用一个单词列表)来发现同一账户下的其他存储桶——所有这些都通过一条命令完成。
s3:ResourceAccountIAM条件键,通过内联STS会话策略逐位暴力破解12位AWS账户IDS3存储桶名称原本共享一个单一的全局命名空间,这导致了桶抢注(bucketsquatting)——攻击者可以抢占他们预期组织会创建的桶名。AWS通过推出账户-区域命名空间来解决此问题,这是一种将每个存储桶与其所属账户和区域绑定的命名方案:
{name}-{accountID}-{region}-an
一个名为myapp-123456789123-eu-north-1-an的桶将位于https://myapp-123456789123-eu-north-1-an.s3.eu-north-1.amazonaws.com。AWS目前推荐新桶使用这种格式,尽管它还不是默认格式。(关于新命名空间如何消灭桶抢注的背景,请参阅One Cloud Please。)
但代价是账户ID和区域现在直接嵌入到了桶名之中。一旦攻击者获得目标的账户ID,他们可以将其与单词列表配对,系统地构造有效的存储桶URL——S3会确认每个桶是否存在,即使桶是私有的。仅凭这一响应就足以进行有用的侦察。
Bucky正是利用了这一点。借鉴自Pwned Labs的研究,它首先使用s3-account-search技术从任意可访问的桶中恢复12位账户ID,然后暴力破解{name}-{accountID}-{region}-an模式以揭示目标更广泛的S3足迹。
s3:ListBucket或s3:GetObject权限sts:AssumeRole,传递一个内联会话策略,其中包含一个类似"1*"的s3:ResourceAccount条件1开头,则HeadBucket调用成功;否则返回403这最多需要120次API调用(12个位置×10个数字),通常在一分钟内完成。
go installgo install github.com/umair9747/bucky@latest
git clone https://github.com/umair9747/bucky.git
cd bucky
go build -o bucky .
sudo mv bucky /usr/local/bin/
bucky --update
这将从仓库拉取并安装最新版本。
在使用bucky之前,你需要在自己的AWS账户中创建一个能访问目标S3存储桶的IAM角色。目标桶必须公开可访问,或者具有允许从你的账户访问的桶策略。
创建trust-policy.json——这将允许你的IAM用户承担该角色:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<YOUR_ACCOUNT_ID>:root"
},
"Action": "sts:AssumeRole"
}
]
}
将
<YOUR_ACCOUNT_ID>替换为你的12位AWS账户ID。你可以将主体范围缩小到特定的IAM用户或角色ARN,而不是:root。
创建s3-policy.json——该角色需要广泛的S3读取权限(bucky的内联会话策略中的s3:ResourceAccount条件处理账户级过滤):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetObject"
],
"Resource": "*"
}
]
}
aws iam create-role \
--role-name bucky-role \
--assume-role-policy-document file://trust-policy.json
aws iam put-role-policy \
--role-name bucky-role \
--policy-name S3ReadAccess \
--policy-document file://s3-policy.json
aws iam get-role --role-name bucky-role --query 'Role.Arn' --output text
# Output: arn:aws:iam::123456789012:role/bucky-role
如果你的IAM用户尚未拥有sts:AssumeRole权限,请附加以下策略:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::<YOUR_ACCOUNT_ID>:role/bucky-role"
}
]
}
使用bucky的主要方式——从已知桶枚举账户ID,然后一步到位模糊测试更多桶:
bucky \
--role-arn arn:aws:iam::123456789012:role/bucky-role \
--bucket target-bucket \
--wordlist wordlists/default.txt \
--access-key AKIA... \
--secret-key wJal...
使用已知对象键(提高可靠性):
bucky \
--role-arn arn:aws:iam::123456789012:role/bucky-role \
--bucket s3://target-bucket/index.html \
--wordlist wordlists/default.txt \
--workers 20
多个桶(逗号分隔):
bucky \
--role-arn arn:aws:iam::123456789012:role/bucky-role \
--bucket bucket1,bucket2,bucket3 \
--wordlist wordlists/default.txt
从文件中读取多个桶:
bucky \
--role-arn arn:aws:iam::123456789012:role/bucky-role \
--bucket-file targets.txt \
--wordlist wordlists/default.txt
将结果保存为JSON:
bucky --bucket target-bucket --wordlist wordlists/default.txt --json
bucky --bucket target-bucket --wordlist wordlists/default.txt --json --output results.json
Output:
=== Phase 1: Account ID Enumeration ===
[*] Verifying access to bucket: target-bucket
[+] Access confirmed
[+] Bucket region: us-west-2
[*] Enumerating account ID...
[██████████████████████████████] 12/12 | 675351422352
[+] Account ID: 675351422352
=== Phase 2: Bucket Discovery ===
[*] Fuzzing buckets for account: 675351422352
[*] Loaded 312 candidates from wordlist
[+] FOUND: target-backups
[+] FOUND: target-logs
[██████████████████████████████] 312/312 | 2 found
=== Summary ===
Account ID : 675351422352
Source : target-bucket
Region : us-west-2
Discovered : 2 additional bucket(s)
- target-backups
- target-logs
如果你只需要账户ID:
bucky enum \
--role-arn arn:aws:iam::123456789012:role/bucky-role \
--bucket target-bucket
多个桶和JSON输出同样适用于子命令:
bucky enum --bucket bucket1,bucket2 --json
bucky enum --bucket-file targets.txt --json --output results.json
如果你已经有一个账户ID并希望发现桶:
bucky fuzz \
--role-arn arn:aws:iam::123456789012:role/bucky-role \
--account-id 675351422352 \
--wordlist wordlists/default.txt \
--workers 20
所有标志都可以通过环境变量设置。当两者都设置时,标志优先。
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=wJal...
export BUCKY_ROLE_ARN=arn:aws:iam::123456789012:role/bucky-role
# Now just specify the target
bucky --bucket target-bucket --wordlist wordlists/default.txt
Bucky附带一个默认的单词列表,位于wordlists/default.txt,包含约300个常见的S3桶命名模式:
backups, logs, data, assets, uploads)cloudtrail-logs, terraform-state, lambda-artifacts)prod-logs, staging-data, dev-backups)api, web-assets, cdn-origin)ci-artifacts, , )对于定向任务,通过在开头添加组织名称来生成自定义单词列表:
ORG="acme"
while read -r line; do
[[ "$line" =~ ^#.*$ || -z "$line" ]] && continue
echo "${ORG}-${line}"
echo "${line}-${ORG}"
echo "${ORG}${line}"
done < wordlists/default.txt > wordlists/acme.txt
任何与ffuf或gobuster等工具兼容的单词列表都可以使用。格式:每行一个桶名,#注释,空行忽略。
| 命令 | 描述 |
|---|---|
bucky | 完整工作流 — 枚举账户ID然后模糊测试更多桶 |
bucky enum | 从已知桶中枚举12位账户ID |
bucky fuzz | 针对已知账户ID模糊测试桶名 |
全局标志(所有命令):
bucky(完整工作流):
| 标志 | 描述 |
|---|---|
--bucket | 目标S3存储桶,逗号分隔 |
bucky enum:
| 标志 | 描述 |
|---|---|
--bucket | 目标S3存储桶,逗号分隔 |
--bucket-file | 包含桶名/URL的文件,每行一个 |
bucky fuzz:
| 标志 | 描述 |
|---|---|
--account-id | 目标12位AWS账户ID |
--bucket标志和--bucket-file接受多种输入格式。Bucky会自动从中提取桶名(以及可选的键):
| 格式 | 示例 |
|---|---|
| 纯名称 | my-bucket |
| S3 URI | s3://my-bucket/path/to/key |
| 虚拟主机式URL |
多个桶可以通过--bucket以逗号分隔传递:
--bucket bucket1,s3://bucket2/key,https://bucket3.s3.amazonaws.com/
或者通过--bucket-file在文件中每行列出一个:
my-bucket
s3://another-bucket/index.html
https://third.s3.us-east-1.amazonaws.com/
当多个桶解析到同一个账户ID时,bucky会去重,每个唯一账户仅运行一次模糊测试。
使用--json将结果保存为JSON文件。默认文件名是当前目录下的bucky-{uuid}.json,或者使用--output指定路径:
bucky --bucket target-bucket --wordlist wordlists/default.txt --json
bucky --bucket target-bucket --wordlist wordlists/default.txt --json --output results.json
输出格式:
{
"timestamp": "2024-01-15T10:30:00Z",
"results": [
{
"bucket": "target-bucket",
"account_id": "675351422352",
"region": "ap-south-1",
"discovered_buckets": [
"dev-675351422352-ap-south-1-an.s3.ap-south-1.amazonaws.com",
"prod-675351422352-ap-south-1-an.s3.ap-south-1.amazonaws.com"
]
}
]
}
Bucky会自动检测目标桶的区域。当S3返回301重定向(桶位于与--region不同的区域)时,bucky会通过x-amz-bucket-regionHTTP响应头解析正确的区域,并将其缓存用于所有后续请求。无需为目标桶手动配置区域。
| 变量 | 标志 | 描述 |
|---|
AWS_ACCESS_KEY_ID | --access-key | AWS访问密钥ID |
AWS_SECRET_ACCESS_KEY | --secret-key | AWS秘密访问密钥 |
AWS_SESSION_TOKEN | --session-token | AWS会话令牌(用于临时凭证) |
AWS_REGION | --region | AWS区域(默认:us-east-1) |
AWS_DEFAULT_REGION | --region | 如果未设置AWS_REGION时的回退值 |
AWS_PROFILE | --profile | 来自~/.aws/credentials的AWS命名的配置文件 |
BUCKY_ROLE_ARN | --role-arn | 要扮演的IAM角色的ARN |
BUCKY_BUCKET | --bucket | 目标S3存储桶名称 |
BUCKY_ACCOUNT_ID | --account-id | 目标AWS账户ID(仅fuzz) |
BUCKY_WORDLIST | --wordlist | 单词列表文件路径 |
BUCKY_WORKERS | --workers | 并发模糊测试工作者数(默认:10) |
build-outputdocker-images| 标志 | 描述 |
|---|
--access-key | AWS访问密钥ID |
--secret-key | AWS秘密访问密钥 |
--session-token | AWS会话令牌 |
--region | AWS区域(默认:us-east-1) |
--profile | AWS命名的配置文件 |
--role-arn | 要扮演的IAM角色的ARN |
--json | 将结果保存为JSON文件 |
--output | JSON输出文件路径(默认:bucky-{uuid}.json) |
--bucket-file| 包含桶名/URL的文件,每行一个 |
--wordlist | 单词列表文件路径 |
--workers | 并发工作者数(默认:10) |
--wordlist| 单词列表文件路径 |
--workers | 并发工作者数(默认:10) |
--regions | 用于名称变异的逗号分隔的区域 |
https://my-bucket.s3.us-west-2.amazonaws.com/key| 路径式URL | https://s3.us-west-2.amazonaws.com/my-bucket/key |