Arthur Saftnes去年做了一些关于使用jQuery CSS选择器进行计时攻击的非常棒的研究,事实上它可能是去年我最喜欢的博客文章。
这是网站将location.hash传递给jQuery $函数的常见设计模式:
$(location.hash);
哈希可能是攻击者控制的,这曾经导致XSS,但jQuery修补了许多年前。亚瑟发现这种模式在理论上仍然可以利用定时攻击来利用。您可以重复调用jQuery :具有选择器并测量性能影响以从目标页面推断内容。这将这些情况从不可利用的XSS转变为读取几乎任何输入值。
我决定跟进这项研究,以使用这种技术找到真实的漏洞。我首先修改了Burp的动态分析,以寻找在hashchange
事件中执行的jQuery选择器,并扫描了一堆网站。我正在寻找hashchange
事件的原因是攻击的局限性; 为了衡量您需要重复更改哈希以对所有可能的字符进行二进制搜索所需的性能影响,这只能在hashchange
事件触发时进行。发布的原始技术的另一个限制是,您需要网站对散列进行URL解码,因为大多数现代浏览器现在对其进行URL编码 - 但我找到了解决此问题的方法。
我发现了一些在事件中使用location.hash
了jQuery $
函数的bug赏金网站hashchange
,但发现的大多数网站并没有真正有趣的数据需要窃取。但是有一个例外,Red Hat在hashchange
事件中使用jQuery选择器并具有帐户功能。查看该网站,它没有任何输入来窃取数据,但它确实在登录时显示您的全名。Arthur最初的攻击使用了CSS属性选择器,但是全名不在任何输入元素中,因此我无法使用它们。
我浏览了所有jQuery CSS选择器,发现:contains selector,它找到包含指定字符串的元素。不幸的是:contains不允许你查看字符串的开头或结尾,所以我需要另一种方法来提取值。我想过使用空格作为锚点来提取名字,但问题是在Firefox上,空间将被URL编码。幸运的是,反斜杠不是URL编码所以我可以使用CSS十六进制转义。起初我尝试\20
但是这会破坏选择器,因为下一个字符将继续十六进制转义,但如果我用零填充转义,这将确保使用正确的CSS转义。我修改了Arthur的代码以改进make_selector函数以使用空格:
function make_selector(prefix, characters, firstNameFlag, firstName) { return characters.split("").map(c => !fir stNameFlag ? SLOW_SELECTOR + SELECTOR_TEMPLATE.replace('{}', c + prefix + '\\000020') : SLOW_SELECTOR + SELECTOR_TEMPLATE.replace("{}", prefix.replace(/ /, '\\000020') + c)) .join(","); }
上面的代码使用十六进制编码空间向后扫描名称。我使用firstNameFlag来判断它是第一个名字还是第二个名字,当找到第一个名称的大写字母设置标志然后它开始匹配第二个名称扫描向前但这次使用第一个名称作为前缀和空间。
if(!firstNameFlag && /[A-Z]/.test(name)) { firstNameFlag = true; name += ' '; backtracks = 0; continue; }
我遇到的另一个问题是你不能在实际的选择器中使用空格,因为它获得了URL编码,并且十六进制转义在这里不起作用。我花了很多时间尝试构建一个没有空格且仍然具有可衡量的性能影响的选择器。最后我想出了以下选择器:
const SLOW_SELECTOR="*:has(*:has(*):parent:has(*):parent:has(*):parent:has(*):parent:has(*)):parent:has("; const SELECTOR_TEMPLATE=".account-user:contains('{}'))";
这会导致性能影响,但比使用CSS后代选择器的空间要慢得多。然后我的下一个问题是如何确定你已到达名称的末尾。就像我之前所说:包含选择器无法查看字符串的结尾。所以我想出的唯一方法是连续寻找6个回溯。
该漏洞现已修复,但我将在下面分享原始PoC,以便您可以看到我使用的代码:
Firefox access.redhat.com jQuery选择器PoC
原文由:https://portswigger.net/blog/abusing-jquery-for-css-powered-timing-attacks