
CVE-2023-41993 的概念验证漏洞利用程序,针对 Safari 中 WebKit JIT 的类型混淆漏洞。通过堆操作和 GetterSetter 混淆提供 addrof/fakeobj 原语,从而在 WebContent 进程中实现任意读写。
针对 CVE-2023-41993 的 PoC 漏洞利用代码。 它只实现了到 addrof/fakeobj 的部分。 可靠性不太好。 如果你想改进它,可以尝试喷射 structure ID。
https://po6ix.github.io/POC-for-CVE-2023-41993/pwn.html
由于多次请求,我决定用 GitHub Pages 来托管。
祝我好运,希望 GitHub 不会封我……
这是因为 pwn 函数中定义的 factor 值不适合你的设备。
针对这种情况,我让它使用 87 到 1088 之间的随机值。
所以你只需刷新几次就能找到正确的 factor 值。
从概率上讲,通常会在 100 次尝试内成功。
如果你能把成功案例中显示的信息发给我,那就更好了。
这能为你提供针对 Safari webcontent 进程的读/写原语。
但要真正利用它,你还需要与其他组件进行串联。
你可能想要一份详细的 writeup。 但遗憾的是,我抽不出时间写那东西。 所以我在这里记一些笔记,让你能理解它的工作原理。
如果你看过那个 commit,它涉及的是对 HeapLocation 的更改。
新增了一个 factor,用来判断节点是否相同。
它告诉我们,像 GetByOffset、MultiGetByOffset 这样的节点可能会被混淆。
但实际上这只是关于偏移(offset)的问题。
假设有两个偏移不同的 GetByOffset 节点。
其中一个会被 CSE 消除,剩下的那个会被使用。
所以这基本上是一种偏移混淆,但它并不能让你访问任意偏移。
因为要对此类节点进行 CSE,它们需要被 LICMPhase 提升(hoist)。
此阶段不允许提升执行写操作的节点。
因此 PutByOffset、MultiPutByOffset 节点不会发生同样的混淆。
另外,当 GetByOffset 被提升时,会调用 safeToExecute 函数来检查该节点是否合法可执行,并且只允许访问小于存储容量(inline/ool)的偏移。
因此,利用这个漏洞的思路是 GetterSetter。
如果你调用 Object.__defineGetter__ 定义一个属性,会创建 GetterSetter 对象,但它存储在属性存储中,通常无法访问。
但你可以利用这个偏移操作来访问它。
然后调用 Object 函数来触发类型混淆。
JSObject* JSCell::toObjectSlow(JSGlobalObject* globalObject) const
{
Integrity::auditStructureID(structureID());
ASSERT(!isObject());
if (isString())
return static_cast<const JSString*>(this)->toObject(globalObject);
if (isHeapBigInt())
return static_cast<const JSBigInt*>(this)->toObject(globalObject);
ASSERT(isSymbol());
return static_cast<const Symbol*>(this)->toObject(globalObject);
}
它会创建一个 SymbolObject,其内部值为 GetterSetter。 这是不正确的,因为 SymbolObject 的内部值本应是 Symbol 实例,而不是 GetterSetter。
let getterSetter = jitme(1);
let symbolObject = Object(getterSetter);
symbolObject.description; // call the getter
String Symbol::description() const
{
auto& uid = m_privateName.uid();
return uid.isNullSymbol() ? String() : uid;
}
然后当你调用 description getter 时,它会返回一个 String 实例。 这是 Symbol.m_privateName 与 GetterSetter.m_getter 之间的类型混淆。 每次调用这个 getter,它都会增加 m_privateName.m_uid 的引用计数字段,该字段位于偏移量 0x0 处。 这非常有用,因为这个偏移量正是 getter 函数 structure ID 所在的位置。 多次调用这个函数,你就可以改变 JSFunction 实例的 structure ID。 我准备了另一个具有很多属性的类型。 然后如果你把 structure ID 同步为与它相同,就可以对属性存储进行越界写入。 这直接提供了 addrof/fakeobj 原语。