滥用jQuery导致CSS时序攻击

阅读量    82064 |   稿费 110

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

vector illustrations of busy concepts, running out of time.

 

亚瑟·萨夫尼斯(Arthur Saftnes)去年发表了一篇非常棒的文章,介绍了他在利用CSS选择器和Javascript进行时序攻击方面的研究成果。说实话,它可能是我去年最喜欢的一篇文章。

注:如果你是第一次接触这类攻击,为方便理解本文,请先阅读上面这篇博文。

网站通常会使用下面这个方法将location.hash传递给jQuery $ function

$(location.hash);

攻击者有时可以控制hash值,并通常用来造成XSS,但jQuery在许多年已修复该漏洞。亚瑟发现该模块理论上仍可以被利用,从而导致时序攻击。你可以通过重复调用jQuery :has selector,然后注意目标页面中的内容的变化,判定对性能的影响。通过这点,攻击者可以把不可能造成XSS的情况转化为解析任意输入的端点。

于是我决定跟进这个研究,想要在外部网络中利用此技术找出一些漏洞。首先我把Burp Scaner修改为动态分析,以寻找hashchange事件中正在执行的jQuery选择器,然后扫描了一堆网站。我选择hashchange这个事件的原因是该攻击的局限性;为判定对性能的影响你需要重复更改hash值,导入所有可能的字符进行二进制搜索,而这只有hashchange事件触发时才能运行。原先那篇博文的一个局限是:目前主流浏览器都会对站点hash进行URL-encode处理,所以你先要解码它——但我找到了解决该问题的方法。

我通过hashchange事件发现了一些使用location.hashjquery $ fuction的Bug赏金项目站点,但找到的大部分站点没有令我感兴趣的数据,我没怎么窃取它们。Red Hat站点例外,它在hashchange事件中使用了jQuery选择器,并且站点启用了账户功能。查看Red Hat站点,找不到可以窃取数据的端点,但你会发现当你登入后确实会显示你的全名。亚瑟在攻击时使用了CSS属性选择器但全名不会出现在任何输入的元素中,因此我不能套用他的方法。

我查看了所有jQuery CSS选择器,发现选择器:contains可以找出包含指定字符的元素。不幸的是使用:contains看不到字符串的开头和末尾,所有我只能找别的方法提取。起初我想利用空格作为锚点来提取出姓名,但问题是Firefox会对空格进行URL编码。幸运的是,反斜杠不会进行URL-encode,所以我可以尝试CSS hex转义。刚开始我使用的是20,后来发现后续的字符也要进行hex转义,否则将破坏选择器,但如果我填充进字符0可以解决这个问题。我对亚瑟的利用代码稍加修改,添加了make_selector函数来解决空格的问题:

function make_selector(prefix, characters, firstNameFlag, firstName) {
 return characters.split("").map(c => !firstNameFlag ? SLOW_SELECTOR +
 SELECTOR_TEMPLATE.replace('{}',  c + prefix + '\000020') : SLOW_SELECTOR +
 SELECTOR_TEMPLATE.replace("{}",  prefix.replace(/ /, '\000020') + c))
                    .join(",");
}

这段代码通过hex编码了空格,帮助扫描出网页背后的名称。我使用了firstNameFlag,以区分出姓名和名字,找到姓名的大写字母后会设置标志,然后使用第一个名称作为前缀和空格继续扫描第二个字母,依次扫描出完整名称。

if(!firstNameFlag && /[A-Z]/.test(name)) {
   firstNameFlag = true;
   name += ' ';
   backtracks = 0;
   continue;
}

我遇到了另一个问题,由于URL-encode的问题,我无法在选择器里使用空格,而且使用hex转义也不能解决问题。我想了很久,构建出下面这个没有空格仍可以判定对性能的影响的选择器:

const SLOW_SELECTOR="*:has(*:has(*):parent:has(*):parent:has(*):parent:has(*):parent:has(*)):parent:has(";
const SELECTOR_TEMPLATE=".account-user:contains('{}'))";

这仍会导致性能问题,但比使用带有空格的CSS descendant选择器要快一些。我面临的下一个问题是如何确定已经递归到名字的末尾。正如我前面提到的那样,我不能看到字符尾端。目前我想到的唯一方法是连续寻找6个回溯。

目前这个漏洞已经完全修复,所以我分享出我的原始Poc:

Firefox access.redhat.com jQuery selector PoC

我录制了一段Video,你可以通过视频详细地查看时序攻击的详情。

Here is The Video.

分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多