六个零攻破外卖帝国:关键2FA绕过漏洞分析

本文详细分析了一个外卖平台的关键2FA绕过漏洞,攻击者仅需输入六个零即可绕过双重验证,同时平台还存在数据过度暴露、缺乏速率限制等严重安全问题,CVSS评分高达9.1分。

当六个零攻破外卖帝国 | Jackson Mittag

这个故事讲述了一个本不该存在的关键2FA绕过漏洞

该漏洞已被负责任地披露并修复。所有敏感信息均已编辑,以保护平台及其用户。

你知道那种测试某样东西时心想"这不可能成功"的感觉吗?

然后它真的成功了。

这正是我发现本地区最大外卖平台之一仅用相当于让前门大开的方式保护数千用户账户时的情景。

事情如何开始

那是一个普通的周二下午。我正在做我最擅长的事情——检查认证系统,寻找弱点。该平台(我们称之为[编辑])已实施双重认证,这本应是个好迹象。

2FA应该是那层额外的安全保护。即使有人获取了你的密码,没有发送到你手机的六位数代码,他们也无法访问你的账户。

“本应"是这里的关键词。

我启动Burp Suite并拦截登录请求。流程起初看起来正常:

步骤1: 用户输入电话号码 步骤2: 系统通过短信发送2FA代码 步骤3: 用户输入代码 步骤4: 如果正确→进入

标准流程。没什么特别的,对吧?

错了。

“这不可能成功"的时刻

这就是事情变得有趣的地方。

当我仅用电话号码发送初始登录请求时,系统响应HTTP 423 Locked和消息:“需要2FA”。好的开始。系统知道2FA存在。

但接下来我尝试的事情,纯粹出于好奇:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
POST /login HTTP/2
Host: [编辑]
Content-Type: application/json

{
  "v2": true,
  "phone": "[编辑]",
  "country_code": "+XX",
  "code": "000000"
}

我没有请求代码。没有拦截短信。我只是…猜了000000。

系统让我进入了。

我坐在那里盯着屏幕,一个有效的JWT令牌像忏悔一样回盯着我。

不。可能。

深入兔子洞

自然地,我必须看看这有多深。如果000000有效,还有什么会有效?

111111 ✅ 有效 123456 ✅ 有效 999999 ✅ 有效

我尝试的每一个可预测代码都被接受了。没有速率限制。没有失败尝试跟踪。除了"这是六位数吗?“之外没有验证。

这不仅仅是一个错误。这是对2FA应该做什么的根本误解。

但等等——还有更多。

信息泄露的金矿

认证后,我访问了/user端点,看看系统会返回关于我账户的什么信息。

我得到的是…一切。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "id": [编辑],
  "name": "[编辑]",
  "email": "[编辑]",
  "phone": "[编辑]",
  "password": "$2a$10$[BCRYPT_HASH_编辑]",
  "document": "[编辑]",
  "sms": "271066",
  "zoop_buyer_id": null,
  "failed_attempts": 0,
  "delinquent": 0
}

让我分析为什么这是灾难性的:

  • BCrypt密码哈希暴露——绝不应发送到客户端
  • 活跃的2FA代码可见——系统字面告诉了我"正确"代码
  • 完整PII泄露——姓名、电子邮件、电话、证件号码
  • 没有数据最小化——为什么前端需要这些?

此时,我不仅仅在看2FA绕过。我在看一个完整的认证和授权失败。

实际影响

让我们谈谈这在现实世界中的意义。

这不是某个只有三家公司使用的晦涩SaaS工具。这是一个拥有数千活跃用户的外卖平台。人们订购餐食。人们存储支付方式。人们在系统中有送货地址。

攻击者可以:

✅ 仅凭电话号码访问任何用户账户 ✅ 查看订单历史和送货地址 ✅ 访问存储的支付信息 ✅ 下欺诈订单 ✅ 收集PII用于钓鱼活动 ✅ 抓取整个用户数据库

CVSS评分:9.1(关键)

这不是理论上的。这是可武器化的。

为什么会发生(以及为什么重要)

当我深入研究这个漏洞时,我意识到这不仅仅是一个错误——这是一连串的安全失败:

失败#1:信任用户输入

系统接受任何六位数代码而不进行验证。不与实际短信代码比较。只是"看起来像代码?进来吧!”

失败#2:没有速率限制

你可以无限暴力破解认证尝试。系统没有"太多失败尝试"的概念。

失败#3:安全剧场

2FA作为复选框功能实施,而不是实际安全。它给用户一种虚假的安全感。

失败#4:数据过度暴露

API返回绝不应离开服务器的敏感数据。BCrypt哈希、活跃2FA代码、完整PII——都不必要地暴露。

失败#5:长期令牌

JWT令牌过期时间极短,扩大了受损窗口。

修复(应该做什么)

如果你是正在阅读此文的开发者,以下是实际实施2FA的方法:

1. 验证真实代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
const validate2FACode = async (userCode, phoneNumber) => {
  // 获取通过短信发送的实际代码
  const storedCode = await getStoredCode(phoneNumber);
    
  // 拒绝可预测模式
  const weakCodes = ['000000', '111111', '123456', '999999'];
  if (weakCodes.includes(userCode)) {
    return false;
  }
    
  // 检查过期(代码应在5-10分钟内过期)
  if (isExpired(storedCode)) {
    return false;
  }
    
  // 时序安全比较
  return crypto.timingSafeEqual(
    Buffer.from(userCode),
    Buffer.from(storedCode.code)
  );
};

2. 实施速率限制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const MAX_ATTEMPTS = 5;
const LOCKOUT_DURATION = 15 * 60 * 1000; // 15分钟

const checkRateLimit = async (identifier) => {
  const attempts = await getFailedAttempts(identifier);
    
  if (attempts >= MAX_ATTEMPTS) {
    const lockoutTime = await getLockoutTime(identifier);
    if (Date.now() - lockoutTime < LOCKOUT_DURATION) {
      throw new Error('尝试次数过多。请在15分钟后重试。');
    }
  }
};

3. 最小化数据暴露

绝不返回:

  • 密码哈希(即使是BCrypt)
  • 活跃2FA代码
  • 不需要的内部ID
  • 不必要的PII

只返回前端实际需要运行的内容。

4. 实施适当的令牌管理

  • 短过期时间(15-30分钟)
  • 刷新令牌轮换
  • 安全令牌存储
  • 安全事件时的会话失效

战壕中的教训

对于安全研究人员

不要停留在明显的地方。有时最大的漏洞并不隐藏在复杂的利用链后面——它们就在那里,伪装成功能。

测试你的假设。我几乎没尝试000000,因为我假设"肯定没有生产系统会接受这个”。永远不要假设。

查看相邻系统。一个漏洞通常会导致其他漏洞。2FA绕过让我发现了信息泄露问题。

对于开发者

安全功能不是复选框。实施2FA不会自动让你安全。你必须正确实施它。

深度防御很重要。如果2FA被正确实施,信息泄露可能不会那么关键。如果数据暴露被最小化,2FA绕过可能不会那么严重。层次很重要。

速率限制不是可选的。如果你的认证端点没有速率限制,你就没有认证——你有一个公共API。

绝不暴露密码哈希。我不在乎它们是BCrypt、Argon2,还是用独角兽眼泪哈希的。它们不属于API响应中。

对于每个人

2FA只与其实现一样强大。不要因为平台启用了2FA,就假设你的账户自动安全。安全在于细节。

应有的赞誉

这个漏洞最初由jphck(Discord)发现,他值得认可发现并负责任地披露了这个关键问题。

安全社区通过协作和知识共享而繁荣。无论你是发现漏洞、编写教育内容,还是只是学习——我们都在让互联网变得更安全一点,一次一个错误。

最后思考

回顾这个漏洞,最让我震惊的不是技术复杂性(没有任何)——而是提醒我们安全是关于基础的。

你可以有最先进的入侵检测系统、最复杂的WAF、最昂贵的安全工具。但如果你的2FA接受000000作为有效代码,这些都不重要。

最危险的漏洞并不总是最复杂的。有时它们是隐藏在众目睽睽之下的简单疏忽,等待有人尝试"不应该有效"的明显事情。

直到它有效。

参考资料和进一步阅读

漏洞分类:

  • CWE-307:不当限制过多的认证尝试
  • CWE-200:向未经授权的参与者暴露敏感信息
  • OWASP ASVS:V2(认证验证)

标准:

  • NIST SP 800-63B:数字身份指南
  • OWASP认证备忘单

记住: 本文用于教育目的。在测试任何系统之前,始终获得适当授权。负责任的披露保护每个人。

原始发现: jphck(Discord) 状态: ✅ 已修复并验证 日期: 2025年11月

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计