利用HTML注入劫持标签Bypass CSP
/0x01 以往劫持方法的局限性
在之前的一篇博文《一道绕过CSP的XSS题目》中提到了一种绕过CSP的方法,就是利用JS的特性,通过劫持指定id的标签来实现注入的JS代码能执行。但是这种方法的局限性是十分明显的——即进行劫持注入的地方必须在被劫持的标签的前面,因为浏览器只会从上至下地解析标签,当页面中存在多个相同id的标签的时,浏览器只会解析第一个标签,如果我们注入的位置在后面,那么将无法成功劫持到该标签。
看之前的图就知道了:
我们想劫持的是id为yourname的标签,而我们能够注入的点是id为forminput的标签,可以看到能够注入的标签是在被劫持的标签的前面,所以我们能够成功劫持到id为yourname的标签。
0x02 HTML标签注入
如果情况变为了,我们可以注入的点在想被劫持的标签后面,那么我们通过之前的办法还能不能劫持成功?
我们看个例子:
1 | <?php |
代码很简单,这里模拟的是模板文件的形式,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不能弹框,然而查看这些浏览器的页面元素都是一样的,至于触发与否应该就是不同浏览器不同机制的原因了。除此之外,这种劫持方法的利用场景在模板文件中会更容易出现。