目录
代码
<div>
<h4>Meme Code</h4>
<textarea class="form-control" id="meme-code" rows="4"></textarea>
<div id="notify"></div>
</div>
<script>
/* Utils */
const escape = (dirty) => unescape(dirty).replace(/[<>'"=]/g, '');
const memeTemplate = (img, text) => {
return (`<style>@import url('https://fonts.googleapis.com/css?family=Oswald:700&display=swap');`+
`.meme-card{margin:0 auto;width:300px}.meme-card>img{width:300px}`+
`.meme-card>h1{text-align:center;color:#fff;background:black;margin-top:-5px;`+
`position:relative;font-family:Oswald,sans-serif;font-weight:700}</style>`+
`<div class="meme-card"><img src="${img}"><h1>${text}</h1></div>`)
}
const memeGen = (that, notify) => {
if (text && img) {
template = memeTemplate(img, text)
if (notify) {
html = (`<div class="alert alert-warning" role="alert"><b>Meme</b> created from ${DOMPurify.sanitize(text)}</div>`)
}
setTimeout(_ => {
$('#status').remove()
notify ? ($('#notify').html(html)) : ''
$('#meme-code').text(template)
}, 1000)
}
}
</script>
<script>
/* Main */
let notify = false;
let text = new URL(location).searchParams.get('text')
let img = new URL(location).searchParams.get('img')
if (text && img) {
document.write(
`<div class="alert alert-primary" role="alert" id="status">`+
`<img class="circle" src="${escape(img)}" onload="memeGen(this, notify)">`+
`Creating meme... (${DOMPurify.sanitize(text)})</div>`
)
} else {
$('#meme-code').text(memeTemplate('https://i.imgur.com/PdbDexI.jpg', 'When you get that WW3 draft letter'))
}
</script>
代码分析
经过分析大部分的输入输出都被过滤函数过滤了例如下图
‘
过滤了以后再寻找新的注入点,看一下这里,漏洞点就在这里
payload构造
对于Jquery.html()
和innerHTML是有区别的,来个例子来解释一下
<style><style/><script>alert(1337)//
输入上面一样的东西对于innerHTML来说,他会补全标签,且style中不允许有标签,如果有标签的话里面的内容会被当作文本解析,里面的内容无法执行,所以就会出现下面那个样子
<style>
<style/><script>alert(1337)//
</style>
对于Jquery.html的话对于标签的处理是先调用htmlPrefilter() 这个函数,然后再用innerHTML
调用htmlPrefilter() 这个函数会将你的输入进行一个匹配,所匹配的代码就是<div/>这个样子,然后他会对你标签进行补全,补完以后就成这样了<div></div>
上面的例子就成了这样,其中script就逃逸出来了,然后中间的两个标签是替换后的标签,然后最上面的标签是innerHTML整出来的标签,然后第一个和第三个标签闭合了
(注:这个技巧对于Jquery.html版本是有要求的)
<style>
<style>
</style>
<script>alert(1337)//
然后回来看一下这道题,首先进行的是DOMPurify这个过滤然后再进行.html,这样是不是就可以使用上面的技巧去绕过
但是想要触发这个.html还需要notify为true,但是这里是false
这里我们就要想办法进行DOM破坏了
然后我们尝试使用<img name=notify>来绕过,为什么不使用<img id=notify>,因为id不允许覆盖已经存在的值,但是name可以覆盖
所以我们构造如下payload
<img name=notify><style><style/><script>alert()//
然后需要我们进入到刚刚分析的漏洞点,所以我们需要进入下图所示,但是只有当img图片加载成功才可以,所以我们需要再img传一个真值,且img标签中属性的执行是异步的所以在src加载的过程中,下面的text也会加载,然后才是onload加载,这样payload也加载到了memeGen里面
所以最终传参为
img=valid_img_url&text=<img name=notify><style><style/><script>alert()//
结果
这里图片源可以添加一个更容易访问的图片,如果很难访问的图片的话可能会因为访问超时而没有回应