hoaxeye
Loslegen
Alle Advisories
HXA-2026-003HochOffen

Out-of-Bounds-Schreibzugriff im NetGameEventV2-Handler durch client-kontrollierten Index

Produkt
FiveM / FXServer
Komponente
citizen-server-impl (NetGameEventV2-Handler)
CVSS
8.1
Veröffentlicht
27. Juli 2026

Zusammenfassung

Der Handler für msgNetGameEventV2 im FXServer verarbeitet eine vom Client gelieferte Liste von Ziel-Spielern. Jeder dieser Werte wird vor jeder Validierung als Index in ein thread_local-Bitset benutzt, das nur für die maximale Spielerzahl dimensioniert ist. Da der Client beliebige 16-Bit-Werte senden darf, entsteht ein Out-of-Bounds-Lese- und -Schreibzugriff mit einem vom Angreifer wählbaren Offset im Thread-Local-Storage des Game-State-Sync-Threads.

Jeder verbundene Client kann das jederzeit auslösen — es handelt sich um Speicher­ korruption, nicht um einen reinen DoS.

Ursache

Der Verarbeitungs-Loop testet und setzt Bits an der vom Client gelieferten Position, bevor der Spielerindex überhaupt geprüft wird:

thread_local eastl::bitset<roundToType<size_t>(MAX_CLIENTS)> processed;
processed.reset();

for (const uint16_t player : targetPlayersSpan)
{
    if (processed.test(player))   // client-kontrolliert, KEIN Bounds-Check → OOB-Read
    {
        continue;
    }
    processed.set(player);        // OOB-Write

    auto targetClient = clientRegistry->GetClientByNetID(player);  // erste Prüfung — zu spät
}

Das Bitset ist nur für die maximale Spielerzahl dimensioniert (wenige hundert Byte). Ein Zugriff mit einem Wert nahe dem 16-Bit-Maximum landet mehrere Kilobyte hinter dem Objekt. eastl::bitset::test/set prüft die Position im Release-Build nicht — der Bereichs-Check ist dort nur ein Debug-Assert. Zusätzlich fehlt dem V2-Handler als einzigem in seiner Nachbarschaft ein Rate-Limiter.

Auswirkung

  • DoS: zuverlässiger Absturz des Sync-Threads bzw. des Prozesses, sobald der Schreib­ zugriff eine ungemappte Seite oder eine kritische Struktur trifft.
  • Speicherkorruption: der Schreibzugriff landet im angrenzenden Thread-Local-Bereich, in dem weitere Objekte anderer Serverkomponenten liegen. Stille Korruption von Server-State ist denkbar; die konkrete Auswirkung hängt vom Heap-Layout des Builds ab.

Behebung

Eine Bereichsprüfung vor dem Bitset-Zugriff genügt:

for (const uint16_t player : targetPlayersSpan)
{
    if (player >= MAX_CLIENTS)   // fehlte
    {
        continue;
    }
    if (processed.test(player)) { /* ... */ }
}

Zusätzlich empfohlen: einen Rate-Limiter analog zu den Nachbar-Handlern ergänzen.