浏览器Fuzz

浏览器fuzz

一、浏览器部分

1.1 浏览器结构

浏览器一般包括DOM引擎、CSS解释器、脚本引擎、渲染引擎、协议支持、媒体流支持、三方库、数据存储、用户界面等几个部分。

  • 用户界面

    与用户交互、接受用户操作、展现浏览器当前状态。包括地址栏、前进/后退按钮、书签菜单等。

  • 浏览器引擎

    是一个可嵌入的组件,提供了查询和操作渲染引擎的高层接口(加载、刷新、后退、错误信息等),使渲染引擎平台无关, 提高可移植性

  • 渲染引擎

    浏览器的核心部分。解析HTML文档和CSS描述;依据CSS,完成HTML布局。渲染引擎根据元素与样式信息,计算大小、位置、布局,渲染解析后的内容。

  • 网络

    提供网络相关的功能,如协议解析、网络I/O等。浏览器支持的协议通常包括HTTP、FTP、WebSocket、HTTP/2、QUIC、DNS、mDNS、WebRTC等。浏览器支持的媒体流通常包括JPG、GIF、PNG、WebM、Ogg、AAC、MP3、MP4、FLAC等。

  • JavaScript解释器

    解析并执行JavaScript代码,结果返回给渲染引擎

  • XML解析器

    解析XML文档

  • 用户界面后端

    提供绘制、窗口原语和字体等

  • 数据存储

    存储书签、Cookie、 缓存等各种数据到硬盘上

1.2 渲染引擎

这是浏览器的核心部分,渲染引擎解析 HTML 文档,并将各标记逐个转化成 DOM 节点。同时也会解析外部 CSS 文件以及样式元素中的样式数据,完成HTML布局。

  • CSS解释器

    解析CSS,为DOM中的各个元素计算样式信息。

  • 脚本引擎

    包括JavaScript、VBScript、asm.js、WebAssembly等解释器,对脚本进行解释执行,并对DOM、CSS进行修改。

  • DOM是文档对象模型(Document Object Model)的缩写,标准由 W3C 组织指定。

    DOM树是把 HTML 文档呈现为带有元素、属性和文本的树结构。DOM引擎解析HTML、XML、SVG、MathML、XUL等内容。

  • Render树

    包含若干带有颜色、面积等可视化属性的矩形。也就是我们学前端时的各个div的布局(从左往右、从上到下)

渲染过程

DOM树fuzz的流程图

1.3 JS引擎

JavaScript引擎是一个专门处理JavaScript脚本的虚拟机,一般会附带在网页浏览器之中。

JS引擎分类

  • V8 (Chrome)
  • SpiderMonkey (FireFox)
  • ChakraCore (Edge)
  • JavaScriptCore (Safari)

执行流程

  • JavaScript源代码被Parser解析成AST
  • AST经Interpreter解析成Bytecode后执行
  • 不断执行Bytecode,收集运行时信息,生成优化后的Bytecode

对上图的解释

  • Parser:脚本执行的入口点,通常发出自定义bytecode
  • Bytecode然后由解释器或JIT编译器解释编译
  • 执行代码与运行时交互,运行时定义各种数据结构的表示,提供内建函数和对象等等
  • 回收内存所需的垃圾收集器

JIT编译器的流程

JS是脚本语言,每次执行前会先编译再执行。当系统识别这一块代码经常出现时,就定义为hot code,直接运行编译好的二进制代码。

具体可以参考JavaScript 编译 - JIT (just-in-time) compiler 是怎么工作的

下图为parser先生成抽象语法树(AST),再由全代码生成汇编语言,箭头指向调用runtime

1.4 WebAssembly

WebAssembly是在Web中引入的低级语言,其目的和Asm.js类似,也是要支持本地代码的运行从而获得媲美原生应用的高性能。

它是一种二进制语言,但是无法直接在处理器上运行。在运行时,代码被编译为中间字节代码,可以在浏览器内快速转换为机器代码,然后比传统JavaScript更有效地执行。

Wasm的设计从如下几个方面考虑来保证Wasm的安全性

  • 保护用户免受由于无意的错误而导致漏洞的应用程序的侵害
  • 保护用户免受故意编写为恶意的应用程序的侵害
  • 为开发人员提供良好的缓解措施

具体的安全措施有

  • Wasm应用程序在沙箱内运行
  • Wasm无法对任意地址进行函数调用。Wasm采用对函数进行编号的方式,编号存储在函数表中
  • 间接函数调用受类型签名检查的约束
  • 调用堆栈受到保护,这意味着无法覆盖返回指针
  • 实现了控制流完整性,这意味着调用意外的函数将失败

二、Fuzz部分

Fuzz又称模糊测试,是一种侧重于发现软件安全漏洞的方法。

Fuzz是通过构造非预期的输入数据并监控目标软件在运行过程中的异常结果来发现软件故障。

2.1 核心功能

2.2 基本流程

2.3 fuzz分类

根据fuzz对象可以分为

  • 白盒模糊测试

  • 黑盒模糊测试

  • 灰盒模糊测试

根据模糊测试的研究类型可分为

  • 基于变异的fuzz
  • 基于生成的fuzz

三、浏览器fuzz

3.1 如何进行浏览器fuzz(我们需要关心什么)

主要关心的问题:如何输入、如何有效输入、可fuzz的地方有哪些

以一个例子为例,可知一个通用的方法:

  • 通过调用接口获取信息
  • 由参数类型构造数据
  • 使用监视器监视

如何构造是个问题

这部分与上述提到的JIT编译器有关。既可以输入js源代码,也可以输入bytecode

在fuzz中,测试集是很重要的。因为后续的变异是基于初试testcase变异的。

这里补充一下fuzz常见的基于的规格:可以戳这里看第五大点

  • 基于块

将程序拖入ida会分成一块一块的代码,这些代码会从上到下依次执行,当遇到转移/跳转指令之后到下一个代码块

  • 基于边

代码块与块之间的跳转

  • 基于元组

基于两个块和中间的边,可以记录前后块的信息,包含一定的前后文的情况

基于文法同样可以获得很多满足结构的语法片段。例如1+2+3可以分为(1+2)+3和1+(2+3),抽象语法树一般有多个满足语法条件的形式。这种方式同样可以生成规模很大的测试集

3.2 对浏览器fuzz的分类

3.2.1 按功能分

在浏览器中,Fuzz框架要实现的主要功能是随机样本的生成、浏览器进程监控、Crash样本保存。而各个框架则在完成基本功能的基础上实现分布式调度、自定义fuzz策略、样本精简、分析crash等特性。

如果要分类的话,浏览器fuzz工具大概可以分为框架类、策略类以及综合类。策略类工具制定了一系列新建、修改、删除DOM元素的规则。通过生成随机样本的方式,产生不同的样本来测试浏览器是否产生异常。而框架类工具则是提供了启动并监控浏览器进程、打开或记录异常样本等功能,将策略类工具承载起来的Fuzzing平台。

  • 框架类比较有代表性的是BFuzzer、grinder、cross_fuzz、x-fuzzer等,这类工具提供了fuzz的基本功能,但是没有涉及样本生成的策略。

  • 策略类工具中比较有代表性的是nduja和fileja[3-5],这两个工具是由Valotta完成的浏览器fuzz样本生成脚本,有着比较丰富的生成规则,但是并没有实现自动化的测试等功能。

  • 综合类则综合了两类的特点,完成了两种功能,比较有代表性的是clusterfuzz、funfuzz、chromefuzzer、ajaxdemolisher、lithium等。

这些工具基本都通过javascript脚本进行fuzz操作,同时通过hook函数、localStorage本地存储等技术手段动态记录fuzz操作日志,捕获到异常后再根据记录日志进行还原。

3.2.2 按攻击面分类

另外一种分类的思路是按攻击面分类,常见的fuzzer都是针对DOM进行fuzz,但是也有一些fuzz工具专注于其他的攻击面。比如针对ActiveX进行fuzz的axman,另外也有一些针对JavaScript脚本、CSS、PDF、SVG、Flash、浏览器扩展、插件等进行fuzz的工具。

3.2.3 随机样本的生成

随机样本生成的好坏,直接决定了该工具能否挖掘出有价值的漏洞。目前纯Fuzz方法中,比较流行的就是根据目标文件格式生成针对性样本,效果也比较理想。以HTML文档为例,诞生过很多随机生成HTML/DOM元素的Browser Fuzzer。

曾经盛名一时的nduja,其大致思路就是,利用JavaScript随机创建DOM元素、随机调用DOM处理函数、随机删除DOM元素等操作来完成每一次Fuzz的。

3.3 样本保存

附上一张从bytecode到插桩的流程图

3.3.1 记录随机数

结合之前提到的创建DOM元素的方式,一种思路是将本次rand函数生成的所有随机数全部保存下来即可。Grinder框架就是基于这种思路,采用DLL注入的方式,劫持了JavaScript的parseFloat函数,用以记录某次样本需要记录的相关参数。

但是这样有几个缺点,一个是浏览器最新版本一般不提供symbols文件或发布symbols文件比较慢,这无疑限制了Grinder对最新浏览器的测试能力。另外一个是有时候可能是多个HTML共同导致了某个Crash,但是Grinder的日志只记录了一个HTML中的log信息,可能无法重现该类漏洞。

3.3.2 记录随机样本

既然记录随机值的方式存在一定的问题,那么就有了另一个思路,直接将模版中的随机元素生成后进行保存,然后浏览器再访问。这样就简化了从日志中恢复样本的流程,而且更为准确。

3.3.3 WebSocket通信

另一个方法是利用websocket,实时的把相关信息发送到服务器,在服务器端进行保存。但是这样也有一个缺点,运行的websocket相关脚本可能也会影响浏览器样本的fuzz情况。

3.4.4 其他样本生成方式

另外一种方式是根据已有的HTML样本进行变换之后生成。 样本可以来源于之前的0day,从网上获取的一些样本。 样本构造尽可能多的覆盖HTML的属性,比如css/javascript等

3.4 浏览器进程监控

在对浏览器的异常检测上,通常有Pydbg和Windbg等方式,这些方式都各有优劣。但pydbg对python3和x64系统软件支持都不是特别好,另外windbg具有很吸引人的!exploitable插件[2-7],虽然某些时候给出的结论并不准确,但不失为一种对漏洞可利用性做出预判的方法。

另一个方法是,在Windows下某个进程崩溃时,通常会弹出WerFault.exe异常提示,可以根据进程列表中有无WerFault.exe作为监控浏览器是否发生Crash的依据。另外,将Windbg的命令行版cdb.exe设置为默认即时调试器。当发生crash时自动调用该调试器,并做下记录,也是一个可行的方法。

3.5 浏览器fuzz缺点

很多浏览器fuzz工具每运行一个测试用例都会重启待测浏览器进程,因为浏览器重启在运行一个测试用例过程中耗时占比较大,进而导致fuzz效率不高。

3.6 别的思考

这里需要说一下JS竟用条件
所有现代浏览器都使用一条OS线程来实现JS脚本引擎。
唯一的例外是web workers,但它不会访问DOM,相对安全风险较小。

由于大部分事件是异步处理的,所以很有可能同一事件内不同部件的内存内容不同,这极易引发安全问题。

附上V8编译概览

这篇文章是我在web安全大作业的一部分,刘老师真的很赞

文中截取了一些霍玮老师、刘奇旭老师课件上的图片(说明俺听了2333

文章也引用了很多交大大佬LyleMi已经总结好的内容,了解到了许多细节

感谢队友,尤其是plusls,orz

在整理过程中,我看了很多相关论文和v8的资料,每一个地方都有太多要学的地方了。v8的内容之后估计还会写到,本文以能解释清楚浏览器fuzz的目的来写,所以v8的部分不是很清楚,以后会结合着真实案例来写。

如果有什么问题,师傅可以点击[联系我](Email)发邮件


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!