0x01 以往劫持方法的局限性

在之前的一篇博文《一道绕过CSP的XSS题目》中提到了一种绕过CSP的方法,就是利用JS的特性,通过劫持指定id的标签来实现注入的JS代码能执行。但是这种方法的局限性是十分明显的——即进行劫持注入的地方必须在被劫持的标签的前面,因为浏览器只会从上至下地解析标签,当页面中存在多个相同id的标签的时,浏览器只会解析第一个标签,如果我们注入的位置在后面,那么将无法成功劫持到该标签。

看之前的图就知道了:

我们想劫持的是id为yourname的标签,而我们能够注入的点是id为forminput的标签,可以看到能够注入的标签是在被劫持的标签的前面,所以我们能够成功劫持到id为yourname的标签。

0x02 HTML标签注入

如果情况变为了,我们可以注入的点在想被劫持的标签后面,那么我们通过之前的办法还能不能劫持成功?

我们看个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$nonce = md5(openssl_random_pseudo_bytes(16));
header("Content-Security-Policy: script-src 'nonce-$nonce' 'unsafe−eval'; ");
?>
<!DOCTYPE html>
<html>
<head><title>CSP Test</title></head>
<body>
<div id="template_target">Mi1k7ea's blog: </div>
<script type="application/template" id="template">https://www.mi1k7ea.com/</script>

Your search is <?php if(isset($_GET['q'])){echo $_GET['q'];} else {echo 'empty, but you can input a param called q.';} ?>

<script nonce=<?php echo $nonce;?>>
let template = document.getElementById('template');
template_target.innerHTML = template.innerText.replace(/{{(.*)}}/g,eval)
</script>
</body>
</html>

代码很简单,这里模拟的是模板文件的形式,CSP策略设置为script-src 'nonce-xxx' 'unsafe−eval';,正常注入XSS payload是执行不成功的,唯一的可利用点是script标签中的eval、它会执行成功正则匹配到的两对大括号里面的内容,但限定了其执行的位置是id为template的标签。

显而易见,我们希望能够劫持的是id为template的标签,因为该标签中的内容会被正则匹配到之后调用eval执行JS代码,但是问题也很明显我们输入的注入点就在id为template的script标签的下面,并不能成功劫持到该标签,我们这里可以试试看,我们注入id为template的div标签,其中内容为符合正则匹配的JS弹框代码:

1
?q=<div id="template">{{ alert('html inject hack') }}</div>

标签是注入了,id也是template,但是是处于id为template的script标签下面,可以看到并没有触发弹框,因为浏览器只解析了排在前面的第一个id为template的标签。

那么如何去实现劫持呢?——通过注入HTML标签实现劫持

我们换个标签,将div换成html标签:

1
?q=<html id="template">{{ alert('html inject hack') }}</html>

Bingo,成功弹框,我们看下页面元素看下为啥能够成功劫持:

可以看到,我们注入的id为template的HTML标签,被浏览器解析到当前页面DOM文档的顶部去了,整个页面的HTML标签中的内容都属于id为template的标签内容内,因此其中符合正则匹配的内容都会被匹配到并成功执行。

通过注入HTML标签的方式,我们就可以由在需劫持的标签下面的位置上升为全局的劫持的位置,从而让我们在全局的层面实现了劫持!

当然,这段HTML标签注入的payload,我在本地的Chrome、IE和360浏览器这些是能够触发成功的,但Firefox不能弹框,然而查看这些浏览器的页面元素都是一样的,至于触发与否应该就是不同浏览器不同机制的原因了。除此之外,这种劫持方法的利用场景在模板文件中会更容易出现。

0x03 参考

PagedOut_001_beta1.pdf 第62页