对一个chakra引擎中RCE漏洞的完整分析

篇文章是一篇关于ChakraCore的RCE漏洞的分析,从我发现它差不多已经过去一年了。我之前从未报告过此漏洞的原因是Chakra很长一段时间没有推出新版本,因此这个bug从未作为Edge的一部分被发布。我仍然可以向MSRC报告此漏洞,并且可能会收到一封感谢信,或者获得赏金。

但是很不幸的是,这个洞最近被修补了,但是在安全开发团队发布补丁的同一天我绕过了补丁,让我们深入研究一下。

利用条件

ChakraCore中的JSObjects

在ChakraCore中,与其他引擎一样,对象的“默认”存储模式使用指向保存属性值的连续内存缓冲区的指针,并使用名为a的对象Type来描述存储给定属性名称的属性值的位置。

JSObject中a对象的布局如下:

· vfptr:虚表指针

· type:保存类型指针

· auxSlots:指向缓冲区保持对象属性的指针

· objectArray:如果对象具有索引属性,则指向JSArray

为了避免在将新属性添加到对象时重新分配和复制先前的属性,auxSlots缓冲区将以特定大小增长,以考虑将来的属性添加问题。

ChakraCore中的JSArrays

使用3种存储来存储数组以允许优化:

· NativeIntArray 其中整数存储在4个字节上的内存位置

· NativeFloatArray 数字以8个字节的形式存储

· JavascritpArray 对象指针会被直接存储

后面介绍数组:)

JIT背景知识

ChakraCore有一个JIT编译器,它有两层优化:

· SimpleJit

· FullJit

FullJit层是执行所有优化的层,重庆并使用直接算法优化函数的控制流图(CFG):

· A backward pass over the graph

· A forward pass

· Another backward pass (called DeadStore pass)

在这些过程中,在每个基本块处收集数据以跟踪关于使用表示JS变量的各种符号的各种信息,但也可以表示内部字段和指针。跟踪的一条信息是暴露的符号,这基本上就可以知道给定的符号是否可以在以后使用。

漏洞分析

该漏洞补丁是在2018年9月发布的。如果我们查看报告,会看到它尝试优化调用的某个指令AdjustObjType并引入一个名为AdjustObjTypeReloadAuxSlotPtr的新指令。

看一下下面的代码段:

 function opt(obj) {
 
     ...
     // assume obj->auxSlots is full at this stage
     
     obj.new_property = 1; // [[ 1 ]]
 
     ...
 }

JIT必须生成一条AdjustObjType指令才能正确增长后面的缓冲区。

这个优化做的是使用暴露的信息来决定它是否应该生成一个AdjustObjType或者AdjustObjTypeReloadAuxSlotPtr,原因是如果该对象上没有更多的属性访问权限,我们就不必重新加载auxSlots指针。

我们可以在下面的方法中看到传递中的特定逻辑

 void
 BackwardPass::InsertTypeTransition(IR::Instr *instrInsertBefore, StackSym *objSym, AddPropertyCacheBucket *data, BVSparse<JitArenaAllocator>* upwardExposedUses)
 {
     Assert(!this->IsPrePass());
 
     IR::RegOpnd *baseOpnd = IR::RegOpnd::New(objSym, TyMachReg, this->func);
     baseOpnd->SetIsJITOptimizedReg(true);
 
     JITTypeHolder initialType = data->GetInitialType();
     IR::AddrOpnd *initialTypeOpnd =
         IR::AddrOpnd::New(data->GetInitialType()->GetAddr(), IR::AddrOpndKindDynamicType, this->func);
     initialTypeOpnd->m_metadata = initialType.t;
 
     JITTypeHolder finalType = data->GetFinalType();
     IR::AddrOpnd *finalTypeOpnd =
         IR::AddrOpnd::New(data->GetFinalType()->GetAddr(), IR::AddrOpndKindDynamicType, this->func);
     finalTypeOpnd->m_metadata = finalType.t;
 
     IR::Instr *adjustTypeInstr =            // [[ 1 ]]
         IR::Instr::New(Js::OpCode::AdjustObjType, finalTypeOpnd, baseOpnd, initialTypeOpnd, this->func); 
 
     if (upwardExposedUses)
     {
         // If this type change causes a slot adjustment, the aux slot pointer (if any) will be reloaded here, so take it out of upwardExposedUses.
         int oldCount;
         int newCount;
         Js::PropertyIndex inlineSlotCapacity;
         Js::PropertyIndex newInlineSlotCapacity;
         bool needSlotAdjustment =
             JITTypeHandler::NeedSlotAdjustment(initialType->GetTypeHandler(), finalType->GetTypeHandler(), &oldCount, &newCount, &inlineSlotCapacity, &newInlineSlotCapacity);
         if (needSlotAdjustment)
         {
             StackSym *auxSlotPtrSym = baseOpnd->m_sym->GetAuxSlotPtrSym();
             if (auxSlotPtrSym)
             {
                 if (upwardExposedUses->Test(auxSlotPtrSym->m_id))
                 {
                     adjustTypeInstr->m_opcode =                 // [[ 2 ]]
                     Js::OpCode::AdjustObjTypeReloadAuxSlotPtr;
                 }
             }
         }
     }
 
     instrInsertBefore->InsertBefore(adjustTypeInstr);
 }

可以看到,默认情况下,它将生成一条AdjustObjType指令,并且只有AdjustObjTypeReloadAuxSlotPtr在测试upwardExposedUses->Test(auxSlotPtrSym->m_id)奇热成功时才将该指令类型做更改。

然后可以看到在Lowerer处理这些特定指令时生成的逻辑:

 void
 Lowerer::LowerAdjustObjType(IR::Instr * instrAdjustObjType)
 {
     IR::AddrOpnd *finalTypeOpnd = instrAdjustObjType->UnlinkDst()->AsAddrOpnd();
     IR::AddrOpnd *initialTypeOpnd = instrAdjustObjType->UnlinkSrc2()->AsAddrOpnd();
     IR::RegOpnd  *baseOpnd = instrAdjustObjType->UnlinkSrc1()->AsRegOpnd();
 
     bool adjusted = this->GenerateAdjustBaseSlots(
         instrAdjustObjType, baseOpnd, JITTypeHolder((JITType*)initialTypeOpnd->m_metadata), JITTypeHolder((JITType*)finalTypeOpnd->m_metadata));
 
     if (instrAdjustObjType->m_opcode == Js::OpCode::AdjustObjTypeReloadAuxSlotPtr)
     {
         Assert(adjusted);
 
         // We reallocated the aux slots, so reload them if necessary.
         StackSym * auxSlotPtr
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值