词条信息

admin
admin
超级管理员
词条创建者 发短消息   

相关词条

热门词条

更多>>
什么是端口?到底是做什么的呢?
端口一般指两种,一种是硬件比如路由器或者交换机的插网线的端口,一种是软件的逻辑的概念,比如http的80端口!...
7种进阶方法让你快速测试端口连通性
Ping是Windows、Linux和Unix系统下的一个检查网络连通性的命令工具,对于大部分互联网用户来说很...
电脑开机,总需要按F1,是什么原因造成的?
一.主板掉电这个说法是行业内的叫法了,一般是主板的CMOS电池没电了导致的。也是最常见的一种提示你按F1的提示...
社保降费对个人有什么影响?
下调城镇职工基本养老保险单位缴费比例是政府给企业发的一个大红包,特别是对于企业来说是一个利好,但是对个人来说有...
车辆“出险”对下年保费的影响,到底有多大?
【出险对交强险的影响】【出险对商业险的影响】车辆“出险”对下年保费的影响,到底有多大?这里有必要先提下车险第三...
解决网 >>所属分类 >> 网络安全   

Safari信息泄露漏洞分析

标签: Safari 信息泄露 漏洞分析

顶[0] 发表评论(0) 编辑词条

目录

前言编辑本段回目录

Javascript中的数组和数组对象一直都是编程人员优化的主要目标,一般来说,数组只会包含一些基本类型数据,比如说32位整数或字符等等。因此,每个引擎都会对这些对象进行某些优化,并提升不同元素类型的访问速度和密集型表示。


Safari信息泄露漏洞分析编辑本段回目录


在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。

 

 

附件列表


按字母顺序浏览:A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

→我们致力于为广大网民解决所遇到的各种电脑技术问题
 如果您认为本词条还有待完善,请 编辑词条

上一篇SD-WAN与VPN:它们有何不同?
下一篇缓冲区溢出漏洞原理

0
1. 本站部分内容来自互联网,如有任何版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
2. 本站内容仅供参考,如果您需要解决具体问题,建议您咨询相关领域专业人士。
3. 如果您没有找到需要的百科词条,您可以到百科问答提问或创建词条,等待高手解答。

关于本词条的提问

查看全部/我要提问>>