导航

[【T00ls公告】] T00ls 邮箱换绑逻辑存在缺陷,存在 CSRF/XSS等漏洞 联动账号接管风险(已修复)

#1

现站点个人资料的邮箱修改功能逻辑存在安全缺陷: 当前换绑流程支持直接输入新邮箱,不需要先向原绑定邮箱发送验证码校验原邮箱所有权,仅给新邮箱发验证码完成绑定。 风险分析:

  1. 如果页面存在存储型 / 反射型 XSS 漏洞,攻击者拿到用户有效会话 Cookie 后,可以直接调用该换绑接口,将账号邮箱修改为攻击者可控邮箱;

  2. 若接口缺少 CSRF Token 校验,可构造 CSRF POC,诱导登录状态下的用户访问恶意页面,自动提交请求修改绑定邮箱;

  3. 邮箱是账号找回、密码重置的核心凭证,一旦邮箱被恶意替换,攻击者可直接接管账号,原用户直接失去账号控制权。

建议修复方案: 正常安全流程应当增加前置校验:修改邮箱前,必须先给原绑定邮箱发送验证码,验证原邮箱控制权,校验通过之后,才允许填写新邮箱、给新邮箱发验证邮件;同时接口强制校验 CSRF 令牌,加固接口防跨站请求伪造能力。 @t00ls管理团队01

匿名
3 小时前
#2
@t00ls管理团队01
3 小时前
#3

结论

这个帖子的核心风险逻辑成立,但标题把几个层次混在了一起:

  1. 主漏洞是邮箱换绑缺少“原邮箱所有权/当前身份”再验证。
  2. CSRF、XSS只是可能的触发方式,帖子没有给出对应请求或代码,不能据此认定目标当前确实存在 CSRF/XSS。
  3. 最终是否能够账号接管,取决于密码找回流程是否信任修改后的邮箱。如果支持通过新邮箱重置密码,攻击链就能闭合。

更准确的描述应是:

邮箱换绑流程仅验证新邮箱,未验证原邮箱或当前身份;攻击者在取得受害者已认证上下文后,可将恢复邮箱替换为攻击者邮箱,并可能通过密码重置接管账号。CSRF/XSS 是获取该已认证上下文的潜在途径。

核心逻辑缺陷

正常的身份迁移至少应证明两件事:

攻击者拥有新邮箱
+
攻击者仍有权代表当前账号发起身份变更

当前流程只验证第一件事。攻击者本来就能控制自己的邮箱,因此“新邮箱验证码”对攻击者几乎没有约束力。

实际状态转换可能是:

仅持有登录态 + 新邮箱验证码
        ↓
修改账号绑定邮箱
        ↓
使用新邮箱触发密码重置
        ↓
获得重置 Token/链接
        ↓
修改密码并接管账号

如果邮箱只是联系方式,不参与登录或密码找回,影响会明显降低;如果邮箱是密码重置、账号申诉等流程的主要恢复因子,则通常属于高风险业务逻辑漏洞。

三种利用路径

1. 直接盗用会话

攻击者拿到有效 Session Cookie 后,可以直接重放换绑接口。此路径不需要 XSS,也不需要 CSRF,XSS 和 CSRF 只是获取或利用受害者会话的其他方式。

需要确认:

  • 换绑接口是否只依赖 Session Cookie;
  • 是否还要求当前密码、TOTP、WebAuthn 等二次认证;
  • 新邮箱验证码是否绑定原会话、申请事务或设备;
  • 换绑后是否立即生效。

2. 利用目标站点的 XSS

帖子中“XSS 拿到 Cookie 后调用接口”的描述不够准确。即使 Cookie 设置了 HttpOnly,XSS 仍然可以利用浏览器自动携带 Cookie 发起同源请求:

(async () => {
  const token =
    document.querySelector('meta[name="csrf-token"]')?.content ||
    document.querySelector('input[name="_csrf"]')?.value;

  const headers = {'Content-Type': 'application/json'};
  if (token) headers['X-CSRF-Token'] = token;

  await fetch('/api/account/email/code', {
    method: 'POST',
    credentials: 'include',
    headers,
    body: JSON.stringify({
      email: 'attacker@example.com'
    })
  });
})();

收到新邮箱验证码后继续提交:

await fetch('/api/account/email/change', {
  method: 'POST',
  credentials: 'include',
  headers: {
    'Content-Type': 'application/json',
    'X-CSRF-Token': token
  },
  body: JSON.stringify({
    email: 'attacker@example.com',
    code: '123456'
  })
});

上述路径和字段名只是骨架,实际应以抓包结果为准。其关键点是:

  • XSS 与目标同源,SameSite 不会阻止同源请求;
  • HttpOnly 只阻止 JavaScript读取 Cookie,不阻止浏览器发送 Cookie;
  • 如果 CSRF Token 位于表单、Meta 标签或可读取的接口响应中,XSS 可以一并获取;
  • 即使有 CSRF Token,XSS 通常仍能以受害者身份完成操作;
  • CORS 只限制跨源读取响应,不会阻止浏览器实际发送简单请求。

因此,对 XSS 来说,真正需要确认的是目标站是否存在可利用的存储型或反射型 XSS,而不是 Cookie 是否能被 document.cookie 读出。

3. 纯 CSRF

纯 CSRF 不需要读取目标接口响应,也不要求 XSS。概念 POC 可以是:

<form id="f" method="POST"
      action="https://TARGET/account/email/code">
  <input type="hidden" name="email" value="attacker@example.com">
</form>
<script>
  document.getElementById('f').submit();
</script>

攻击者收到验证码后,再让受害者浏览器提交:

<form id="f" method="POST"
      action="https://TARGET/account/email/change">
  <input type="hidden" name="email" value="attacker@example.com">
  <input type="hidden" name="code" value="RECEIVED_CODE">
</form>
<script>
  document.getElementById('f').submit();
</script>

但“接口没有 CSRF Token”并不等于一定可以纯 CSRF,还要验证:

  • Session Cookie 的 SameSite 是 Lax、Strict 还是 None;
  • 接口使用 GET、POST,还是 PUT/PATCH/JSON;
  • 是否校验 Origin、Referer、Sec-Fetch-Site;
  • 是否要求自定义请求头或不可预测的表单 Token;
  • 是否接受 application/x-www-form-urlencoded、multipart/form-data 或 text/plain;
  • 是否存在 CORS 预检;
  • 验证码是否绑定发起请求时的受害者会话或事务 ID。

现代浏览器中,SameSite=Lax/Strict 通常会阻止跨站 POST 自动携带 Cookie,但跨站顶层 GET 仍可能携带;而 SameSite=None、旧浏览器、旧登录框架或特殊认证方式又可能改变结果。因此 CSRF 必须在真实浏览器里验证,不能只靠 Burp 手动附加 Cookie。

另外,如果接口只接受 application/json,普通 HTML 表单不能直接构造该 Content-Type,这可能使纯 CSRF 失败,但不会阻止同源 XSS。

账号接管链的关键确认点

最有价值的验证顺序是:

  1. 登录测试账号,在浏览器抓取“发送新邮箱验证码”和“确认换绑”两个请求。
  2. 不进行原邮箱验证码步骤,直接给攻击者邮箱获取验证码并提交换绑。
  3. 确认换绑是否立即生效。
  4. 使用攻击者邮箱触发密码重置。
  5. 验证是否能收到重置邮件或验证码并完成密码修改。
  6. 分别移除 CSRF Token、修改 Origin/Referer,测试纯 CSRF。
  7. 在目标站 XSS 环境中用 fetch(..., credentials:'include') 重放相同请求。
  8. 检查验证码是否绑定会话、邮箱、申请事务、IP、设备及有效期。

只有第 2~5 步形成闭环,才能确认“联动账号接管”,而不是单独的“邮箱可被修改”。

与公开案例的关联

T00ls 公开的 DEDECMS v5.7(2013-06-07)member/flink_main.php xss+csrf 0day 案例体现了一个典型组合:攻击者可以在源站上下文中直接触发原本依赖用户身份的状态变更操作,XSS 会使单纯的 CSRF Token 防护失去意义。mooSocial 3.1.8 Reflected XSS(CVE-2023-4173)则可作为反射型 XSS 的公开参考。

不过,这些案例只能说明“XSS+CSRF/状态变更接口”是一种常见攻击组合,不能替代对 T00ls 当前换绑接口的独立复现。

最终判断:帖子的核心攻击链在“换绑不验证原邮箱或当前身份”且“密码重置信任新邮箱”的前提下成立,属于可导致账号接管的业务逻辑缺陷;XSS 和 CSRF 应分别作为待验证的触发向量,而不能仅凭当前帖子直接宣称已经确认存在这两个漏洞。

2 小时前
#4
请证明漏洞存在
sbbbb.cn 网络安全导航
2 小时前
#5

回复 4# 0xShe

为什么你的名字会发光?
没个性,不签名
1 小时前
#6

回复 5# apibug

https://www.t00ls.com/memberpage.html?goto=memberstyle

用户名配色功能。

T00.ls:https://t00.ls/t00ls
T00ls官方Telegram公开频道:https://t.me/T00lsCom
T00ls官方Telegram群:https://t.me/T00lsTB
T00ls官方Twitter:https://twitter.com/T00lsCom
半小时前
#7

回复 4# 0xShe

在账号个人中心 https://www.t00ls.com/memcp.html?action=profile&amp;typeid=6 直接可以输入新的邮箱发送验证码,不需要校验旧的邮箱验证获取验证码。 正常的校验流程应该是:验证老邮箱 -> 验证新邮箱 -> 修改成功 当前的校验流程是:验证新邮箱 -> 修改成功

半小时前
#8
1、为什么不需要验证原邮箱?是因为太多修改邮箱的原因是原邮箱丢失,根本访问不了原邮箱,从而造成管理员的配合压力。
2、是否存在漏洞?理论上而言得有XSS情况下,通过XSS获取页面formhash才行,只是存在理论上的可能性。单纯凭借CSRF是做不到的。
3、修复方法:增加了密码认证,修改邮箱必须验证密码,与修改密码必须认证原密码一样,从根本上杜绝任何修改邮箱的可能性。

上述修复方法已经应用,楼主可以继续检测是否存在问题。
T00.ls:https://t00.ls/t00ls
T00ls官方Telegram公开频道:https://t.me/T00lsCom
T00ls官方Telegram群:https://t.me/T00lsTB
T00ls官方Twitter:https://twitter.com/T00lsCom
24 分钟前
#9
安全性和便捷性还是要一个平衡的