USDT自动API接口

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

当开发人员决议使用新的Torque语言重新实现两个CodeStubAssembler(CSA)函数时,V8中就会泛起破绽。这两个函数用于在JavaScript中建立新的FixedArray和FixedDoubleArray工具,只管新的实现乍看之下是有用的,但它们缺少一个要害组成部门:更大长度检查,以确保新建立的数组的长度不会跨越预界说的上限。

在一般人看来,这个缺失的长度检查看起来很正常,但对于攻击者来说,就可以行使了TurboFan的typer来获取接见权限中的一个异常壮大的行使原语:数组,其长度字段远大于其容量。该原语为攻击者提供了V8堆上的越界接见原语,这很容易导致代码执行。

破绽实现历程

若是要继续,则需要构建V8版本8.5.51(提交64cadfcf4a56c0b3b9d3b5cc00905483850d6559),建议使用完整符号举行构建(修改args.gn并添加symbol_level = 2行)。

在x64.release目录中,你可以使用以下下令以零编译器优化来编译刊行版本:

find . -type f -exec grep '\-O3' -l {} ";" -exec sed -i 's/\-O3/\-O0/' {} ";" -ls

若是你想继续阅读本博文中的一些代码示例,我仍然建议你构建通俗刊行版(启用编译器优化)。若是不举行优化,某些示例将破费异常长的时间才气运行。

从上面链接的bugtracker中获取观点证实。

2017年之前的V8

在2017年之前,许多JavaScript内置函数(Array.prototype.concat,Array.prototype.map等)都是用JavaScript自己编写的,只管这些函数使用了TurboFan(V8的推测性优化编译器,稍后将举行详细说明)。为了更大限度地施展性能,它们的运行速率基本没有使用本机代码编写的速率快。

对于最常见的内置函数,开发人员将以手写汇编形式编写异常优化的版本。之以是可行,由于ECMAScript规范(点击查看示例)中对这些内置函数的形貌异常详细。然则,它有一个很大的瑕玷:V8针对大量平台和体系结构,这意味着V8开发人员必须为每个体系结构编写和重写所有这些优化的内置函数。随着ECMAScript尺度的不停发展和新语言函数的不停尺度化,维护所有这些手写程序集变得异常繁琐且容易失足。

遇到此问题后,开发人员更先寻找更好的解决方案。直到TurboFan引入V8才找到解决方案。

CODESTUBASSEMBLER

TurboFan为低层指令带来了跨平台的中心示意(IR),V8团队决议在TurboFan之上构建一个新的前端,他们将其称为CodeStubAssembler。 CodeStubAssembler界说了一种可移植的汇编语言,开发人员可以使用该语言来实现优化的内置函数。最主要的是,可移植汇编语言的跨平台性子意味着开发人员只需编写一次内置函数即可。所有支持的平台和体系结构的现实本机代码都由TurboFan举行编译,你可以在此处阅读有关CSA的更多信息。

只管这是一个很大的改善,但仍然存在一些问题。使用CodeStubAssembler的语言编写更佳代码需要开发人员积累许多专业知识。纵然掌握了所有这些知识,仍然存在许多容易导致安全破绽的异常规破绽,这导致V8团队最终编写了一个称为Torque的新组件。

Torque

Torque是基于CodeStubAssembler构建的语言前端,它具有类似TypeScript的语法,壮大的类型系统和壮大的破绽检查函数,所有这些使得它成为V8开发人员编写内置函数的理想选择。Torque编译器使用CodeStubAssembler将Torque代码转换为有用的汇编代码,它极大地减少了安全破绽的数目,你可以在此处阅读更多有关Torque的信息。

破绽发生的缘故原由

由于Torque仍相对较新,因此仍然需要重新实现大量的CSA代码。其中包罗用于处置建立新的FixedArray和FixedDoubleArray工具的CSA代码,它们是V8中的“快”数组(“快”数组具有延续的数组后备存储,而“慢”数组具有基于字典的后备存储)。

破绽行使

开发人员将CodeStubAssembler :: AllocateFixedArray函数重新实现为两个Torque宏,一个用于FixedArray工具,另一个用于FixedDoubleArray工具:

若是将上述函数与CodeStubAssembler :: AllocateFixedArray变体举行对照,则会发现缺少更大长度检查。

NewFixedArray应确保返回的新FixedArray的长度小于FixedArray :: kMaxLength,即0x7fffffd或134217725。

同样,NewFixedDoubleArray应该凭据FixedDoubleArray :: kMaxLength(为0x3fffffe或67108862)检查数组的长度。

在我们研究若何使用缺少的长度检查之前,让我们先领会一下Sergey是若何Trigger此破绽的,由于它不像建立一个大于kMaxLength的新数组那样简朴。

V8中的数组

现在,我们需要更多地领会V8中数组的示意方式。

内存中的数组

让我们以数组[1、2、3、4]为例,并在内存中查看它。你可以通过运行带有--allow-natives-syntax标志的V8,建立数组并执行%DebugPrint(array)来获取其地址来实现此目的,使用GDB查看内存中的地址。

在V8中分配数组时,它现实上分配了两个工具。请注意,每个字段的长度为4字节/ 32位:

*** Array工具是现实的数组,它包罗四个主要字段以及其他一些不主要的字段。

1.映射指针:这决议了数组的“形状”,详细来说,它确定数组存储哪种元素,以及其后备存储工具是什么类型。在这种情况下,我们的数组存储整数,后备存储区为FixedArray。

2.属性指针:指向存储数组可能具有的任何属性的工具。在本例中,数组除了长度以外没有任何属性,长度被内联存储在 *** Array工具自己中。

3.元素指针:指向存储数组元素的工具。这也称为后备存储,在本例中,后备存储指向FixedArray工具,稍后会详细先容。

4.数组长度:这是数组的长度。在研究人员那公布的观点证实中,这是他将长度字段笼罩为0x24242424,然后允许他举行越界读取和写入。

*** Array工具的元素指针指向后备存储,这是一个FixedArray工具,有两个要害的事情要记着:

1.不需要思量FixedArray中的后备存储长度,你可以将其笼罩为任何值,但仍然无法读取或写入界限。

2.每个索引存储在数组的元素上,内存中值的示意形式取决于数组的“元素种类”,而数组的“元素种类”则取决于原始 *** Array工具的映射。在本例中,这些值是一个小的整数,它们是31位整数,其更低位设置为零。 1示意为1 << 1 = 2,2示意为2 << 1 = 4,依此类推。

,

usdt支付接口

菜宝钱包(www.caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,

元素种类

V8中的数组也具有“元素种类”的观点,你可以在此处找到所有元素种类的列表,但所有表的基本思想都是一样的:在V8中每次建立数组时,都市用元素种类符号它,该种类界说了数组包罗的元素类型。最常见的三种元素如下:

PACKED_ *** I_ELEMENTS:数组被压缩而且仅包罗Smis(31位小整数,第32位设置为0)。

PACKED_DOUBLE_ELEMENTS:与上面相同,但为双精度(64位浮点值)。

PACKED_ELEMENTS:与上面相同,但数组仅包罗引用。这意味着它可以包罗任何类型的元素(整数,双精度数,工具等)。

数组也可以在元素类型之间举行转换,然则转换只能针对更通用的元素类型,而不能针对更详细的元素类型。例如,具有PACKED_ *** I_ELEMENTS类型的数组可以转换为HOLEY_ *** I_ELEMENTS类型,但不能转换为HOLEY_ *** I_ELEMENTS类型,即填充已经有孔的数组中的所有孔都不会导致转换为压缩元素类型的变体。

下面的图表展示了最常见的元素类型的转换格:

我们现实上只体贴与元素类型有关的两件事:

1. *** I_ELEMENTS和DOUBLE_ELEMENTS类型的数组将其元素存储在延续的数组后备存储中,作为它们在内存中的现实示意形式。例如,数组[1.1、1.1、1.1]会将0x3ff199999999999a存储在内存中三个元素的延续数组中(0x3ff199999999999a是1.1的IEEE-754示意形式)。另一方面,PACKED_ELEMENTS类型的数组将存储对HeapNumber工具的三个延续引用,这些引用又包罗1.1的IEEE-754示意形式。另有基于字典的备份存储的元素种类,但这不是本文的重点。

2.由于 *** I_ELEMENTS和DOUBLE_ELEMENTS类数组的元素巨细差别(Smis是31位整数,而双精度是64位浮点型值),以是它们也具有差别的kMaxLength值。

观点验证

Sergey提供了两个观点证实:第一个为我们提供了一个数组,类型为HOLEY_ *** I_ELEMENTS类型,长度为FixedArray :: kMaxLength + 3,而第二个为我们提供了一个数组,类型为HOLEY_DOUBLE_ELEMENTS类型,长度为FixedDoubleArray :: kMaxLength + 1。他仅行使第二个观点证实来组织最终的越界接见原语。

两种观点证实都使用Array.prototype.concat来首先获得一个数组,该数组的巨细正好小于响应元素类型的kMaxLength值。完成此操作后,将使用Array.prototype.splice向数组中添加更多元素,这会导致其长度增加到kMaxLength以上。之以是可行,是由于Array.prototype.splice的快路径间接使用了新的Torque函数,若是原始数组不够大,则会分配一个新数组。出于好奇,实现此目的的函数挪用链可能如下:

你可能想知道为什么不能建立一个巨细恰好低于FixedArray::kMaxLength的大数组并使用它。让我们实验一下(使用优化的刊行版守候的时间会短一些):

这不仅需要破费一些时间,而且还会收到OOM(内存不足)破绽!发生这种破绽的缘故原由是,数组分配不会是一次性完成的。对AllocateRawFixedArray的挪用许多,每个挪用都分配一个稍大的数组。你可以通过在AllocateRawFixedArray上设置断点,然后如上所述分配数组,来在GDB中看到这一点。我不完全确定为什么V8会这样做,然则许多分配都市导致V8很快耗尽内存。

我的另一个想法是改用FixedDoubleArray :: kMaxLength,由于它要小得多(使用优化的刊行版):

这确实有用,由于它会返回一个新的HOLEY_DOUBLE_ELEMENTS类型的数组,其长度设置为FixedDoubleArray :: kMaxLength + 1,因此可以使用它取代array .prototype.concat。我信赖这样做的缘故原由是由于分配巨细为0x3fffffd的数组所需的分配数目足够小,以至于不会不会导致引擎进入OOM。

然则,此方式有两个瑕玷:分配和填充重大的数组需要破费大量时间,因此在破绽行使中并不理想。另一个问题是,实验在内存受限的环境(例如旧手机)中以这种方式Trigger破绽,可能会导致引擎运行OOM。

另一方面,Sergey的第一个观点证实在我的盘算机上破费了约莫2秒钟,而且内存效率很高。以下是详细剖析历程。

第一个观点证实

第一个观点证实如下,请确保你使用优化的刊行版版原本运行它,否则将需要很长时间才气完成:

让我们一步一步来剖析,在[1]的位置,建立了一个巨细为0x80000的数组,并使用1填充。这种巨细的数组需要分配大量内存,但险些没有使引擎成为OOM的条件。由于数组最初是空的,因此它获得的是HOLEY_ *** I_ELEMENTS类型,纵然将其填充为1也会保留该元素的类型。

我们稍后会回到[2],然则在[3]中,使用0xff元素建立了一个新的args数组,每个元素都被设置为在[1]处建立的数组。这使args数组总共为0xff * 0x80000 = 0x7f80000个元素。在[4]处,将另一个巨细为0x7fffc的数组压入args数组,这使其总数为0x7f80000 + 0x7fffc = 0x7fffffc个元素,0x7fffffc仅比FixedDoubleArray :: kMaxLength = 0x7fffffd小1。

在[5],Array.prototype.concat.apply将args数组中的每个元素毗邻到空数组[],你可以在此处阅读有关Function.prototype.apply()若何事情的更多信息,但它现实上将args视为参数数组,并将每个元素毗邻为最终的数组。我们知道元素总数为0x7fffffc,因此最终数组将具有那么多元素。这种毗邻发生得对照快(在我的装备上约莫需要2秒钟),只管它比我前面演示的简朴地建立数组要快得多。

最后,在[6]处,Array.prototype.splice向该数组追加了4个分外的元素,这意味着其长度现在为0x8000000,即FixedArray :: kMaxLength + 3。

唯一需要说明的是[2],其中将属性添加到原始数组。要领会这一点,你必须首先领会险些所有V8内置函数的约定是有一个快路径和一个慢路径。在Array.prototype.concat的情况下,接纳慢路径的一种简朴方式是向要毗邻的数组添加属,快路径具有以下代码:

可以看到,快路径检查确保最终数组的长度不跨越kMaxLength值。由于FixedDoubleArray::kMaxLength是FixedArray::kMaxLength的一半,以是上述观点证实将永远不会通过此检查。随意实验在不使用array.prop = 1的情况下运行代码;看看会发生什么!

另一方面,慢路径(Slow_ArrayConcat)没有任何长度检查(然则,若是长度跨越FixedArray :: kMaxLength,它仍然会溃逃并发生致命的OOM破绽,由于它挪用的其中一个函数仍然会检查长度。这就是为什么研究者会使用慢路径的缘故原由,由于可以绕过快路径中存在的检查。

第二个观点证实(第一部门)

只管第一个观点证实演示了破绽,而且可以用于行使(在第二个观点证实中,你只需稍微修改一下trigger函数),但它需要几秒钟才气完成,这可能也不太理想。Sergey选择使用HOLEY_DOUBLE_ELEMENTS类数组。这可能是由于FixedDoubleArray::kMaxLength值显著小于它的FixedArray变体,从而导致更快的Trigger。若是你理解了第一个观点证实,,那么第二个观点证实的第一部门的以下注释版本就很好理解了:

此时,giant_array的长度为0x3ffffff,即FixedDoubleArray :: kMaxLength +1。现在的问题是,我们若何在破绽行使程序中使用此数组?我们没有任何有用的原语,因此我们需要找到引擎的其他部门,这些部门的代码取决于数组长度不能跨越kMaxLength值。

对于大多数研究人员来说,该破绽自己确实很容易被发现,由于你只需要将函数的新的Torque实现与旧的实现举行对照即可。只管知道若何行使它,但需要对V8自己有更深入的领会。 Sergey接纳的行使途径行使了V8的推测性优化编译器TurboFan,它需要自己引入。

本文翻译自:https://www.elttam.com/blog/simple-bugs-with-complex-exploits/,content: Allbet声明:该文看法仅代表作者自己,与www.allbetgame.us无关。转载请注明:usdt币在哪里交易(www.caibao.it):一个有关 V8 破绽的细节剖析 (一)
发布评论

分享到:

跑分usdt(www.caibao.it):西甲是哪个国家的?陶菲克、丹琳和李宗伟,哪一个应该成为羽毛球运发动的楷模?高清图:内马尔戴帽比三庆祝 姆巴佩点球欧冠破荒_欧博开户网址_ALLbet6.com
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。