剑客
关注科技互联网

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

原文链接: https://www.brokenbrowser.com/abusing-of-protocols/

原作者:Manuel Caballero

译:Holic (知道创宇404安全实验室)

在 10 月 25 日,研究员 @MSEdgeDev twitter 了一个 链接 ,成功引起了我的注意,因为我点击那个链接的时候(在 Chrome 上),Windows 应用商店会自动打开。这对你来说也许不足为奇,但它足以让我感到惊讶。

在我印象中,Chrome 有这样一个健康的习惯,在打开外部程序之前询问用户是否打开外部程序。但是这次的情况是它直接打开了相应程序,而且没有警告。

这次的差别反应引起了我的主页,因为我从来没有允许 Chrome 打开 Windows 商店。有一些插件和协议会自动打开,但我从来没有允许过 Windows 商店这一应用。

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

缩短的 Twitter 链接重定向至 https://aka.ms/extensions-storecollection ,然后再一次重定向到: ms-windows-store://collection/?CollectionId=edgeExtensions ,Interesting ~.

这一协议我不甚了解,因此我马上试着找到与该协议存在多处关联的地方:注册表。搜索 “ms-windows-store” 立即返回了我们在 PackageId 中的字符串,这似乎是 Windows 应用商店的程序。

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

注意我们也在一个名为 “Windows.Protocol” 的键之中,我稍微上下滚动了一些,以便看看有没有其他的应用程序在其中。然后我发现他们很多拥有自己的注册协议。这便是极好的,因为这直接从浏览器打开了一个新的攻击面。然而我们按 F3 看看我们是否找到其他的匹配项。

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

似乎 ms-windows-store:协议也接受搜索参数,所以我们可以试着直接从 Google Chrome 打开我们的自定义搜索。事实上,Windows 应用商店应用程序似乎使用了 Edge 的引擎渲染 HTML,这也是很有趣的地方,因为我们可能尝试进行 XSS 攻击,亦或是本地程序,发送一大堆数据然后看看会发生什么。

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

但是现在我们不会这么干,我们回到注册表上来,按下 F3 看看我们能找到什么。

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

这也是很有意思的,因为如果它们用字符串 “URL:”前缀的话,它会给我们快速找到更多的协议的线索。让我们将搜索重置为 “URL:”,看看我们得到什么。按下 [HOME] 键回到注册表的顶部,搜索 “URL:” ,将马上返回递归个匹配的 “URL: about:blank ”,以及顺便确认下我们还没疯掉。

再次按下 F3 ,我们找到了 bingnews: 协议,但是这次 Chrome 向我们确认了是否要打开它。没毛病,让我们在 Edge 上试试看会发生什么。它打开了!在注册表中下一个匹配的的是 calculator: 协议。这会生效吗?

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

Wow!exploit 的作者们肯定好气啊。它们将弹出什么程序呢?calc 和 notepad 可以打开,而且没有产生内存损坏。现在 cmd.exe 已经弃用,而是采用了 powershell。微软移除了你们这群人的乐趣 :stuck_out_tongue: 。

这便是枚举所有可能被加载的协议的时候了,先去看看哪些程序接受参数,那么我们可以尝试注入代码(二进制或者纯 Javascript,取决于应用程序的编码方式和他如何处理参数)。有很多有趣的玩法,如果我们继续寻找协议,我们将发现大量的能打开的程序(包括 Candy Crush,我还不知道我电脑上有这东西)。

通过按几次 F3 ,我受益匪浅。例如,有一个 microsoft-edge: 协议在新标签中加载 URL。这看起来似乎并不重要,直到我们记住 HTML 页面应有的限制。弹出窗口拦截器会阻止我们打开 20 个 microsoft-edge:http://www.google.com 标签吗?

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

[ PoC – 在微软 Edge 浏览器上弹窗 ]

那么 HTML5 沙箱又怎样呢?如果你不熟悉它,它只是一种使用 iframe 沙箱属性或者 http header 的沙箱属性对网页施加限制的方法。例如,如果我们想在 iframe 中渲染内容并且确保它不运行 javascript (甚至不打开新标签),我们只需要使用此标签:

<iframe src=”sandboxed.html” sandbox></iframe>

然后渲染的页面将被完全限制。它基本上只能渲染 HTML/CSS ,但是没有 javascript 或者其他访问接触到像 cookie 这样的东西。事实上,如果我们使用沙盒粒度,并且至少允许打开新窗口/标签,他们应该全都继承沙箱属性,以及从 iframe 点击链接打开的依然受沙盒限制。然而,使用 microsoft-edge 协议完全绕过了这一点。

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

[ PoC – 在 微软 Edge 浏览器上绕过 HTML5 沙箱 ]

很高兴看到 microsoft-edge 协议允许我们绕过不同的限制。我更深入研究,但你可以一试!这是发现之旅,纪念一条 tweet 激发了我研究的动力,而且最终给我们真正值得进行更多研究的材料。

我继续在注册表中按下 F3 键,发现了 read: 协议,它引起了我的注意力,因为当阅读它的 (javascript)源码时,它可能有潜在的 UXSS 漏洞,但是尝试的过程中 Edge 一次次地崩溃了。它崩溃太多次了。例如,将 iframe 的 location 设置为 “read:” 就足以使浏览器崩溃,包括所有选项卡。想看看吗?

[ PoC – Crash on MS Edge ]

OK,我很好奇发生了什么,所以我附加了几个字节到 read 协议,并启动了 WinDbg 看看崩溃是不是和无效数据有关。这些东西迅速且简单,没有 fuzzing 或任何特殊的东西: read:xncbmx,qwieiwqeiu;asjdiw!@#$%^&*

Oh yes,我真的打出来了这些东西。我发现的不会使 read 协议崩溃的唯一方法就是加载来自 http[s]的东西。其他的都会使浏览器崩溃。

那么让我们将 WinDbg 附加至 Edge 浏览器吧。有一个快速的脏方法,我使用它来简单地杀死 Edge 进程和子进程,重新打开它并附加到使用 EdgeHtml.dll 的最新进程。当然还有更简单的方法,但是…yeah,我就是这么做的。打开命令行,然后…

taskkill /f /t /im MicrosoftEdge.exe

** Open Edge and load the webpage but make sure it doesn't crash yet **

tasklist /m EdgeHtml.dll

足够了。现在加载 WinDbg ,并将其附加到使用 EdgeHtml 的最新列出的 Edge 进程。记住在 WinDbg 中使用的符合。

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

一旦附加上去,只需要按 F5 或者在 WinDbg 中按 g [回车],使 Edge 保持运行。这是我屏幕现在看起来的样子。左边有我用来测试一切的页面,在右边, WinDbg 附加到特定的 Edge 进程。

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

我们将使用 window.open 伴以 read: 协议继续玩耍,而不是一个 iframe ,因为它使用起来更舒服。仔细想想,有的协议/url 可能会最终改变顶部 location,无论它们如何使用框架。

如果我们开始在 iframe 中使用协议,有可能我们自己的页面(顶部)将被卸载,失去我们刚刚键入的代码。我特定的测试页面保存了我键入的内容,如果浏览器崩溃,它很可能被恢复。但即使一切都保存下来了,当我编写一些可以改变我测试页面的 URL 的代码时,我就在一个新窗口中打开它。这只是一种习惯罢了。

在左侧屏幕上,我们可以快速键入并执行 JavaScript 代码,右侧我们有 WinDbg 准备向我们解释在崩溃的背后发生了什么。继续,我们允许 JavaScript 代码以及… Bang! WinDbg 中断了连接。

ModLoad: ce960000 ce996000 C:/Windows/SYSTEM32/XmlLite.dll  
ModLoad: c4110000 c4161000 C:/Windows/System32/OneCoreCommonProxyStub.dll  
ModLoad: d6a20000 d6ab8000 C:/Windows/SYSTEM32/sxs.dll

(2c90.33f0): Security check failure or stack buffer overrun - code c0000409 (!!! second chance !!!)
EdgeContent!wil::details::ReportFailure+0x120:  
84347de0 cd29 int 29h

OK,看来 Edge 知道出了问题因为它位于一个叫做 “ReportFailure” 的函数中,对吧?得了,我知道我们马上可以猜测,如果 Edge 在此,它会有失“优雅”。所以我们检查 stack trace 来看看我们来自何方。在 WinDbg 中输入 “k” 键。

0:030> k  
# Child-SP RetAddr Call Site
00 af248b30 88087f80 EdgeContent!wil::details::ReportFailure+0x120  
01 af24a070 880659a5 EdgeContent!wil::details::ReportFailure_Hr+0x44  
02 af24a0d0 8810695c EdgeContent!wil::details::in1diag3::FailFast_Hr+0x29  
03 af24a120 88101bcb EdgeContent!CReadingModeViewerEdge::_LoadRMHTML+0x7c  
04 af24a170 880da669 EdgeContent!CReadingModeViewer::Load+0x6b  
05 af24a1b0 880da5ab EdgeContent!CBrowserTab::_ReadingModeViewerLoadViaPersistMoniker+0x85  
06 af24a200 880da882 EdgeContent!CBrowserTab::_ReadingModeViewerLoad+0x3f  
07 af24a240 880da278 EdgeContent!CBrowserTab::_ShowReadingModeViewer+0xb2  
08 af24a280 88079a9e EdgeContent!CBrowserTab::_EnterReadingMode+0x224  
09 af24a320 d9e4b1d9 EdgeContent!BrowserTelemetry::Instance::2::dynamic  
0a af24a3c0 8810053e shlwapi!IUnknown_Exec+0x79  
0b af24a440 880fee33 EdgeContent!CReadingModeController::_NavigateToUrl+0x52  
0c af24a4a0 88074f98 EdgeContent!CReadingModeController::Open+0x1d3  
0d af24a500 b07df508 EdgeContent!BrowserTelemetry::Instance'::2::dynamic  
0e af24a5d0 b0768c47 edgehtml!FireEvent_BeforeNavigate+0x118

看看前两行,都叫做 blah blah ReportFailure ,你不觉得 Edge 运行到这里是因为出现错误了吗?当然!让我们继续运行下去,直到我们找到一个有意义的函数名。下一个叫做 blah FallFast ,它也有一些 Edge 知道出错了才调用的味道。但是我们想找到使 Edge 不愉悦的代码,那么继续读下去吧。

下一个是 blah _loadRMHTML 。这个对我来说看起来好多了,你难道不也这么认为吗?事实上,他的名字让我觉得它是加载 HTML 的。在崩溃之前断下程序的话,这将会变得有意思多了,所以为什么不在 _LoadRMHTML 上面几行设置断点呢?我们检查了 stack-trace,现在我们来看看代码。

我们先从那个断点(函数+偏移)查看反汇编。这很简单,在 WinDbg 中使用 “ub” 命令。

0:030> ub EdgeContent!CReadingModeViewerEdge::_LoadRMHTML+0x7c  
EdgeContent!CReadingModeViewerEdge::_LoadRMHTML+0x5a:  
8810693a call qword ptr [EdgeContent!_imp_SHCreateStreamOnFileEx (882562a8)]  
88106940 test eax,eax  
88106942 jns EdgeContent!CReadingModeViewerEdge::_LoadRMHTML+0x7d (8810695d)  
88106944 mov rcx,qword ptr [rbp+18h]  
88106948 lea r8,[EdgeContent!`string (88261320)]  
8810694f mov r9d,eax  
88106952 mov edx,1Fh  
88106957 call EdgeContent!wil::details::in1diag3::FailFast_Hr (8806597c)

我们将只关注名字,忽略其他东西,好伐?就像我们 试着寻找 mimeType bug 的偏差 一样,我们在此投机取巧,当然如果我们失败了我们会继续深入。但有时在调试器上的快速查看可以阐明很多事情。

我们知道如果 Edge 到达这个片段的最后一条指令(地址为 88106957,FailFast_Hr),Edge 就会崩溃掉。我们的目标是弄清我们最终到达的地方,就是说谁TM把我带到那里的。上面的代码的第一条指令似乎是调用了一个复杂名称的函数,这显然大量体现了我们的东西。

EdgeContent!_imp_SHCreateStreamOnFileEx

在 ! 前的第一部分是该指令所在的模块(exe,dll等等…)。这种情况下是 EdgeContent 我们甚至不关心它的扩展,它只是一段代码。! 之后有个有趣的函数名叫 _imp_ ,然后 SHCreateStreamOnFileEx 似乎是一个“创建文件流”的函数名。你同意吗?事实上, _imp_ 的部分让我想起这可能是从不同的二进制文件加载的导入函数。让我 google 一下这个名字,看看能不能找到有趣的东西。

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

这太棒了。第一个结果正是我们搜索的准确名称。让我们点击一下。

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

好。此函数接收的第一个参数是 “ A pointer to a null-terminated string that specifies the file name ” 。因垂丝挺!如果这段代码正被执行,,那么它应该接收一个指向文件名的指针作为第一个参数。但是我们这么能看到第一个参数呢?很简单,我们在 Win x64上运行, 调用约定 / 参数解析 说,“前四个参数是 RCX, RDX, R8, R9 ”(说道整数/指针)。这意味着第一个参数(指向文件名的指针)将被装载入 RCX 寄存器。

有了这些信息,我们可以在 Edge 调用之前设置一个断点,看看 RCX 在那个确定时刻有何值。但是我们重新启动一遍程序吧,因为这时已经有点迟了:Edge 已经崩溃了。请重新按照上面描述的做一遍(杀掉 Edge 进程,打开它,加载页面,找到进程并附加上去)。

这个时候,不要运行(F5)进程,我们先设置一个断点。WinDbg 显示了我们执行 “ub” 命令时的确切偏移量。

0:030> ub EdgeContent!CReadingModeViewerEdge::_LoadRMHTML+0x7c  
EdgeContent!CReadingModeViewerEdge::_LoadRMHTML+0x5a:  
8810693a ff1568f91400 call qword ptr [EdgeContent!_imp_SHCreateStreamOnFileEx (882562a8)]  
88106940 85c0 test eax,eax

所以断点应该在 EdgeContent!CReadingModeViewerEdge :: _ LoadRMHTML 0x5a 处。我们键入 “bp” 和函数名 + 偏移[回车]。然后 “g” 让 Edge 运行。

0:029> bp EdgeContent!CReadingModeViewerEdge::_LoadRMHTML+0x5a  
0:029> g

这很一颗赛艇。在 SHCreateStreamOnFileEx 执行之前,我们想要看到 RCX 指向的文件名(或者字符串)。我们运行代码,稍适小憩。好吧,宝宝我感受到它了 =) 断点连至我的童年。让我们运行这段 JavaScript 代码吧,bang!WinDbg 在此中断。

Breakpoint 0 hit  
EdgeContent!CReadingModeViewerEdge::_LoadRMHTML+0x5a:  
8820693a ff1568f91400 call qword ptr [EdgeContent!_imp_SHCreateStreamOnFileEx (883562a8)]

这太棒了,现在我们可以检查 RCX 指向的内容。为此我们使用 “d” 命令(显示内存)@ 和寄存器名称,如下所示:

0:030> d @rcx  
02fac908 71 00 77 00 69 00 65 00-69 00 77 00 71 00 65 00 q.w.i.e.i.w.q.e.  
02fac918 69 00 75 00 3b 00 61 00-73 00 6a 00 64 00 69 00 i.u.;.a.s.j.d.i.  
02fac928 77 00 21 00 40 00 23 00-24 00 25 00 5e 00 26 00 w.!.@.#.$.%.^.&.  
02fac938 2a 00 00 00 00 00 08 00-60 9e f8 02 db 01 00 00 *.......`.......  
02fac948 10 a9 70 02 db 01 00 00-01 00 00 00 00 00 00 00 ..p.............  
02fac958 05 00 00 00 00 00 00 00-00 00 00 00 19 6c 01 00 .............l..  
02fac968 44 14 00 37 62 de 77 46-9d 68 27 f3 e0 92 00 00 D..7b.wF.h'.....  
02fac978 00 00 00 00 00 00 08 00-00 00 00 00 00 00 00 00 ................

这对我的眼睛很不好,但在第一行的右边,我看到了一些类似于 Unicode 字符串的东西。我们将它显示为Unicode字符吧(du)。

0:030> du @rcx  
02fac908 "qwieiwqeiu;asjdiw!@#$%^&*"

Nice!字符串将我包围!看看我们刚才运行的 JavaScript 代码。

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

看来,传给这个函数的参数是 逗号 后面输入的任何内容。有了这点知识加上知道它期望是一个文件,我们可以尝试一个在硬盘上的完整的路径。因为 Edge 在 AppContainer 内部运行,我们将尝试一个可访问的文件。例如来自 windows/system32 目录的内容。

read:,c:/windows/system32/drivers/etc/hosts

我们也在删除逗号之前的垃圾,看起来似乎无关(虽然他值得进行更多研究)。我们快速分离,重启 Edge,并运行我们的新代码。

url = "read:,c://windows//system32//drivers//etc//hosts";

w = window.open(url, "", "width=300,height=300");

正如预期,在新窗口中加载本地文件并没有崩溃。

利用特殊协议加载本地文件, 绕过 HTML5 沙箱, 打开弹窗诸事

[ PoC – Open hosts on MS Edge ]

跟着 bug hunter,我将在此停顿,但我详细所有的这些事情值得更多的研究,取决于你获得的乐趣了:

A)枚举所有可加载的协议,并通过请求字符串攻击应用程序

B) 使用 microsoft-edge: 绕过 HTML5 沙盒,弹出窗口拦截器和不知谁知道的东西。

C) 继续使用 read: 协议。我们找到了一种方法来阻止它崩溃,但记住有一个函数 SHCreateStreamOnFileEx 期望我们能够影响的东西!这值得更多尝试。此外,我们可以继续在参数上做点事情,看看是否使用逗号分隔参数等等。如果调试二进制是无聊的,那么你仍然可以尝试对阅读视图进行 XSS。

希望你能找到成吨的漏洞!如果你有问题,请在 @magicmac2000 上 ping 我。

Have a nice day!

Reported to MSRC on 2016-10-26

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址