打破状态机:Web竞态条件的真正潜力

58 阅读5分钟

打破状态机:Web竞态条件的真正潜力

长期以来,Web竞态条件攻击仅局限于少数场景。由于复杂的工作流程、工具缺失以及网络抖动等因素,其真正潜力一直被掩盖,只有最明显简单的案例才能被发现。

本文中,我将介绍远超传统限制溢出漏洞的新型竞态条件类别,并利用这些技术成功入侵多个知名网站以及Rails流行认证框架Devise。同时还将介绍单包攻击技术——一种能够规避网络抖动的策略,可实现从墨尔本到都柏林的30个请求在1毫秒内同时执行。

背景知识:竞态条件基础

大多数网站使用多线程处理并发请求,所有线程都从同一个共享数据库读写数据。应用程序代码很少考虑并发风险,因此竞态条件问题在Web领域普遍存在。

典型的利用方式是限制溢出攻击——通过同步请求突破某种限制,例如:

  • 多次兑换礼品卡
  • 重复使用单个折扣码
  • 多次评价同一产品
  • 超额提取或转账
  • 重复使用验证码
  • 绕过暴力破解防护

这些问题都源于安全检查与被保护操作之间存在时间差。例如两个线程可能同时查询数据库确认"TOP10"折扣码尚未使用,然后都尝试应用该折扣,导致重复使用。

超越限制溢出漏洞

通过深入研究,我发现竞态条件的真正潜力可以用一句话概括:渗透测试人员都知道多步骤流程是漏洞温床,但在竞态条件下,所有操作都是多步骤的。

为说明这点,让我们分析一个我偶然发现的严重漏洞。当用户登录时,会看到"角色选择"页面,其中包含分配角色并重定向到特定应用的按钮。请求流程如下:

POST /login → 302 Found
GET /role → 200 Found 
POST /role → 302 Found
GET /application → 200 OK

我最初错误地假设GET /role请求不会改变应用状态。实际上,应用会为每个会话初始化管理员权限,然后在浏览器获取角色选择页面时覆盖这些权限。通过跳过/role直接访问应用,任何人都能获得超级管理员权限。

单包攻击技术

子状态是应用在处理单个请求时经历的短暂隐藏状态,通常仅持续约1毫秒。要发现子状态,需要一个初始HTTP请求触发状态转换,以及第二个在时间窗口内与相同资源交互的请求。

传统方法受网络抖动影响极大。为此我开发了"单包攻击"技术,使用该技术可以让20-30个请求无视网络抖动同时到达服务器。在Turbo Intruder中实现后,从墨尔本到都柏林发送20个请求的测试显示,单包攻击比传统最后字节同步技术效率高4-10倍。

技术实现要点:

  1. 预发送请求主体:对于无正文请求发送所有头部但不设置END_STREAM标志;对于有正文请求发送除最后一字节外的所有数据
  2. 准备发送最终帧:等待100ms确保初始帧已发送,禁用TCP_NODELAY以利用Nagle算法
  3. 发送保留帧:验证它们是否在单个数据包中

方法论

发现可利用子状态的系统方法:

  1. 预测潜在冲突:识别具有安全控制的对象及关联端点
  2. 探测线索:发送大量请求寻找异常行为
  3. 验证概念:将线索转化为可行攻击

案例研究

Gitlab对象掩蔽漏洞

通过竞态条件,可以创建低权限邀请函,当被撤销时会被管理员级别邀请函替换。

多端点碰撞

在Gitlab的邮件验证流程中,通过同时验证邮件地址和修改地址,可使系统错误标记错误地址为已验证状态。

单端点碰撞

在Gitlab修改邮件地址功能中,同时向两个不同地址发送修改请求,导致确认邮件被发送到错误地址但包含有效令牌。

延迟碰撞

某些关键数据处理会定期批量执行,此时不需要精确的时间控制即可触发竞态条件。

防御建议

  1. 避免混合不同来源的数据
  2. 使用数据存储的并发功能确保敏感端点状态变更的原子性
  3. 利用数据存储的完整性/一致性特性
  4. 不要用一个数据存储层保护另一个
  5. 确保会话处理框架保持内部一致性

关键结论

  • HTTP请求处理不是原子性的,任何端点都可能使应用经历不可见的子状态
  • 单包攻击解决了网络抖动问题,使攻击如同在本地系统进行
  • 发现异常是找到竞态条件的最重要技能

[完整技术细节和案例分析请参阅原文]