发布:2020年9月23日,13:01 UTC
更新:2020年9月23日,14:51 UTC
我们最近更新了不可能的XSS实验室系列有新的挑战。对于这种情况,您的注射发生在单个引用的JavaScript字符串中,您只能使用CharsetA-ZA-Z0-9'+。
X='注射';
脚本>
目的是窃取cookie并将其发送到远程服务器。发布此挑战一个月后,我们收到了一个惊人而富有想象力的解决方案卢安·埃雷拉(Luan Herrera)。我们问他是否想在我们网站上写作作为客人帖子,他同意了。向你卢安。
我最初的方法是清楚地定义问题。了解挑战的限制,从那里开始努力解决方案。
限制
输入反映在单个引用的字符串中。
字符集仅限于A-ZA-Z0-9'+。
。
要点
给定注射环境,为了逃避引用的字符串,有必要使用'和 +字符。
由于 +运算符,逃脱单个引号之后使用的任何有效负载都将与其他两个字符串串联,然后分配给变量x-这意味着tostring()
总是会自动称为:X=''+注射+'';
可以通过标记的模板调用函数 - 这是通过使用反向字符来实现的,尽管有限制:
只能将一个参数传递给该函数。
只能将文字字符串作为参数传递,因为不允许$ {}字符,这阻止了变量的值传递到函数。
After frustratingly long hours diving deeper into Javascript’s native functions, trying to chain functions in a way that allowed me to pass arbitrary variables to Tagged templates, or even trying to find a clever way to make assignments, I wasn’t any the wiser and concluded this was indeed an impossible challenge =(
几周后,在阅读 /r /slackers subreddit的线程时,我注意到加雷斯的信息是:
如果我可以以其他方式泄露数据可能足够的方式,则可能并不是明确的。
考虑到新的方法,我决定重新审视挑战。在限制性charset中,还有什么其他含义可以渗透透明含量?我立即想到了侧通道攻击。
我考虑了是否只能使用允许的charset构建某种甲骨文,然后使用许多侧通道泄漏技术剥离所说的信息。
使用允许的Charset,我可以访问任何窗口属性的参考(document.cookie,dom cottents),并且通过字符串操作,我有能力一次检查一个字符。
// document.cookie =“秘密= 1337”;
文档。曲奇饼。charat`0`//“ S”
文档。曲奇饼。charcodeat`0`// 115
下一步是创建一些比较机制,如果“选定”字符等同于对其进行测试的“选定”字符,则行为会以一种方式做出响应,如果没有,则以不同的方式进行了反应。必须通过一种侧道泄漏技术来检测到这种行为的差异。
string.prototype.split()
结合string.prototype.repeat()
原来是类似甲骨文的行为的好候选人。
通过在将目标炭分开之前重复靶向炭,可以任意夸大匹配的char vs vs over之间的执行时间差。
In the example below, when the selected char matched the one being targeted, the execution time was much higher than when it didn’t.
让在里面=表现。现在();
“ S”。重复((40000000)。分裂((“ S”);//(40000001)[“”,“”,“”,“”,…]
安慰。日志((表现。现在()-在里面);// 1098.244999999224
让在里面=表现。现在();
“ S”。重复((40000000)。分裂((“一个”);// [“ sssss ... SSSSS”]
安慰。日志((表现。现在()-在里面);// 31.080000000656582
例如,该页面完全加载和触发iframe的加载事件所花费的时间也反映了这个更高的执行时间,例如,自然而然地,可以利用此时间差来从页面上删除信息。
有一个警告:由于字符量有限,并非所有字符都可以明确传递给拆分功能 - 这需要对炭的代表方式进行一些调整。
例如,“>” char不能直接作为参数传递到拆分函数,因为它不是给定字符集的有效字符。要解决此问题,每个字符的char代码都转换为字符串。
这使我能够比较允许的Charset中仅使用字符中存储在窗口属性中的任何字符。我选择十六进制作为基础,因为所有ASCII字符都可以表示为两个字节,范围从00到ff。
// document.cookie =“秘密= 1337”;
文档。曲奇饼。charcodeat`0`// 115
。to`16`//“ 73”
。charat`0`//“ 7”
文档。曲奇饼。charcodeat`0`// 115
。to`16`//“ 73”
。charat`1`//“ 3”
基于计时的概念证明测试挑战的所有字符document.cookie
逐个;每个字符都转换为十六进制字符串,其字节通过测试是否匹配0到F进行单独解决。
尽管漏洞利用有效,但我觉得有空间可以提高其效率。已知基于时间的侧渠道渗透易受到网络延迟的影响。另外,由于有必要在符合字符匹配时足够长时间悬挂页面(出于检测目的),因此利用最终会很慢。
我重新审视了甲骨文,并试图重写利用以删除剥落的基于时间的方面。一个意识到,而不是分裂()
和重复()
,我可以使用string.prototype.match()
要匹配被测试的字符与目标一一匹配。
诀窍是,当匹配匹配时,返回了包含匹配相关内容的数组,否则将其返回null。第二个飞跃是认识到数组可以访问tostring()
方法和无效。
// document.cookie =“秘密= 1337”;
文档。曲奇饼。charcodeat`0`//返回115
。to`16`//将115转换为“ 73”,十六进制基础
。匹配`73`//返回匹配[“ 73”,索引:0,输入:“ 73”,组:未定义]
。to````//评估较早的匹配项为“ 73”
// document.cookie =“秘密= 1337”;
文档。曲奇饼。charcodeat`0`//返回115
。to`16`//将115转换为“ 73”,十六进制基础
。匹配`74`//返回null
。to````// vm528:2未熟悉的typeError:无法阅读<匿名>:2:54的null的属性'tostring'
如证明,调用object.prototype.tostring()
要么要:
如果字符匹配,请返回对自身测试的字符。
如果该字符不匹配,则会引发异常,因为该方法不存在。
这种行为差异使我能够对窗口属性的任何内容进行布尔查询,如下所示:
“是document.cookie [0]
等于“ S”?”
如果是,则表达式返回“ S”本身。
如果没有,脚本通过尝试使用会触发异常tostring()
在零。
只有剥落的问题。使用较早的Oracle,可以任意夸大成功查询的执行时间并用performance.now()
。不再是这种情况。
我最初的发现想法是,如果炭是匹配的,tostring()
如果字符不匹配,会发生异常,并且会发生重定向tostring()
在零上会导致错误和位置。将永远无法达到分配,因此没有重定向。
X=''+'。匹配`s`。to````+地点。分配`1`+'';
由于我将挑战的页面加载到iframe内部,因此我认为我能够检测到重定向是否通过收听iframe的负载事件并计算触发的次数(已知的次数XS-Leak技术)。
假设[如果有匹配]:
加载事件触发挑战页面加载到iframe中时。
发生匹配,脚本不会崩溃,位置。将重定向重定向到 /1。
当重定向页面完全加载时,第二次触发加载事件。
通过收听IFRAME的负载事件来检测这两个负载。
同样的话,如果脚本抛出tostring()
,,,,位置。分配
永远不会到达,重定向到 /1也不会发生。在这种情况下,负载事件最终仅一次触发一次。
[…]
不幸的是,这还不是发生的事情。我没有考虑到页面满载时才触发加载事件。
相反,发生了两件事之一:
如果有匹配,脚本没有崩溃,重定向在页面满载之前发生。当重定向页面满载时,触发了加载事件。
如果没有比赛,脚本崩溃了,挑战的页面完成了加载,并触发了加载事件。
在这两种情况下,负载事件仅触发一次,使我无法将其用作侧渠道以泄漏Oracle的结果。
一种晦涩的窗口方法是停止()
“在当前浏览上下文中停止进一步的资源加载,等同于浏览器中的停止按钮”。
如果有可能防止页面充分加载,则不会触发加载事件;这就是发生的事情!
因为在同一脚本块中的任何脚本停止()
被称为仍将执行,证明可以将其纳入注射的Oracle。
X=''+停止````+`s`。匹配`s`。to````+地点。分配`1`+'';
脚本>
与较早的情况不同,无论是否有比赛,都会触发加载事件一次停止()
相反,发生的事情是,它阻止了进一步的资源超过当前脚本块 - 阻止iFrame的加载事件触发。
这很有帮助,因为:
如果没有匹配:脚本崩溃了,重定向从未发生。
停止()
防止页面加载,从未触发该iframe的加载事件。
脚本没有崩溃,重定向仍然发生(因为它是在同一脚本块中触发的)。
加载事件在重定向页面的负载完成后触发。
通过这种情况,我们可以更可靠地推断信息,又可以泄漏,这些信息都是任何给定窗口属性中的所有字符(例如cookie.cookie,dom contents等)。
该技术可以通过以下几点总结:
我们可以通过访问给定窗口属性中的所有字符依次测试所有字符charcodeat(i)
;
当“选定”字符与来自窗口属性的char匹配时,就会发生重定向location.assign()
将被称为 - 由于此,将触发负载事件;
当“选定”字符不匹配窗口属性的字符时,不会触发负载事件停止()
被调用并防止页面加载;
我们可以通过检测是否或何时触发交叉原始事件来观察行为差异;
这就是工作(更有效)利用的概念!剩下的就是用此处讨论的技术实施概念证明,并为每个角色自动化相同的过程。
您可以找到决赛基于负载的概念证明在这里。
当不在浏览器中进行安全研究时,您可能会发现Luan Herrera狩猎虫子以谋生为生。他喜欢旅行和在业余时间玩CTF。他还是一个狂热的XS-Leaks爱好者。