内容简介:Javascript中的数组和数组对象一直都是编程人员优化的主要目标,一般来说,数组只会包含一些基本类型数据,比如说32位整数或字符等等。因此,每个引擎都会对这些对象进行某些优化,并提升不同元素类型的访问速度和密集型表示。在JavaScriptCore中,JavaScript引擎是在WebKit中实现的,其中每一个存储在对象中的元素都代表着一个IndexingType值,一个8位整数代表一套Flag组合,具体的参数定义可以在IndexingType.h中找到。接下来,引擎会检测一个对象中indexing的
前言
Javascript中的数组和数组对象一直都是编程人员优化的主要目标,一般来说,数组只会包含一些基本类型数据,比如说32位整数或字符等等。因此,每个引擎都会对这些对象进行某些优化,并提升不同元素类型的访问速度和密集型表示。
在JavaScriptCore中,JavaScript引擎是在WebKit中实现的,其中每一个存储在对象中的元素都代表着一个IndexingType值,一个8位整数代表一套Flag组合,具体的参数定义可以在IndexingType.h中找到。接下来,引擎会检测一个对象中indexing的类型,然后决定使用哪一条快速路径,其中最重要的一种indexing类型就是ArrayWithUndecided,它表示的是所有元素均为未定义(undefined),而且没有存储任何实际的值。在这种情况下,引擎为了提升性能,会让这些元素保持未初始化。
分析
下面,我们一起看一看旧版本中实现Array.prototype.concat的代码( ArrayPrototype.cpp ):
EncodedJSValueJSC_HOST_CALL arrayProtoPrivateFuncConcatMemcpy(ExecState* exec) { ... unsigned resultSize =checkedResultSize.unsafeGet(); IndexingType firstType =firstArray->indexingType(); IndexingType secondType =secondArray->indexingType(); IndexingType type =firstArray->mergeIndexingTypeForCopying(secondType); // [[ 1 ]] if (type == NonArray ||!firstArray->canFastCopy(vm, secondArray) || resultSize >=MIN_SPARSE_ARRAY_INDEX) { ... } JSGlobalObject* lexicalGlobalObject =exec->lexicalGlobalObject(); Structure* resultStructure =lexicalGlobalObject->arrayStructureForIndexingTypeDuringAllocation(type); if(UNLIKELY(hasAnyArrayStorage(resultStructure->indexingType()))) return JSValue::encode(jsNull()); ASSERT(!lexicalGlobalObject->isHavingABadTime()); ObjectInitializationScopeinitializationScope(vm); JSArray* result =JSArray::tryCreateUninitializedRestricted(initializationScope, resultStructure,resultSize); if (UNLIKELY(!result)) { throwOutOfMemoryError(exec, scope); return encodedJSValue(); } if (type == ArrayWithDouble) { [[ 2 ]] double* buffer =result->butterfly()->contiguousDouble().data(); memcpy(buffer,firstButterfly->contiguousDouble().data(), sizeof(JSValue) *firstArraySize); memcpy(buffer + firstArraySize,secondButterfly->contiguousDouble().data(), sizeof(JSValue) *secondArraySize); } else if (type != ArrayWithUndecided) { ...
这个函数主要用来判断结果数组[[1]]的indexing类型,我们可以看到,如果indexing类型为ArrayWithDouble,它将会选择[[2]]作为快速路径。接下来,我们看一看:
mergeIndexingTypeForCopying的实现代码,这个函数主要负责在Array.prototype.concat被调用时,判断结果数组的indexing类型:
inlineIndexingType JSArray::mergeIndexingTypeForCopying(IndexingType other) { IndexingType type = indexingType(); if (!(type & IsArray && other& IsArray)) return NonArray; if (hasAnyArrayStorage(type) ||hasAnyArrayStorage(other)) return NonArray; if (type == ArrayWithUndecided) return other; [[ 3 ]] ...
我们可以看到在这种情况下,有一个输入数组的indexing类型为ArrayWithUndecided,结果indexing类型将会是另一个数组的indexing类型。因此,如果我们我们用一个indexing类型为ArrayWithUndecided的数组和另一个indexing类型为ArrayWithDouble的数组去调用Array.prototype.concat方法的话,我们将会按照快速路径[[2]]运行,并将两个数组进行拼接。
这段代码并不能保证这两个“butterfly”(JavaScript引擎攻击技术里的一种概念,详情请参考【 这篇文章】 )在代码调用memcpy之前能够正确初始化。这也就意味着,如果我们能够找到一条允许我们创建一个未初始化数组并将其传递给Array.prototype.concat的代码路径,那我们就能够在堆内存中拥有一个包含了未初始化值的数组对象了,而且它的indexing类型还不是ArrayWithUndecided。从某种程度上来说,这个安全问题跟lokihardt在2017年报告的一个 旧漏洞 有些相似,只不过利用方式不同。
在创建这种数组对象时,可以利用NewArrayWithSize DFG JIT的操作码来实现,在对FTLLowerDFGToB3.cpp中FTL所实现的allocateJSArray操作码进行分析之后,我们可以看到这个数组将会包含未初始化的值。引擎根本不需要对数组进行初始化,因为这个数组的indexing类型为ArrayWithUndecided。
ArrayValuesallocateJSArray(LValue publicLength, LValue vectorLength, LValue structure,LValue indexingType, bool shouldInitializeElements = true, boolshouldLargeArraySizeCreateArrayStorage = true) { [ ... ] initializeArrayElements( indexingType, shouldInitializeElements ?m_out.int32Zero : publicLength, vectorLength, butterfly); ... voidinitializeArrayElements(LValue indexingType, LValue begin, LValue end, LValuebutterfly) { if (begin == end) return; if (indexingType->hasInt32()) { IndexingType rawIndexingType =static_cast<IndexingType>(indexingType->asInt32()); if (hasUndecided(rawIndexingType)) return; // [[ 4 ]]
语句new Array(n)在被FTL JIT编译时将会触发[[4]],然后返回一个indexing类型为ArrayWithUndecided的数组,其中就包含未初始化的元素。
漏洞利用
清楚了之前所介绍的漏洞原理之后,想必触发这个漏洞也并非难事:我们可以不断重复调用一个使用new Array()方法来创建数组的函数,然后调用concat方法将这个数组和一个只包含double类型数据的数组进行拼接。在调用够足够次数之后,FTL编译器将会对其进行编译。
这份【 漏洞利用代码 】可以利用这个漏洞来泄漏一个目标对象的内存地址,实现机制是通过我们所创建的对象进行内存喷射,在触发这个漏洞之后,我们就能够从代码所返回的数组中找到目标对象的地址了。
总结
这个漏洞目前已经在iOS 12和macOS Mojave的最新版本(Safari)中修复了,该漏洞的CVE编号为 CVE-2018-4358 。
* 参考来源: phoenhex ,FB小编Alpha_h4ck编译,转载请注明来自FreeBuf.COM
以上所述就是小编给大家介绍的《Safari信息泄露漏洞分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 导致数据库凭据泄露:详细分析Jenkins Swarm、Ansible、GitLab插件信息泄露漏洞(CVE-2019-10309/10...
- CVE-2018-9581分析:安卓RSSI广播敏感信息泄露漏洞
- 多种设备基于 SNMP 协议的敏感信息泄露漏洞数据分析报告
- 漏洞分析:OpenSSH用户枚举漏洞(CVE-2018-15473)分析
- 【漏洞分析】CouchDB漏洞(CVE–2017–12635, CVE–2017–12636)分析
- 【漏洞分析】lighttpd域处理拒绝服务漏洞环境从复现到分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
用UML构建Web应用
科纳尔伦 (Conallen Jim) / 陈起 / 中国电力出版社 / 2003-11 / 39.0
用UML构建Web应用(第2版),ISBN:9787508315577,作者:(美)Jim Conallen著;陈起,英宇译;陈起译一起来看看 《用UML构建Web应用》 这本书的介绍吧!