CVE-2019-0609: Edge use-after-unmap漏洞分析
字数 1473 2025-08-27 12:33:31

CVE-2019-0609: Edge浏览器use-after-unmap漏洞深度分析

漏洞概述

CVE-2019-0609是Microsoft Edge浏览器中ChakraCore JavaScript引擎的一个安全漏洞,属于use-after-unmap类型。该漏洞源于JavaScript引擎在处理栈函数boxing时的缺陷,可能导致内存破坏和远程代码执行。

背景知识

JavaScript引擎中的函数分配

在JavaScript中,函数是第一类对象,可以像其他值一样被传递和返回。当函数在全局代码或其他函数中声明时,JavaScript引擎通常会在栈上分配这些函数对象。但在某些情况下,需要将这些函数移动到堆中:

  1. 当函数被多个指针引用时
  2. 当函数的作用域需要逃逸出原始声明范围时(如函数返回内部声明的函数)

这种从栈到堆的移动过程称为"boxing",类似于Java中的装箱(boxing)概念。

漏洞详细分析

漏洞触发机制

漏洞的核心在于ChakraCore引擎对栈函数的boxing处理不当。当满足以下条件时,会导致use-after-unmap漏洞:

  1. 函数在栈上分配
  2. 函数需要被box(如被返回或跨作用域引用)
  3. 函数的栈分配在私有区域而非原生栈

关键代码分析

问题代码路径

StackScriptFunction::BoxState::Box函数中,存在以下关键断言:

StackScriptFunction *stackFunction = interpreterFrame->GetStackNestedFunction(i);  
ScriptFunction *boxedFunction = this->BoxStackFunction(stackFunction);  
Assert(stackFunction->boxedScriptFunction == boxedFunction);  
this->UpdateFrameDisplay(stackFunction);

当断言失败时,表明boxing过程出现问题:stackFunction->boxedScriptFunction为nullptr,而boxedFunction指向有效对象。

Boxing失败原因

BoxStackFunction函数的实现如下:

ScriptFunction * StackScriptFunction::BoxState::BoxStackFunction(ScriptFunction * scriptFunction)
{
    // Box the frame display first, which may in turn box the function
    FrameDisplay * frameDisplay = scriptFunction->GetEnvironment();
    FrameDisplay * boxedFrameDisplay = BoxFrameDisplay(frameDisplay);

    if (!ThreadContext::IsOnStack(scriptFunction))
    {
        return scriptFunction;
    }
    // ...
    boxedFunction = ScriptFunction::OP_NewScFunc(boxedFrameDisplay,
        reinterpret_cast<FunctionInfoPtrPtr>(&functionInfo));
    stackFunction->boxedScriptFunction = boxedFunction;

问题在于ThreadContext::IsOnStack检查:当函数分配在私有区域而非原生栈时,该检查会返回false,导致函数未被正确box。

根本原因

InterpreterStackFrame::InterpreterHelper中,引擎会根据本地变量数量决定分配方式:

if (varAllocCount > InterpreterStackFrame::LocalsThreshold)

当本地变量超过阈值(LocalsThreshold)时,引擎会分配私有区域作为栈框架。然而:

  1. ThreadContext::IsOnStack不将私有区域视为栈框架
  2. 导致需要box的函数未被正确box
  3. 函数返回后,栈被unmap,但未box的函数仍指向原栈地址
  4. 后续访问导致use-after-unmap

补丁分析

ChakraCore 1.11.7中修复了该漏洞,关键补丁如下:

if (stackVarAllocCount != 0)
{
    size_t stackVarSizeInBytes = stackVarAllocCount * sizeof(Var);
    PROBE_STACK_PARTIAL_INITIALIZED_INTERPRETER_FRAME(GetScriptContext(), 
        Js::Constants::MinStackInterpreter + stackVarSizeInBytes);
    stackAllocation = (Var*)_alloca(stackVarSizeInBytes);
}

补丁的主要改进:

  1. 显式计算stackVarAllocCount作为boxing决策依据
  2. 使用_alloca确保stackScriptFunctions在堆中分配
  3. 避免了私有区域分配导致的boxing失败

漏洞利用(PoC)

以下是触发该漏洞的概念验证代码:

function test() {
    function a() {
        function d() {
          let e = function() {};
          return e;
        }
        function b() {
            let fun_d = [d];
            return fun_d;
        }
        // 使用足够大的对象触发私有区域分配
        var obj = [/* big-size object */];
        return b();
    }
    return a();
}

var f = test();

function test1() {
    // 重新分配内存以触发use-after-unmap
    var obj = [/* big-size object */];
    print(f[0]); // 未box的函数d仍指向栈地址
}

test1();

漏洞影响与缓解

影响

  1. 可能导致远程代码执行
  2. 影响Microsoft Edge浏览器(使用ChakraCore引擎的版本)
  3. 可结合其他漏洞实现完整攻击链

缓解措施

  1. 更新到已修复的Edge浏览器版本
  2. 启用适当的沙箱保护机制
  3. 禁用不必要的JavaScript功能

总结

CVE-2019-0609展示了JavaScript引擎实现中内存管理的复杂性。该漏洞的关键在于:

  1. 栈分配策略与boxing逻辑的不一致
  2. 内存区域检测的不完备性
  3. 作用域逃逸处理中的边界条件

理解此类漏洞有助于开发者更好地设计安全的内存管理策略,也为安全研究人员提供了分析类似漏洞的参考框架。

CVE-2019-0609: Edge浏览器use-after-unmap漏洞深度分析 漏洞概述 CVE-2019-0609是Microsoft Edge浏览器中ChakraCore JavaScript引擎的一个安全漏洞,属于use-after-unmap类型。该漏洞源于JavaScript引擎在处理栈函数boxing时的缺陷,可能导致内存破坏和远程代码执行。 背景知识 JavaScript引擎中的函数分配 在JavaScript中,函数是第一类对象,可以像其他值一样被传递和返回。当函数在全局代码或其他函数中声明时,JavaScript引擎通常会在栈上分配这些函数对象。但在某些情况下,需要将这些函数移动到堆中: 当函数被多个指针引用时 当函数的作用域需要逃逸出原始声明范围时(如函数返回内部声明的函数) 这种从栈到堆的移动过程称为"boxing",类似于Java中的装箱(boxing)概念。 漏洞详细分析 漏洞触发机制 漏洞的核心在于ChakraCore引擎对栈函数的boxing处理不当。当满足以下条件时,会导致use-after-unmap漏洞: 函数在栈上分配 函数需要被box(如被返回或跨作用域引用) 函数的栈分配在私有区域而非原生栈 关键代码分析 问题代码路径 在 StackScriptFunction::BoxState::Box 函数中,存在以下关键断言: 当断言失败时,表明boxing过程出现问题: stackFunction->boxedScriptFunction 为nullptr,而 boxedFunction 指向有效对象。 Boxing失败原因 BoxStackFunction 函数的实现如下: 问题在于 ThreadContext::IsOnStack 检查:当函数分配在私有区域而非原生栈时,该检查会返回false,导致函数未被正确box。 根本原因 在 InterpreterStackFrame::InterpreterHelper 中,引擎会根据本地变量数量决定分配方式: 当本地变量超过阈值( LocalsThreshold )时,引擎会分配私有区域作为栈框架。然而: ThreadContext::IsOnStack 不将私有区域视为栈框架 导致需要box的函数未被正确box 函数返回后,栈被unmap,但未box的函数仍指向原栈地址 后续访问导致use-after-unmap 补丁分析 ChakraCore 1.11.7中修复了该漏洞,关键补丁如下: 补丁的主要改进: 显式计算 stackVarAllocCount 作为boxing决策依据 使用 _alloca 确保 stackScriptFunctions 在堆中分配 避免了私有区域分配导致的boxing失败 漏洞利用(PoC) 以下是触发该漏洞的概念验证代码: 漏洞影响与缓解 影响 可能导致远程代码执行 影响Microsoft Edge浏览器(使用ChakraCore引擎的版本) 可结合其他漏洞实现完整攻击链 缓解措施 更新到已修复的Edge浏览器版本 启用适当的沙箱保护机制 禁用不必要的JavaScript功能 总结 CVE-2019-0609展示了JavaScript引擎实现中内存管理的复杂性。该漏洞的关键在于: 栈分配策略与boxing逻辑的不一致 内存区域检测的不完备性 作用域逃逸处理中的边界条件 理解此类漏洞有助于开发者更好地设计安全的内存管理策略,也为安全研究人员提供了分析类似漏洞的参考框架。