火狐内部的进程间通信(IPC)层是火狐多进程安全架构的基石。因此,消除IPC层中的安全漏洞仍然至关重要。在这篇博文中,我们调查并描述了Firefox用于执行进程间通信的不同通信方法,希望能提供逻辑上的切入点,以有效地摸清Firefox中的IPC层。
0x1 火狐多进程安全架构的背景
启动 Firefox Web 浏览器时,它会在内部生成一个特权进程(也称为父进程),然后启动和协调多个内容进程的活动。 这种多进程架构允许 Firefox 将更复杂或可信度较低的代码分离到进程中,其中大部分都减少了对操作系统资源或用户文件的访问。 (在地址栏中输入 about:processes 会显示有关所有正在运行的进程的详细信息)。 因此,较低特权的代码将需要请求较高特权的代码来执行它本身无法执行的操作。 请求委托操作或通常内容和父进程之间的任何通信都通过 IPC 层发生。
0x2 IPC—进程间通信
从安全角度来看,进程间通信 (IPC) 尤其令人感兴趣,因为它跨越了 Firefox 中的多个安全边界。最明显的是 PARENT <-> CONTENT 进程边界。 托管一个或多个包含 Web 内容的选项卡的内容(或子进程)是无特权和沙盒的,并且在威胁建模场景中通常被认为是受到威胁并运行任意攻击者代码。另一方面,父进程拥有对主机的完全访问权。虽然这种父子关系不是唯一的安全边界(进程权限的文档),但从安全角度来看,它是最关键的边界,因为任何违规都会导致沙箱逃逸。
Firefox内部使用三种主要的通信方法(加上一个过时的方法),内容进程可以通过这些方法与主进程通信。更详细地说,Firefox中进程间的通信是通过以下方式进行的。(1)IPDL协议,(2)共享内存,(3)JS Actors,有时也通过(4)消息管理器 这种过时的进程通信机制。请注意,(3)和(4)在内部是建立在(1)之上的,因此可以识别 IPDL。
IPDL
进程间通信协议定义语言(IPDL)是Mozilla特有的语言,用于定义C++代码中进程间的信息传递机制。在Firefox中管理父进程和子进程间通信的主要协议是Content协议。正如你在其名为PContent.ipdl的源码中所看到的,该定义相当大,包括了很多其他的协议—因为在Content中有很多子协议,所以整个协议定义是分层的。在每个协议里面,都有启动每个子协议的方法,并提供对新启动的子协议里面的方法的访问方式。对于自动化测试,这种结构也意味着没有一个包含所有有趣方法的平坦攻击面。相反,它可能需要多次消息往返才能到达某个子协议,然后可能会暴露一个漏洞。除了PContent.ipdl之外,Firefox还使用PBackground.ipdl,它不包括在Content中,但允许后台通信,因此提供了另一种在C++进程之间传递信息的机制。
共享内存
Shmems是跨进程共享内存的主要对象。它们是IPDL感知的,并由IPDL角色创建和管理。在代码中找到它们的最简单方法是搜索 “AllocShmem/AllocUnsafeShmem “的用法,或者搜索所有以”.ipdl “结尾的文件。作为一个例子,PContent::InvokeDragSession使用PARENT <-> CONTENT Shmems来传输拖/放图像、文件等。一个更简单的例子,尽管不总是涉及父进程,可以在PCompositorBridge::EndRecordingToMemory中找到,它被用来记录浏览器的显示,用于收集视觉性能指标等事情。它返回一个CollectedFramesParams结构,其中包含一个Shmem。首先,消息由PCompositorBridgeChild角色发送至GPU进程的PCompositorBridgeParent,在那里Shmem被分配,填充为char*并返回。然后,它被子角色解释为原始字节的缓冲区,使用Span对象。请注意,上面的例子不一定涉及父进程,因为它说明了进程设计中两个更微妙的混乱方面。首先,PCompositorBridge行为体同时作为GPU <-> PARENT行为体与GPU <-> CONTENT行为体存在,GPU <-> PARENT用于浏览器UI渲染,在文档的某些部分称为 “chrome”,而GPU <-> CONTENT行为体,用于页面渲染。(PCompositorBridge的注释更详细地解释了这种关系)。其次,GPU进程并不总是存在。例如,在基于Mac的系统上没有GPU进程。这并不意味着这些角色不被使用——它只是意味着当没有GPU进程时,父进程会代替它。最后,SharedMemoryBasic是一个不使用IPDL的低级共享内存对象。它的使用没有得到很好的标准化,但它在正常操作中被大量使用,例如,SourceSurfaceSharedData在进程之间共享纹理数据。
JSActors
在JavaScript中执行IPC的首选方式是基于JS Actors,其中JSProcessActor提供了一个子进程和其父进程之间的通信通道,JSWindowActor提供了一个框架和其父框架之间的通信通道。在注册了一个新的JS行为体后,它提供了两种方法来发送消息。(a) sendAsyncMessage()和(b) sendQuery(),以及一个接收消息的方法:receiveMessage()。
消息管理器
除了JS Actors之外,还有一种方法可以在JavaScript中执行IPC,即消息管理器。尽管我们正在用更强大的JS Actors取代基于消息管理器的IPC通信,但代码库中仍然有一些实例依赖于消息管理器。它的总体结构非常松散,没有方法的注册表,也没有类型的规范。MessageManager消息是通过以下四个JavaScript方法发送的。(a) sendSyncMessage(), (b) sendAsyncMessage(), (c) sendRpcMessage(), and (d) broadcastAsyncMessage().启用MOZ_LOG=”MessageManager:5” 将输出所有在IPC层来回传递的消息。MessageManager是使用PContent::SyncMessage和PContent::AsyncMessage建立在IPDL之上。
0x3 隔离中的模糊测试 vs 系统测试
对于自动化测试,特别是模糊测试,隔离要测试的组件已被多次证明是有效的。不幸的是对于IPC,这种方法并不成功,因为有趣的类,如ContentParent IPC端点,与周围环境有非常复杂的运行时契约。孤立地使用它们会导致大量的误报崩溃。作为一个例子,比较一下ContentParent的libFuzzer目标,它发现了一些安全漏洞,但也发现了更多的(主要是nullptr)崩溃,这些崩溃与缺少初始化步骤有关。误报不仅降低了对工具结果的信心,还需要额外的维护,而且还会指出由于设置不当而导致攻击面的某些缺失部分。因此,我们认为系统测试方法是全面IPC测试的唯一可行的解决方案。
要有效地对这种情况进行模糊处理,一个潜在的方法是用一个新的标签启动Firefox,导航到一个虚拟页面,然后对父程序进行快照(基于进程或虚拟机的快照模糊处理),然后用覆盖率引导的模糊处理取代常规的子消息—引导模糊测试。快照方法将进一步允许不时地将父级重置为定义的状态,而不会遭受重新启动进程的主要性能瓶颈。如上文IPDL部分所述,有多个消息来回传递对于确保我们能够深入到协议树中是至关重要的。最后,崩溃的可复现性也很关键,因为没有可靠的复现步骤的bug通常受到的关注要少得多。换句话说,有可靠的复现步骤的漏洞可以更快地被隔离、处理和修复。
对于基于虚拟机的快照模糊处理,我们意识到成功的模糊处理需要满足某些要求。特别是:
- 进程准备好进行模糊测试时的回调:为了知道什么时候开始拦截和进行模糊测试的通信,必须在事情 “准备好 “的正确时刻进行某种回调。举个例子,当一个新的标签准备好浏览的时候,或者更准确地说,已经加载了它的URI,这可能是一个很好的PARENT <-> CONTENT 子项模糊测试的入口点。在这个时间点上拦截消息的一个方法是检查STATE_STOP消息和你正在加载的预期URI,例如在OnStateChange()中。
- 创建通信套接字时的回调:在 Linux 上,Firefox 使用 UNIX 套接字进行 IPC 通信。 如果您想拦截父级中套接字的创建,则应该寻找 ChannelImpl::CreatePipe方法。
0x4 以前的BUG示例
我们通过各种方式发现了(安全)错误,包括静态分析、手动审计和隔离部分的libFuzzer 目标(具有上述问题)。 查看这些错误的报告可能还会提供一些有用的信息:
- Heap-use-after-free in PNecko
- Various issues in PHal
- Arbitrary file listing in PContent
- Out-of-bounds read in PContent through arbitrary icon size
- Heap-use-after-free related to PPrinting
- Out-of-bounds read in URL Classifier through PContent
0x5 更多与Firefox有关的模糊处理资源
以下资源不是专门针对IPC模糊测试的,但可以提供额外的背景信息,并且在Mozilla广泛用于以各种方式模糊测试Firefox:
- prefpicker – Compiles prefs.js files used for fuzzing Firefox
- fuzzfetch – A tool to download all sorts of Firefox or JS shell builds from our automation
- ffpuppet – Makes it easier to automate browser profile setup, startup etc.
- Fuzzing Interface – Shows how libFuzzer targets, instrumentation, etc. work in our codebase
- Sanitizers – How to build with various sanitizers, known issues, workarounds, etc.
0x6 前瞻
为系统的安全设计提供架构方面的见解对于真正的开源工作至关重要,并最终允许贡献者、黑客和错误赏金猎人验证和挑战我们的设计决策。我们想指出的是,IPC层中的bug有资格获得bug赏金——你可以通过在Bugzilla上提交bug来报告潜在的漏洞。谢谢你!
发表评论
您还未登录,请先登录。
登录