Google XSS漏洞挖掘与分析教学文档
漏洞概述
本教学文档详细分析了一个通过Acunetix漏洞扫描器发现的Google XSS漏洞,该漏洞最终获得了$5000的漏洞赏金。漏洞存在于Google的某些服务端点中,允许攻击者通过构造特殊的URL执行跨站脚本攻击(XSS)。
漏洞发现过程
1. 使用Acunetix扫描Google服务
研究人员使用Acunetix Vulnerability Scanner对Google的多个服务进行扫描时,发现了一个XSS漏洞警告。扫描器提供的测试Payload为:
https://google.ws/ajax/pi/fbfr?wvstest=javascript:domxssExecutionSink(1,%22%27%5C%22%3E%3Cxsstag%3E()locxss%22)
2. 初步验证
虽然大多数扫描器报告的XSS漏洞都是误报,但由于目标为Google服务,研究人员决定深入分析。
漏洞分析
HTTP响应分析
漏洞端点返回的HTTP响应内容如下:
HTTP/1.1 200 OK
...
<!doctype html><div style="display:none">
<form method="post">
</form>
<script nonce="+ao+4Egc+7YExl3qyyWMJg==">(function(){var a=window.document.forms[0],b=location.hash.substr(1);b||window.close();var c=b.split("&"),d=decodeURIComponent(c[0]);a.action=d;for(var e=1;e<c.length;e++){var f=c[e].split("="),g=document.createElement("input");g.type="hidden";g.name=f[0];g.value=decodeURIComponent(f[1]);a.appendChild(g)}a.submit();}).call(this);</script>
</div>
JavaScript代码解析
整理后的JavaScript代码:
(function() {
var a = window.document.forms[0],
b = location.hash.substr(1);
b || window.close();
var c = b.split("&"),
d = decodeURIComponent(c[0]);
a.action = d;
for (var e = 1; e < c.length; e++) {
var f = c[e].split("="),
g = document.createElement("input");
g.type = "hidden";
g.name = f[0];
g.value = decodeURIComponent(f[1]);
a.appendChild(g)
}
a.submit();
}).call(this);
代码功能分析:
- 获取页面中的第一个表单元素(
window.document.forms[0]) - 获取URL中
#后面的部分(location.hash.substr(1)) - 如果没有hash部分,则关闭窗口(
b || window.close()) - 将hash部分按
&分割成数组 - 解码第一个数组元素作为表单的action属性
- 将剩余的参数作为隐藏input添加到表单中
- 自动提交表单
漏洞原理
漏洞存在于对location.hash的处理方式:
- 代码直接从URL的hash部分(
#之后的内容)获取数据 - 将hash部分第一个
&之前的内容解码后直接设置为表单的action属性 - 没有对hash内容进行任何验证或过滤
- 攻击者可以构造javascript:伪协议作为action值
漏洞利用
构造Payload
有效的XSS攻击Payload:
https://google.ws/ajax/pi/fbfr#javascript:alert(document.cookie)
这个URL会被解析为:
location.hash= "javascript:alert(document.cookie)"- 解码后直接设置为表单的action属性
- 表单提交时执行JavaScript代码
影响范围
该漏洞不仅存在于google.ws域名,还影响其他Google服务:
https://google.com/ajax/pi/fbfr#javascript:alert(document.cookie)
漏洞修复方案
修复代码
只需在设置action属性前添加一行验证代码:
0 != d.indexOf("http") && window.close();
完整修复后的代码:
(function() {
var a = window.document.forms[0],
b = location.hash.substr(1);
b || window.close();
var c = b.split("&"),
d = decodeURIComponent(c[0]);
// 新增的验证代码
0 != d.indexOf("http") && window.close();
a.action = d;
for (var e = 1; e < c.length; e++) {
var f = c[e].split("="),
g = document.createElement("input");
g.type = "hidden";
g.name = f[0];
g.value = decodeURIComponent(f[1]);
a.appendChild(g)
}
a.submit();
}).call(this);
修复原理
新增的代码检查hash解码后的第一个部分是否以"http"开头,如果不是则关闭窗口,防止javascript:伪协议的执行。
教学要点
-
URL hash的安全风险:开发人员常忽略URL hash部分的安全风险,认为它不会发送到服务器,但客户端JavaScript可以读取并处理它。
-
自动表单提交的危险:自动处理用户输入并提交表单的设计容易引入安全漏洞。
-
输入验证的重要性:所有用户可控的输入,包括URL各部分,都必须进行严格的验证。
-
漏洞扫描器的使用:即使是自动化工具报告的漏洞,对高价值目标也值得手动验证。
-
防御性编程:在处理用户输入时,应采用白名单而非黑名单的验证策略。
实践建议
- 在开发涉及URL处理的JavaScript代码时,始终验证所有URL部分
- 避免直接将用户输入设置为DOM属性值
- 使用Content Security Policy (CSP)限制脚本执行
- 对表单action属性进行严格验证,确保只允许预期的URL格式
- 定期使用自动化工具扫描应用,并对报告的问题进行手动验证
总结
这个案例展示了即使是Google这样拥有顶级安全团队的公司,也可能因为对URL hash处理不当而引入XSS漏洞。关键在于开发人员需要对所有用户可控的输入保持警惕,实施严格的输入验证,特别是当这些输入被用于动态修改DOM或执行脚本时。