Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
CVE-2026-2763-POC — CVE-2026-2763 的概念验证漏洞利用程序,针对 Mozilla JavaScript 引擎中的释放后使用(use-after-free)漏洞,演示了受约束的 1 位写入原语,进而导致越界读写。 | Kitploit
工具/GitHubGitHub/ppwwiinn/cve-2026-2763-poc
内存取证漏洞分析漏洞利用Web安全二进制利用
GitHubppwwiinn/cve-2026-2763-poc

CVE-2026-2763-POC

CVE-2026-2763 的概念验证漏洞利用程序,针对 Mozilla JavaScript 引擎中的释放后使用(use-after-free)漏洞,演示了受约束的 1 位写入原语,进而导致越界读写。

查看仓库
25个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

漏洞

该漏洞源于 for-in 语法的实现。

for-in 循环的作用域由一对字节码表示:JSOP::Iter 和 JSOP::EndIter。

root@kitploit:~
CASE(Iter) {
      MOZ_ASSERT(REGS.stackDepth() >= 1);
      HandleValue val = REGS.stackHandleAt(-1);
      JSObject* iter = ValueToIterator(cx, val); // [1]
      if (!iter) {
        goto error;
      }
      REGS.sp[-1].setObject(*iter); // [2]
    }

...

CASE(EndIter) {
      MOZ_ASSERT(REGS.stackDepth() >= 2);
      CloseIterator(&REGS.sp[-2].toObject());
      REGS.sp -= 2; // [3]
    }

在作用域入口处,在 [1] 处调用 ValueToIterator 来创建一个 PropertyIteratorObject。解释器随后在 [2] 处将其存储(压入)到解释器栈上。当作用域结束时,在 [3] 处移除该 PropertyIteratorObject。

root@kitploit:~
static PropertyIteratorObject* GetIteratorImpl(JSContext* cx, HandleObject obj,
                                               bool wantIndices,
                                               bool forObjectKeys) {
...
  PropertyIteratorObject* iterobj = CreatePropertyIterator(
      cx, obj, keys, supportsIndices, indicesPtr, cacheableProtoChainLength,
      ownPropertyCount, forObjectKeys); // [1]
  if (!iterobj) {
    return nullptr;
  }
  if (!forObjectKeys) {
    RegisterEnumerator(cx, iterobj->getNativeIterator(), obj); // [2]
  }
...
  return iterobj;
}

static inline void RegisterEnumerator(JSContext* cx, NativeIterator* ni,
                                      HandleObject obj) {
  ni->initObjectBeingIterated(*obj);

  // Register non-escaping native enumerators (for-in) with the current
  // context.
  ni->link(cx->compartment()->enumeratorsAddr());

  MOZ_ASSERT(!ni->isActive());
  ni->markActive();
}

在 ValueToIterator 内部,对象创建过程如上所示。在 [1] 处分配迭代器对象后,在 [2] 处将其链接到一个全局链表中。此时,对 PropertyIteratorObject 的唯一引用保存在解释器栈上。

root@kitploit:~
void js::CloseIterator(JSObject* obj) {
  if (!obj->is<PropertyIteratorObject>()) {
    return;
  }

  // Remove iterator from the active list, which is a stack. The shared iterator
  // used for for-in with null/undefined is immutable and unlinked.

  NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
  if (ni->isEmptyIteratorSingleton()) {
    return;
  }

  ni->unlink();

  MOZ_ASSERT(ni->isActive());
  ni->markInactive();

  ni->clearObjectBeingIterated();

  // Reset the enumerator; it may still be in the cached iterators for
  // this thread and can be reused.
  ni->resetPropertyCursorForReuse();
}

当作用域结束时,会调用 CloseIterator,将迭代器从之前链接的链表中解除链接,从而防止在作用域之外访问该 PropertyIteratorObject。

然而,由于 yield 未被正确处理,有可能在 CloseIterator 运行之前执行循环作用域之外的代码。

在这种情况下,由于对 PropertyIteratorObject 的唯一强引用位于解释器栈上,如果执行了 yield 且返回的生成器也没有被引用,那么当 GC 运行时,该 PropertyIteratorObject 就可能被回收(释放)。

root@kitploit:~
static bool SuppressDeletedPropertyHelper(JSContext* cx, HandleObject obj,
                                          Handle<JSLinearString*> str) {
  NativeIteratorListIter iter(obj->compartment()->enumeratorsAddr());
  while (!iter.done()) {
    NativeIterator* ni = iter.next();
    if (!SuppressDeletedProperty(cx, ni, obj, str)) { // [1]
      return false;
    }
  }

  return true;
}

当使用 delete 时,会调用此函数。它会遍历全局链表并更新迭代器对象,以反映被删除的属性。在 [1] 处,可能会调用一个回调;如果这触发了 GC,一个未被引用的 PropertyIteratorObject 就可能被释放。其终结器也会释放对应的 ni 对象,从而导致释放后使用(UAF)。


利用

root@kitploit:~
static bool SuppressDeletedProperty(JSContext* cx, NativeIterator* ni,
                                    HandleObject obj,
                                    Handle<JSLinearString*> str) {
  ...
  // Check whether id is still to come.
  Rooted<JSLinearString*> idStr(cx);
  IteratorProperty* cursor = ni->nextProperty();
  for (; cursor < ni->propertiesEnd(); ++cursor) {
    idStr = cursor->asString();
    // Common case: both strings are atoms.
    if (idStr->isAtom() && str->isAtom()) {
      if (idStr != str) {
        continue;
      }
    } else {
      if (!EqualStrings(idStr, str)) {
        continue;
      }
    }
    ...
    cursor->markDeleted(); // [1]
    ni->markHasUnvisitedPropertyDeletion();
    return true;
  }

  return true;
}

NativeIterator 的大小可以通过目标对象上的属性数量来控制,而 cursor 是指向将被标记为已删除的属性槽的指针。由于 [1] 实际上是对单个位进行 OR 操作,这产生了一个受限的原语:在任意(8 字节对齐的)地址上进行 1 位写入。

NativeIterator 并非通过 GC 分配器分配,而是从 js::MallocArena 分配。因此,无法通过典型的 JS 对象喷射来将受控的 GC 堆对象放置在其相邻位置。

root@kitploit:~
js::UniquePtr<ImmutableScriptData> js::ImmutableScriptData::new_(
    FrontendContext* fc, uint32_t codeLength, uint32_t noteLength,
    uint32_t numResumeOffsets, uint32_t numScopeNotes, uint32_t numTryNotes) {
  auto size = sizeFor(codeLength, noteLength, numResumeOffsets, numScopeNotes,
                      numTryNotes);
  if (!size.isValid()) {
    ReportAllocationOverflow(fc);
    return nullptr;
  }

  // Allocate contiguous raw buffer.
  void* raw = fc->getAllocator()->pod_malloc<uint8_t>(size.value());
  MOZ_ASSERT(uintptr_t(raw) % alignof(ImmutableScriptData) == 0);
  if (!raw) {
    return nullptr;
  }

  // Constuct the ImmutableScriptData. Trailing arrays are uninitialized but
  // GCPtrs are put into a safe state.
  UniquePtr<ImmutableScriptData> result(new (raw) ImmutableScriptData(
      codeLength, noteLength, numResumeOffsets, numScopeNotes, numTryNotes));
  if (!result) {
    return nullptr;
  }

  // Sanity check
  MOZ_ASSERT(result->endOffset() == size.value());

  return result;
}

一个在 js::MallocArena 中分配的有用对象是 ImmutableScriptData,它存储 JS 字节码。

root@kitploit:~
static MOZ_ALWAYS_INLINE void InitElemArrayOperation(JSContext* cx,
                                                     jsbytecode* pc,
                                                     Handle<ArrayObject*> arr,
                                                     HandleValue val) {
  MOZ_ASSERT(JSOp(*pc) == JSOp::InitElemArray);

  // The dense elements must have been initialized up to this index. The JIT
  // implementation also depends on this.
  uint32_t index = GET_UINT32(pc); // [1]
  MOZ_ASSERT(index < arr->getDenseCapacity());
  MOZ_ASSERT(index == arr->getDenseInitializedLength());

  // Bump the initialized length even for hole values to ensure the
  // index == initLength invariant holds for later InitElemArray ops.
  arr->setDenseInitializedLength(index + 1); // [2]

  if (val.isMagic(JS_ELEMENTS_HOLE)) {
    arr->initDenseElementHole(index);
  } else {
    arr->initDenseElement(index, val);
  }
}

这是初始化 JS 数组时使用的字节码处理器。

它从字节码流中读取一个 4 字节值,并在 [2] 处使用该值来设置数组的 InitializedLength。

如果将 1 位写入应用于 [1] 处的字节码,就可以将 InitializedLength 设置为超出数组实际容量。从那里开始,利用过程通过利用越界(OOB)读写原语继续进行。

下载工具