Javascript中变量声明带来的Tags Broken

问题产生

问题代码如下

1
2
3
4
5
6
7
8
<html>

<script>
var example = 'Consider this string: <!-- <script>';
console.log(example);
</script>
<img src=/>
<html>

GW8xXt.png

看一下会造成的影响

GWGI3j.png

很明显可见的是img标签以及下面的html全部被吃掉了

原因探讨以及不同场景下的fuzz

原因

官方文档给出的解释如下:

由于遗留原因,HTML元素中的“ <!--”和“ <script”字符串script需要进行平衡,以便解析器考虑关闭该块

也就是说在<!--的作用下<script>是要去和下面的</script>闭合的,而<!--的作用仍然是充当注释符作用,虽然<script></script>进行了闭合,但是因为注释符的原因,里面的内容并不会被解析

注释状态下的其他语句解析

通过测试发现这种注释语句还会带来标签内语句无法解析的效果

测试代码如下:

1
2
3
4
5
6
7
8
9
<html>

<script>
var a=alert(1);
var example = 'Consider this string: <!-- <script>';
console.log(example);
</script>
<img src=/>
<html>

虽然最后alert(1)也在script标签内 ,但是无法执行

G4XMqK.png

不同场景下的fuzz

标签的fuzz

这里主要是不同标签能否产生类似script这样的闭合进行的fuzz,结果发现,似乎只有具备script这样特性的标签才可以产生这样的效果。

并且必须要<!–与<script>进行结合搭配才可以产生这样的效果,在只有<!–和<script>的情况下都无法产生预期的结果

这里对为什么必须要二者结合在一起才能产生这样的效果进行了一个猜想:

在如下代码情况下:

1
2
3
4
5
6
7
8
9
<html>

<script>
var example = 'Consider this string: <!--';
console.log(example);
// alert(1);
</script>
<img src=/>
<html>

<!--并未起到注释的效果可能是因为被变量的字符串作用域所限制,使得他只能是一个普通的字符串,而在如下的代码情况下:

1
2
3
4
5
6
7
8
9
<html>

<script>
var example = 'Consider this string: <!--<script>';
console.log(example);
// alert(1);
</script>
<img src=/>
<html>

由于<script></script>的闭合导致后面的单引号失去了原本的作用,而<!–也发挥了他本身的作用,所以最终导致的后果就是,红框里的内容全部当作了注释

GWWXCQ.png

需要注意的是:在这种情况下<script>里面的代码是完全无效的

探讨:

在测试完标签后发现了同样比较有趣的一点是如果我们直接在变量中声明</script>,测试代码与效果如下:

1
2
3
4
5
6
7
8
9
<html>
<body>
<script>
var example = 'Consider this string: </script>';
console.log(example);
</script>
<img src=/>
</body>
<html>

GW4KhD.png

可以发现如果在变量中直接声明</script>的话会直接和前面的<script>进了闭合,把单引号以及之后的内容都扔到了后面,产生了直接使后面代码无效化的影响

字符串场景下的利用

漏洞利用场景如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<body>
<script>

var payload='<?php echo $_GET['xss']; ?>';

</script>
<script>
setTimeout(() => {
location="http://www.google.com";

}, 1000);
</script>
</body>
<html>

对于这个页面的效果是在1000ms后自动跳转到谷歌,不过我们可以变量的设置来bypass这个跳转

实现如下:
Gf3bcR.png

非字符串场景下的利用

非字符串下变量声明中<!–产生的问题

如果对一个变量直接用如下方式赋值(在控制台)

1
var a=<!--

将一直处于输入状态

对于这个情况,可以前面一些字符或者数字进行避免

1
var a=1<!--

最终a的值为1

1
2
3
4
5
6
7
8
9
<html>

<script>
var a=1<!--;
var b=2
console.log(a)
</script>
<img src=/>
<html>

以上js可正常执行

应用场景探讨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<body>
<script>

var payload=<?php echo $_GET['xss']; ?>;

</script>
<script>
setTimeout(() => {
location="http://www.baidu.com"

}, 1000);
</script>
</body>
<html>

利用方式其实差不多

  • 在没有分号的情况的下可以用:xss=<!--<script或者<!--<script>
  • 在有分号限制的情况下只有:xss=<!--<script>
进一步探讨

在另一种结构下: 如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<body>
<script>
setTimeout(function(){
try{
return location = 'http://127.0.0.1';
test=<?php echo $_GET['xss']; ?>;
}catch(err){
return location = '/?a='+<?php echo $_GET['xss']; ?>;
}
},1000);
</script>
<script>
//normal part
</script>
</body>
<html>

我们需要做的是不影响下面的正常js语句且能做到xss

而且特别需要注意的是在try中的代码时return location而非location,区别是

  • return条件下:return完了之后的语句并不会执行

  • 非return条件下:test和location都会被赋值操作

也就是说非return条件下其实是无难度的xss。

这里我们采用的是<!--<script去闭合而非<!--<script>,后者产生的注释直接会将后面的所有全部注释掉,无法再满足正常的js结构,并且此注释状态不可取消掉。

前者只会把script标签内的部分注释掉(因为没有闭合),并且可以通过特定语句来取消掉注释状态,所以我们现在可以传参a=alert(1);<!--script,这样会符合正常的语句,但会直接location掉,所以我们再重新声明下location,再给一个报错就可以了

最终payload

TIM截图20200409160838.png