Author: Wfox@360RedTeam
0x00 前言
早前因兴趣使然,从以往一些软件漏洞上得到启发,研究了一段时间嵌入式浏览器框架,在软件漏洞挖掘方面有一些产出,并且挖掘思路与Web漏洞相似,对Web方向选手比较友好,故计划把自己摸索过程中所思所做的东西记录下来,分成多篇文章阐述基础知识、漏洞发掘思路、以往漏洞的细节分析。
0x01 什么是嵌入式浏览器框架
简单理解,嵌入式浏览器框架就是嵌入在客户端软件中的浏览器控件,浏览器与宿主程序是隔离的,通过浏览器控件的丰富接口可以使浏览器与宿主程序进行交互实现丰富的功能。
嵌入式浏览器控件的优势在于,可以更便捷、更优雅的开发出一款客户端产品,并且支持多平台,兼容性高;无需高深的软件开发知识,通过html5就能写出炫酷的软件交互界面。大众熟知的产品如网易云音乐、印象笔记、VSCode、Typora、蚁剑等。
(你所看到的软件界面全是html5网页实现的)
常见的嵌入式框架有Cef、Electron、NW.js、QtWebKit、MSHTML,以应用范围最广的Cef框架举例介绍。
Chromium嵌入式框架(Cef,Chromium Embedded Framework)是个基于Google Chromium项目的开源Web browser控件,支持Windows, Linux, Mac平台。 Cef通过本地库提供的C和C++编程接口,将宿主程序与Chromium和WebKit的实现细节隔离,通过Javascript扩展把网页与宿主程序打通,让浏览器与应用程序无缝集成,并支持自定义插件、伪协议、Javascript对象与扩展。
0x02 Cef基本结构
Cef3采用了多进程架构,Browser进程为主进程,负责窗口管理、界面绘制、网络交互等;Render进程负责页面渲染、V8引擎、Dom节点等。默认的进程模型中,会为每一个标签页创建一个新的Render进程。
Cef进程之间可以通过IPC进行通信,Browser和Render进程可以通过发送异步消息进行双向通信,下面通过介绍Cef结构体来了解他们之间的实现原理。
CefSettings
CefSettings结构体允许定义全局的CEF配置,经常用到的配置项如下:
- single_process 设置为true时,Browser和Render使用一个进程。此项也可以通过命令行参数”single-process”配置。
- browser_subprocess_path 设置用于启动子进程单独执行的路径。
- cache_path 设置存放缓存数据的位置。如果此项为空,某些功能使用内存缓存,多数功能使用临时的磁盘缓存。
- locale 此设置项将传递给Blink。如果此项为空,将使用默认值”en-US”。在Linux平台下此项被忽略,使用环境变量中的值,解析的依次顺序为:LANGUAE,LC_ALL,LC_MESSAGES和LANG。此项也可以通过命令行参数”lang”配置。
- log_file 此项设置文件路径用于输出debug日志,如果此项为空,默认的日志文件名为debug.log,位于应用程序所在的目录。此项也可以通过命令参数”log-file”配置。
- log_severity 此项设置日志级别,只有当前等级、或者更高等级的日志才会被记录。此项可以通过命令行参数”log-severity”配置,可以设置的值为”verbose”,”info”,”warning”,”error”,”error-report”,”disable”。
- resources_dir_path 此项设置资源文件夹的位置。如果此项为空,Windows平台下cef.pak、Linux平台下devtools_resourcs.pak、Mac OS X下的app bundle Resources目录必须位于组件目录。此项也可以通过命令行参数”resource-dir-path”配置。
- locales_dir_path 此项设置locale文件夹位置。如果此项为空,locale文件夹必须位于组件目录,在Mac OS X平台下此项被忽略,pak文件从app bundle Resources目录。此项也可以通过命令行参数”locales-dir-path”配置。
- remote_debugging_port 此项可以设置1024-65535之间的值,用于在指定端口开启远程调试。此项也可以通过命令行参数”remote-debugging-port”配置。
- command_line_args_disabled 用于禁用Cef进程命令行参数功能,只能通过CefSettings方式进行设置。
CefApp
CefApp接口提供了不同进程的可定制回调函数,每一个进程对应一个CefApp接口。CefBrowserProcessHandler对应浏览器进程的回调,CefRenderProcessHandler对应渲染进程的回调。我们应该继承CefApp、CefBrowserProcessHandler、CefRenderProcessHandler接口。如果完全使用多进程模式,可以分别在浏览器进程和渲染进程里分开继承接口
CefApp接口提供了不同进程的可定制回调函数。主要的回调函数如下:
- OnBeforeCommandLineProcessing 用于修改Cef进程命令行启动参数。
- OnRegisterCustomSchemes 用于注册自定义schemes伪协议。
- GetBrowserProcessHandler 返回Browser进程的Handler,该Handler包括了诸如OnContextInitialized的回调。
- GetRenderProcessHandler 返回定制Render进程的Handler,该Handler包含了JavaScript相关的一些回调以及消息处理的回调。
CefClient
每一个CefBrowser对象会对应一个CefClient接口,用于处理浏览器页面的各种回调信息,包括了Browser的生命周期,右键菜单,对话框,状态通知显示,下载事件,拖曳事件,焦点事件,键盘事件,离屏渲染事件等。
- OnProcessMessageReceived 在Browser收到Render进程的消息时被调用
0x03 C++与网页交互实现
Cef结构铺垫完了,接下来就要讲网页是如何通过JavaScript与C++进行交互的,这个也是后续漏洞挖掘中的重点关注部分。
Chromium和CEF都是使用V8 JS引擎执行JavaScript,每一个frame标签页在浏览器进程中都有一个属于自己的JS上下文(Context),在frame中提供一个安全和有限的环境执行js代码。
在Render中执行JavaScript
在客户端执行JavaScript最简单的方法是使用CefFrame::ExecuteJavaScript()函数,该函数在Browser和Render进程中都可以使用,并且能在JS上下文之外使用。
CefRefPtr<CefBrowser> browser = ...;
CefRefPtr<CefFrame> frame = browser->GetMainFrame();
frame->ExecuteJavaScript("alert('ExecuteJavaScript works!');",
frame->GetURL(), 0);
这样网页就会执行JS代码 alert(‘ExecuteJavaScript works!’);,弹个提示框
窗体绑定
窗口绑定允许C++将值附加到网页的window对象,通过CefRenderProcessHandler::OnContextCreated()方法实现,这个接口是在Render进程的V8引擎Context上下文创建后调用的。
void MyRenderProcessHandler::OnContextCreated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) {
// 获取JS上下文的window对象
CefRefPtr<CefV8Value> object = context->GetGlobal();
// 创建一个V8字符串值
CefRefPtr<CefV8Value> str = CefV8Value::CreateString("My Value!");
// 将字符串作为"window.myval"添加到窗口对象。
object->SetValue("myval", str, V8_PROPERTY_ATTRIBUTE_NONE);
}
窗口绑定对象之后,JS的window对象就会多出一个window.myval的字符串
<script language="JavaScript">
alert(window.myval); // 弹窗内容为 "My Value!"
</script>
每次重新加载frame时,窗口绑定都会重新加载,从而使应用程序每次加载时绑定不同的值。例如,通过修改绑定到该frame标签页window对象的值,可以为不同的frame标签页提供不同的访问应用程序权限,比如说下载功能只绑定保存文件、打开文件等对象,登录功能只绑定验证相关的对象。
JavaScript扩展
扩展跟窗口绑定类似,当扩展加载到每个frame标签页的上下文后就无法修改了。通过CefRenderProcessHandler::OnWebKitInitialized()方法初始化WebKit时调用CefRegisterExtension()函数将JavaScript扩展函数注册到上下文中。
void MyRenderProcessHandler::OnWebKitInitialized() {
// Define the extension contents.
std::string extensionCode =
"var test;"
"if (!test)"
" test = {};"
"(function() {"
" test.myval = 'My Value!';"
"})();";
// Register the extension.
CefRegisterExtension("v8/test", extensionCode, NULL);
}
extensionCode中的字符串可以是任意的JS代码,样例代码中注册了一个test对象,并且给test对象的myval变量赋值字符串,然后注册到上下文的test对象中。可以通过frame的JS与JS扩展进行交互。
<script language="JavaScript">
alert(test.myval); // 弹窗内容为 "My Value!"
</script>
这种方式是最为常见的页面JS与C++交互方式,高级用法就是通过这种方式注册Native JavaScript函数,从而调用C++在网页中实现丰富的软件功能。
0x04 结语
本章节主要是梳理了一些Cef的基本结构、JS与C++的交互实现原理,为后面的漏洞挖掘分析章节做铺垫。当然你也可以不需要太深入地了解原理,有Web漏洞基础也能够去发掘这方面的漏洞。
0x05 引用
https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage https://bitbucket.org/chromiumembedded/cef/wiki/JavaScriptIntegration https://blog.csdn.net/zhuhongshu/article/details/70159672
发表评论
您还未登录,请先登录。
登录