Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
CVE-2026-2763-POC — Proof-of-Concept-Exploit für CVE-2026-2763, eine Use-after-Free-Schwachstelle in Mozillas JavaScript-Engine, die eine eingeschränkte 1-Bit-Schreibprimitive demonstriert, die zu Out-of-Bounds-Lesen/Schreiben führt. | Kitploit
Tools/GitHubGitHub/ppwwiinn/cve-2026-2763-poc
SpeicherforensikSchwachstellenanalyseExploitationWebsicherheitBinary-Exploitation
GitHubppwwiinn/cve-2026-2763-poc

CVE-2026-2763-POC

Proof-of-Concept-Exploit für CVE-2026-2763, eine Use-after-Free-Schwachstelle in Mozillas JavaScript-Engine, die eine eingeschränkte 1-Bit-Schreibprimitive demonstriert, die zu Out-of-Bounds-Lesen/Schreiben führt.

Repository anzeigen
2vor 5 MonatenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

Schwachstelle

Die Schwachstelle ergibt sich aus der Implementierung der for-in-Syntax.

Der Gültigkeitsbereich einer for-in-Schleife wird durch ein Paar von Bytecodes repräsentiert: JSOP::Iter und 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]
    }

Beim Eintritt in den Gültigkeitsbereich wird bei [1] ValueToIterator aufgerufen, um ein PropertyIteratorObject zu erstellen. Der Interpreter speichert (pusht) es dann bei [2] auf dem Interpreter-Stack. Wenn der Gültigkeitsbereich endet, wird das PropertyIteratorObject bei [3] entfernt.

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();
}

Innerhalb von ValueToIterator wird die Objekterstellung wie oben gezeigt durchgeführt. Nach der Zuweisung des Iterator-Objekts bei [1] wird es bei [2] in eine globale verknüpfte Liste eingebunden. Zu diesem Zeitpunkt wird die einzige Referenz auf das PropertyIteratorObject auf dem Interpreter-Stack gehalten.

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();
}

Wenn der Gültigkeitsbereich endet, wird CloseIterator aufgerufen und entfernt den Iterator aus der zuvor verknüpften Liste, wodurch ein Zugriff auf das PropertyIteratorObject außerhalb des Gültigkeitsbereichs verhindert wird.

Da yield jedoch nicht korrekt behandelt wird, ist es möglich, Code außerhalb des Schleifen-Gültigkeitsbereichs auszuführen, bevor CloseIterator läuft.

In dieser Situation, da die einzige starke Referenz auf das PropertyIteratorObject auf dem Interpreter-Stack liegt, kann das PropertyIteratorObject, wenn yield ausgeführt wird und der zurückgegebene Generator ebenfalls nicht referenziert wird, beim Ausführen des GC eingesammelt (freigegeben) werden.

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;
}

Diese Funktion wird aufgerufen, wenn delete verwendet wird. Sie durchläuft die globale verknüpfte Liste und aktualisiert Iterator-Objekte, um die gelöschte Eigenschaft zu berücksichtigen. Bei [1] kann ein Callback aufgerufen werden; wenn dieser GC auslöst, kann ein nicht referenziertes PropertyIteratorObject freigegeben werden. Sein Finalizer gibt auch das entsprechende ni-Objekt frei, was zu einem Use-after-Free (UAF) führt.


Exploit

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;
}

Ein NativeIterator kann in seiner Größe über die Anzahl der Eigenschaften des Zielobjekts gesteuert werden, und cursor ist ein Zeiger auf den Eigenschafts-Slot, der als gelöscht markiert wird. Da [1] effektiv ein einzelnes Bit OR-verknüpft, ergibt sich daraus ein eingeschränktes Primitive: ein 1-Bit-Schreibzugriff an einer beliebigen (8-Byte-ausgerichteten) Adresse.

NativeIterator wird nicht über den GC-Allokator zugewiesen; stattdessen wird es aus js::MallocArena allokiert. Daher kann man nicht mit einem typischen JS-Object-Spray fortfahren, um kontrollierte GC-Heap-Objekte daneben zu platzieren.

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;
}

Ein nützliches Objekt, das in js::MallocArena allokiert wird, ist ImmutableScriptData, das JS-Bytecode speichert.

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);
  }
}

Dies ist der Bytecode-Handler, der beim Initialisieren eines JS-Arrays verwendet wird.

Er liest einen 4-Byte-Wert aus dem Bytecode-Stream und verwendet ihn, um die InitializedLength des Arrays bei [2] zu setzen.

Wenn der 1-Bit-Schreibzugriff auf den Bytecode bei [1] angewendet wird, wird es möglich, InitializedLength über die tatsächliche Kapazität des Arrays hinaus zu setzen. Von dort aus fährt der Exploit fort, indem er OOB-Lese-/Schreib-Primitives ausnutzt.

Tool herunterladen