如安在v8引擎中找到未被发现的攻击面(以CVE-2019-5790为例) | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

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

申博_安全预警 申博 104次浏览 未收录 0个评论

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

若是你检察近来关于浏览器安全性和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将挪用渣滓收受接管机制来收受接管内存空间。

APT34原型: Glimpse project

APT34 Glimpse project可能是截止目前研究人员最了解的的APT 34项目了。研究人员观察到基于文件的C2结构、VBS启动器、PowerShell Payload和dns引擎之上的秘密信道。 背景 从2014年开始,伊朗黑客组织APT34开始活跃,该组织执行了一系列针对伊朗的攻击活动。重要攻击目标位于中东地区,主要攻击金融、政府、能源、化工、电信和其他行业。 下面主要介绍Glimpse project,研究人员认为这可以认为是APT 34的原型。 Glimpse Project 该package中有一个名为Read me.txt的README文件。文件的内容是教授如何设置nodejs服务器和运行standalone .NET应用的Windows服务器来来控制受感染机器。感染首先是从一个名为runner_.vbs的VBS脚本的繁殖开始的,该脚本也是多数熟练的PowerShell payload的运行器。该Powershell payload是一个复杂的脚本,有许多功能。下图是反混淆的主循环: Glimpse感染Payload主循环 Payload会循环等待指令,一旦从C2服务器获得命令,就开始执行特定动作,并通过请求基于变量$aa_domain_bb伪造的子域名来响应C2。Payload实现的最重要的函数就是释放和执行其他工具集。事实上,payload是基于DNS 秘密信道的夹杂了其他控制功能的传播模块。 变量 $aa_domain_bb 含有C

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浏览器如许的大型进击目标,照样有许多未被发明的破绽,要想找到这些未被发明的破绽,就得找到新的进击面。


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明如安在v8引擎中找到未被发现的攻击面(以CVE-2019-5790为例)
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

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