如何在v8引擎中找到未被发现的攻击面(以CVE-2019-5790为例)

2024-03-06 07:18

本文主要是介绍如何在v8引擎中找到未被发现的攻击面(以CVE-2019-5790为例),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

如果你查看最近关于浏览器安全性和JavaScript引擎安全性的所有文章,你很容易产生一种错觉,即在现代JavaScript实现中,只能在即时(just-in-time, JIT)编译器中寻找未被发现的漏洞。JavaScript引擎是一个专门处理JavaScript脚本的虚拟机,一般会附带在网页浏览器之中,该引擎的结构非常复杂、代码非常混乱(仅在3月份,仅V8就提交了500次有问题的代码),另外公开披露的漏洞的数量似乎不断地在增加,这些都表明现代JavaScript就是所有攻击的初始源头。

然而,在研究web浏览器等常见的攻击目标时,许多研究人员的关注点很可能会习惯性的被公开披露的漏洞所主导。我想在发现漏洞这件事上,造成这样的原因主要有两方面:

一方面,在开始寻找一个从未被发现的新漏洞时,这种参考已有成果的方法可以让研究人员很好地快速了解代码库中可能容易出错的区域。毫无疑问,有些研究范围(JavaScript引擎)比其他领域更复杂,技术要求更高,因此为了快速得出成果,许多人就愿意在既定成果中打转转,不做思考。

另一方面,人们常常忘记代码库的其他部分(目前不太受公众关注)也可能提供一些有趣的攻击面和漏洞,不应该被轻易忽略。

如果研究人员的目标是寻找生命周期已经超过几周甚至几个月的漏洞,那以上这种跟风的方法是没有什么问题的。不过在寻找新出现甚至还未在公开出版物中提及的漏洞时,以上方法就没有用了。

在这篇文章里,我们描述我们是如何成功地找到一个在v8引擎中的漏洞(CVE-2019-5790)?除此之外,我还会介绍一下如何专注于一个看起来不像是有攻击面的架构的。中国菜刀

JavaScript管道

下面给出了JavaScript管道中涉及的不同阶段的高级描述,以便对可能的攻击表面提供非常粗略的概述。如果你想了解更多,请点此。

                             AST               Bytecode
+-------------+   +--------+    +-------------+        +--------------+
| JavaScript  |-->| Parser |--->| Interpreter |------->| JIT Compiler |----+
| source code |   |        |    | (Ignition)  |        | (TurboFan)   |    |
+-------------+   +--------+    +-------------+        +--------------+    | Assembly |                                   | code|               +---------+         |+-------------->| Runtime |<--------+Bytecode    +---------+

以下描述将重点介绍V8,但类似的概念也适用于其他引擎。

分析JavaScript引擎漏洞的第一步是解析JavaScript源代码,目的就是将源代码转换为抽象语法树(AST)表示。即使是这样一个看似简单的任务,例如从字符流中扫描已知令牌的文本,在V8等现代JavaScript引擎中也得到了高度的速度优化和持续改进。这正是第一阶段,这一阶段是我们能够确定漏洞的基础,下文将加以说明。

构建AST之后,它被转换为自定义字节码,然后由解释器或JIT编译器使用。V8使用Ignition作为它的解释器,2016年谷歌V8 JavaScript引擎引入新解释器Ignition,旨在减少内存消耗。字节码要么由寄存器设备直接执行,要么传递给JIT编译器。在JavaScript管道这个阶段,我们已经有了进行了一些优化,因此潜在的漏洞也可能会出现。

当一个函数在interpeter中执行了一定次数后,它被令牌为“hot”,并将由JIT编译器编译为设备代码。V8使用TurboFan作为它的JIT编译器,从整体架构上Ignition与TurboFan在一起配合工作有许多的好处。例如,不需要在手工编写高效的程序集处理Ignition生成的字节码,而是使用TurboFan中间件来处理,并让TurboFan为V8所支持的平台进行优化和生成最终的执行代码。目前,这个阶段是一个非常复杂的过程,已经成为了大量漏洞的来源之处。

与JavaScript管道同时使用的还有垃圾收集器,它允许程序员不必显式地管理内存。尽管这减少了大量的漏洞,比如内存泄漏,但是它也会导致一些有趣的漏洞。因为当需要分配的内存空间不再使用的时候,JVM将调用垃圾回收机制来回收内存空间。

JavaScript解

实现解析器的代码可以在src/parser/V8源代码树中找到:

                                                  +-----------++---------->| PreParser ||  tokens   +-----------+|                 ||                 v
+-------+       +--------+        +---------+        +--------+     
| Blink |------>| Stream |------->| Scanner |------->| Parser |
+-------+       +--------+        +---------+        +--------+ASCII            UTF-16             tokens      || AST               v+----------+            +----------+| TurboFan |<-----------| Ignition |+----------+            +----------+   bytecode

解析JavaScript源代码的第一步是扫描令牌的文本。Scanner类使用输入并生成由解析器使用的令牌对象。UTF16CharacterStream类用作文本输入流的抽象,为扫描器提供UTF-16格式的令牌,并抽象出从网络接收到的不同的JavaScript编码格式。然后,Parser类根据所使用的令牌生成最终的AST。

LiteralBuffer整数溢出(CVE-2019-5790)

以下漏洞是由研究人员DimitriFourny (@DimitriFourny)发现的,并于2018年12月13日报告给谷歌的。它已在Chrome版本73.0.3683.75中修复,可以在这里找到相应的漏洞跟踪信息。天空彩

Scanner::Scan方法通过调用Scanner::ScanSingleToken来查找数据流中的下一个非空白令牌。根据遇到的令牌,它会实现一些特殊情况来适当地处理它们。例如,单字符令牌(如大括号、大括号或分号)只会返回,而其他令牌则会消耗数据流中的更多字符。

比如TOKEN::String令牌,它是为引用字符返回的。如果遇到此令牌,将调用Scanner::ScanString方法。该方法在循环中调用Scanner::AddLiteralChar,直到找到结束引号字符。

Scanner::AddLiteralChar方法调用Scanner::LiteralBuffer::AddChar,如果初始引号字符后跟两个字节的字符,它最后会调用Scanner::LiteralBuffer::AddTwoByteChar。

void Scanner::LiteralBuffer::AddTwoByteChar(uc32 code_unit) {DCHECK(!is_one_byte());if (position_ >= backing_store_.length()) ExpandBuffer();if (code_unit <=static_cast(unibrow::Utf16::kMaxNonSurrogateCharCode)) {*reinterpret_cast<uint16_t*>(&backing_store_[position_]) = code_unit;position_ += kUC16Size;} else {*reinterpret_cast<uint16_t*>(&backing_store_[position_]) =unibrow::Utf16::LeadSurrogate(code_unit);position_ += kUC16Size;if (position_ >= backing_store_.length()) ExpandBuffer();*reinterpret_cast<uint16_t*>(&backing_store_[position_]) =unibrow::Utf16::TrailSurrogate(code_unit);position_ += kUC16Size;}
}

backing_store_byte向量缓冲已经扫描的字符串部分,会根据需要动态调整大小。如果Scanner::LiteralBuffer::AddTwoByteChar方法检测到向量需要扩大,它会调用Scanner::LiteralBuffer::ExpandBuffer,它会分配一个更大的缓冲区,然后将旧缓冲区中的字节复制到新缓冲区中。

void Scanner::LiteralBuffer::ExpandBuffer() {Vector new_store = Vector::New(NewCapacity(kInitialCapacity));MemCopy(new_store.start(), backing_store_.start(), position_);backing_store_.Dispose();backing_store_ = new_store;
}

Scanner::LiteralBuffer::NewCapacity方法用于计算新向量的大小:

int Scanner::LiteralBuffer::NewCapacity(int min_capacity) {int capacity = Max(min_capacity, backing_store_.length());int new_capacity = Min(capacity * kGrowthFactory, capacity + kMaxGrowth);return new_capacity;
}

我们可以通过改变初始引号字符后面的字符数来控制backing_store_.length()。巨大的JavaScript字符串会导致巨大的容量值,这会使表达式capacity * kGrowthFactory溢出,因此new_capacity将被设置为比前一个容量更小的值。因此,下一个MemCopy调用将向向量写入比先前分配的字节更多的字节,从而导致堆内存损坏。

以下简单的概念验证就可以触发漏洞:

let s = String.fromCharCode(0x4141).repeat(0x10000001) + "A";
s = "'"+s+"'";
eval(s);

这个漏洞看起来非常明显,阅读以上代码便可以轻而易举地发现。但是如果它们被模糊处理,则可能很难被发现,因为它需要大约20 GB的内存,而且在典型的台式机上触发它需要相当长的时间。

总结

人云亦云的做法存在于各个行业,当要探索的漏洞是一个被很多人研究过的目标时,即使环境比较复杂,你也可能会发现许多漏洞。尽管如此,像web浏览器这样的大型攻击目标,还是有很多未被发现的漏洞,要想找到这些未被发现的漏洞,就得找到新的攻击面。

这篇关于如何在v8引擎中找到未被发现的攻击面(以CVE-2019-5790为例)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/779247

相关文章

BUUCTF靶场[web][极客大挑战 2019]Http、[HCTF 2018]admin

目录   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 [web][HCTF 2018]admin 考点:弱密码字典爆破 四种方法:   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 访问环境 老规矩,我们先查看源代码

速了解MySQL 数据库不同存储引擎

快速了解MySQL 数据库不同存储引擎 MySQL 提供了多种存储引擎,每种存储引擎都有其特定的特性和适用场景。了解这些存储引擎的特性,有助于在设计数据库时做出合理的选择。以下是 MySQL 中几种常用存储引擎的详细介绍。 1. InnoDB 特点: 事务支持:InnoDB 是一个支持 ACID(原子性、一致性、隔离性、持久性)事务的存储引擎。行级锁:使用行级锁来提高并发性,减少锁竞争

Smarty模板引擎工作机制(一)

深入浅出Smarty模板引擎工作机制,我们将对比使用smarty模板引擎和没使用smarty模板引擎的两种开发方式的区别,并动手开发一个自己的模板引擎,以便加深对smarty模板引擎工作机制的理解。 在没有使用Smarty模板引擎的情况下,我们都是将PHP程序和网页模板合在一起编辑的,好比下面的源代码: <?php$title="深处浅出之Smarty模板引擎工作机制";$content=

MySQL技术内幕_innodb存储引擎

MySQL技术内幕_innodb存储引擎 INNODB innodb中如果表没有主键 表是否由 非空唯一键,有则该字段为主键没有,则自动创建一个6字节大小的指针 innodb存储引擎的所有数据都存储在表空间中,表空间由段,区,页(块)组成。 如果启用了 innodb_file_per_table, 则每张表内的数据可以单独放在一个表空间中即使启用了上面参数,共享表空间也会因为 系统事务信息

Matter.js:Web开发者的2D物理引擎

Matter.js:Web开发者的2D物理引擎 前言 在现代网页开发中,交互性和动态效果是提升用户体验的关键因素。 Matter.js,一个专为网页设计的2D物理引擎,为开发者提供了一种简单而强大的方式,来实现复杂的物理交互效果。 无论是模拟重力、碰撞还是复杂的物体运动,Matter.js 都能轻松应对。 本文将带你深入了解 Matter.js ,并提供实际的代码示例,让你一窥其强大功能

认识鬼火引擎

一、Irrlicht简介 (1)概念Irrlicht引擎是一个用C++书写的高性能实时3D引擎,可以应用于C++程序或者.NET语言中。通过使用Direct3D(Windows平台)、OpenGL 或它自己的软件着色程序,可以实现该引的完​全跨平台。尽管是开源的,该Irrlicht库提供了可以在商业级的3D引擎上具有的艺术特性,例如动态的阴影,粒子系统,角色动画,室内和室外技术以及碰撞检测等。(

一个图形引擎的画面风格是由那些因素(技术)决定的?

可能很多人第一直覺會認為shader決定了視覺風格,但我認為可以從多個方面去考慮。 1. 幾何模型 一個畫面由多個成分組成,最基本的應該是其結構,在圖形學中通常稱為幾何模型。 一些引擎,如Quake/UE,有比較強的Brush建模功能(或應稱作CSG),製作建築比較方便。而CE則有較強的大型地表、植被、水體等功能,做室外自然環境十分出色。而另一些遊戲類型專用的引擎,例

在二叉树中找到两个节点的最近公共祖先(基于Java)

如题  题解 public int lowestCommonAncestor(TreeNode root, int o1, int o2) {//记录遍历到的每个节点的父节点。Map<Integer, Integer> parent = new HashMap<>();Queue<TreeNode> queue = new LinkedList<>();parent.put(roo

Matlab/Simulink和AMEsim联合仿真(以PSO-PID算法为例)

目录 安装软件和配置环境变量 Matlab/Simulink和AMEsim联合仿真详细流程 非常重要的一点 Simulink模型和AMEsim模型用S-Function建立连接 从AMEsim软件打开Matlab Matlab里的设置 Matlab的.m文件修改(对于PSO-PID算法) 运行程序 我印象中好像做过Matlab/Simulink和AMEsim联合仿真的分享似的

气膜场馆:乡村振兴中的健康与经济新引擎—轻空间

随着乡村振兴战略的深入推进,气膜场馆作为新兴建筑形式,正在为农村地区带来全新的发展机遇。它不仅是乡村百姓锻炼身体的好去处,更是带动当地经济发展的强劲动力。 首先,气膜场馆为农村地区的居民提供了更多运动健身的机会。与传统体育设施相比,气膜场馆建设周期短、成本低,非常适合在乡村快速推广。通过提供羽毛球、篮球、排球等多种运动项目,村民可以在空闲时间增强体质,改善生活方式。这对于长期从事农业劳动的村