Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2026-2763-POC — Эксплойт-доказательство концепции для CVE-2026-2763, use-after-free в движке JavaScript от Mozilla, демонстрирующий ограниченный примитив записи в 1 бит, приводящий к чтению/записи за пределами границ. | Kitploit
Инструменты/GitHubGitHub/ppwwiinn/cve-2026-2763-poc
Криминалистика памятиАнализ уязвимостейЭксплуатацияВеб-безопасностьЭксплуатация Бинарных Файлов
GitHubppwwiinn/cve-2026-2763-poc

CVE-2026-2763-POC

Эксплойт-доказательство концепции для CVE-2026-2763, use-after-free в движке JavaScript от Mozilla, демонстрирующий ограниченный примитив записи в 1 бит, приводящий к чтению/записи за пределами границ.

Репозиторий
2166 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Уязвимость

Уязвимость возникает из-за реализации синтаксиса 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]
    }

При входе в область действия вызывается ValueToIterator в [1] для создания PropertyIteratorObject. Затем интерпретатор сохраняет (помещает в стек) его в стек интерпретатора в [2]. Когда область действия завершается, PropertyIteratorObject удаляется в [3].

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, что приведёт к use-after-free (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 байт) адресу.

NativeIterator не выделяется через GC-аллокатор; вместо этого он выделяется из js::MallocArena. В результате нельзя продолжить с типичным распылением JS-объектов для размещения контролируемых GC-heap объектов рядом с ним.

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-байтовое значение из потока байткода и использует его для установки InitializedLength массива в [2].

Если применить запись одного бита к байткоду в [1], становится возможным установить InitializedLength за пределы фактической ёмкости массива. Отсюда эксплуатация продолжается с использованием примитивов OOB чтения/записи.

Скачать инструмент