Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-1801C — Analyse statique de rétro-ingénierie des heuristiques d'entrée de mouvement dans le moteur Source 2, identifiant les cas limites structurels dans l'automatisation des entrées et les routines de limitation du taux de saut. | Kitploit
Outils/GitHubGitHub/misterdengi/cve-2026-1801c
Analyse des VulnérabilitésExploitationRétro-ingénierieAnalyse de BinairesArticles et Recherche
GitHubmisterdengi/cve-2026-1801c

CVE-2026-1801C

Analyse statique de rétro-ingénierie des heuristiques d'entrée de mouvement dans le moteur Source 2, identifiant les cas limites structurels dans l'automatisation des entrées et les routines de limitation du taux de saut.

Voir le dépôt
2il y a 5h 47mPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2026-1801C (QUANTUM-SHIFT) / CVE-2026-180A7 (BAL-JUMP) : Analyse statique des heuristiques d'entrée de mouvement dans Source 2 (server.dll)

Résumé

Ce document présente une analyse statique de rétro-ingénierie de deux routines de vérification de mouvement côté client implémentées dans le moteur Counter-Strike 2 (server.dll) : l'évaluateur d'automatisation d'entrée / SOCD (sub_1801C6B30, désigné CVE-2026-1801C / QUANTUM-SHIFT) et le limiteur de débit des requêtes de saut (sub_180A7EDB0, désigné CVE-2026-180A7 / BAL-JUMP). Grâce à l'analyse de désassemblage et à la modélisation mathématique formelle des machines à états sous-jacentes, nous démontrons des cas limites structurels dans les deux heuristiques. Plus précisément, nous documentons comment la quantification temporelle discrète dans sub_1801C6B30 crée une frontière de classification invariante sous des décalages de phase fixes à tick unique, et comment repose sur une métrique d'intervalle univariée ($\Delta t$) qui reste invariante sous des déclencheurs à impulsion unique verrouillés en phase.

sub_180A7EDB0

1. Vérification de l'automatisation d'entrée (sub_1801C6B30)

1.1 Spécification de la routine

  • Binaire cible : server.dll (build commercial Win64)
  • Décalage de symbole : 0x1C6B30
  • Interface : void __fastcall sub_1801C6B30(int *pMovementServices, __int64 pPlayerController, __int64 pUserCmdPB)

1.2 Pipeline d'exécution et représentation d'état

La routine évalue les événements de transition des boutons de mouvement (+moveleft / +moveright) soumis via les charges utiles client CBaseUserCmdPB. L'exécution suit un pipeline conditionnel :

  1. Filtrage par vélocité : La magnitude scalaire au carré de la vélocité 2D du pion du joueur doit dépasser un seuil statique : $$|v_{xy}|^2 > (0.52 \cdot 260.0)^2 = 18279.04 \text{ unité}^2/\text{s}^2$$
  2. Quantification temporelle : Les deltas de phase de transition ($\Delta t_{\text{trans}}$) sont quantifiés en unités discrètes de ticks de frame : $$k = \text{round}\left( \frac{\Delta t_{\text{trans}}}{\tau_{\text{tick}}} \right), \quad \tau_{\text{tick}} \approx 15.625\text{ ms}$$
  3. Classification symbolique : La valeur entière $k \in [0, 15]$ correspond à un code d'événement discret $c \in {1, 2, 3}$ : $$c = \begin{cases} 1 & \text{si } k = 0 \text{ (transition idéale à 0 tick)} \ 2 & \text{si } k \ge 1 \text{ et l'état est Chevauchement} \ 3 & \text{si } k \ge 1 \text{ et l'état est Sous-chevauchement} \end{cases}$$
  4. Stockage en tampon circulaire : Le code de classification $c$ est stocké dans un tableau d'historique circulaire de capacité $N$ (sv_auto_cstrafe_attempt_window, valeur par défaut 100).

1.3 Logique décompilée (sub_1801C6B30)

root@kitploit:~
void __fastcall sub_1801C6B30(int *pMovementServices, __int64 pPlayerController, __int64 pUserCmdPB)
{
    if (!pPlayerController || (*(_BYTE *)(pUserCmdPB + 16) & 1) == 0)
        return;

    __int64 pPawn = sub_180B137A0(pPlayerController);
    if (!pPawn || !(*(unsigned __int8 (__fastcall **)(__int64))(*(_QWORD *)pPawn + 3216i64))(pPawn))
        return;

    float *pVel = (float *)sub_1803CBD90(pPawn, &szVelocityBuf);
    float vSq = (pVel[0] * pVel[0]) + (pVel[1] * pVel[1]) + (pVel[2] * pVel[2]);
    
    // Filtrage : Seuil de vélocité minimale
    if (vSq <= 18279.04f)
        return;

    int rawTicks = *(_DWORD *)(pUserCmdPB + 24);
    bool bIsOverlap = (rawTicks >= 0);
    int absTicks = bIsOverlap ? rawTicks : ~rawTicks;

    double curTime = sub_1801F46D0(off_181C9F350);
    if (curTime * 1e-9 * (double)absTicks >= 15.5)
        return;

    int roundedTicks = (int)V_roundd();
    if ((unsigned int)roundedTicks > 15)
        return;

    // Attribution de symbole
    char symbolCode;
    if (roundedTicks == 0)
        symbolCode = 1; // Succès / 0-tick parfait
    else
        symbolCode = bIsOverlap ? 2 : 3; // Chevauchement ou Sous-chevauchement

    // Récupération des pointeurs de configuration ConVar
    unsigned int windowCap = *GetConVarUInt(&unk_181DDBD30, &qword_181DDBD38);
    if (windowCap - 1 > 999)
        return;

    sub_1801ECEA0(pMovementServices, windowCap);

    // Ajout du symbole au tampon circulaire
    int bufCap = pMovementServices[0];
    if (bufCap > 0)
    {
        int writeIdx = pMovementServices[6];
        if (writeIdx >= 0 && writeIdx < bufCap)
        {
            *(_BYTE *)(writeIdx + *((_QWORD *)pMovementServices + 1)) = symbolCode;
            if (++pMovementServices[6] == bufCap)
                pMovementServices[6] = 0;
        }
    }

    // Mise à jour des histogrammes de fréquence de ticks discrets
    if (bIsOverlap)
        pMovementServices[roundedTicks + 7]++;
    else
        pMovementServices[roundedTicks + 23]++;

    int seqLen = *GetConVarInt(&unk_181DDBD40, &qword_181DDBD48);
    if ((unsigned int)(seqLen - 1) > 999)
        return;

    // Évaluation des métriques de séquence en fenêtre glissante
    int totalValid = 0;
    int totalOverlaps = 0;
    int curSequenceSuccesses = 0;
    int maxSequenceSuccesses = 0;

    for (int i = 0; i < (int)windowCap; ++i)
    {
        if (i < bufCap)
        {
            int readIdx = i + pMovementServices[6] - bufCap;
            if (i + pMovementServices[6] < bufCap)
                readIdx = i + pMovementServices[6];

            char sym = *(_BYTE *)(readIdx + *((_QWORD *)pMovementServices + 1));
            if (sym != 0)
            {
                totalValid++;
                if (sym == 2) totalOverlaps++;
                else if (sym == 1) curSequenceSuccesses++;
            }
        }

        // Mise à jour de la fenêtre glissante
        if (i >= seqLen && (i - seqLen) >= 0)
        {
            if ((i - seqLen) < bufCap)
            {
                int popIdx = i + pMovementServices[6] - seqLen - bufCap;
                if (i + pMovementServices[6] - seqLen < bufCap)
                    popIdx = i + pMovementServices[6] - seqLen;

                if (*(_BYTE *)(popIdx + *((_QWORD *)pMovementServices + 1)) == 1)
                    curSequenceSuccesses--;
            }
        }

        if (maxSequenceSuccesses < curSequenceSuccesses)
            maxSequenceSuccesses = curSequenceSuccesses;
    }

    int minAttempts = *GetConVarInt(&unk_181DDBD20, &qword_181DDBD28);
    float dynamicLimit = 0.0f;

    if (totalValid >= minAttempts)
    {
        int minSuccessThreshold = *GetConVarInt(&unk_181DDBD50, &qword_181DDBD58);
        if (maxSequenceSuccesses >= minSuccessThreshold)
        {
            float successRatio = 0.0f;
            if (seqLen > minSuccessThreshold)
            {
                successRatio = (float)(maxSequenceSuccesses - minSuccessThreshold) / 
                               (float)(seqLen - minSuccessThreshold);
            }

            float lowerPct = *GetConVarFloat(&unk_181DDBD70, &qword_181DDBD78); // Défaut 20.0%
            float upperPct = *GetConVarFloat(&unk_181DDBD60, &qword_181DDBD68); // Défaut 5.0%
            
            dynamicLimit = (lowerPct - upperPct) * successRatio + upperPct;
        }
    }

    float observedOverlapPct = ((float)totalOverlaps / (float)totalValid) * 100.0f;

    // Condition de violation : La limite dynamique dépasse le ratio de chevauchement observé
    if (dynamicLimit > observedOverlapPct)
    {
        sub_1801ECEA0(pMovementServices, 0); // Vidage de l'état du tampon
        
        bool bEnableKick = *GetConVarByte(&unk_181DDBD90, &qword_181DDBD98);
        if (bEnableKick)
        {
            __int64 pEngine = qword_182012050;
            void (__fastcall *pfnKickClient)(__int64, unsigned int, _QWORD, __int64) = 
                *(void (__fastcall **)(__int64, unsigned int, _QWORD, __int64))(*(_QWORD *)pEngine + 768i64);

            int slotIdx = -1;
            sub_181265470(pPlayerController, &slotIdx);
            
            // Code de déconnexion 162 : NETWORK_DISCONNECT_KICKED_INPUTAUTOMATION
            pfnKickClient(pEngine, (unsigned int)(slotIdx - 1), 0, 162);
        }
    }
}

1.4 Modèle mathématique et analyse de classification

Soit $\mathbf{S} = {s_1, s_2, \dots, s_N}$ la séquence de symboles d'événements dans le tampon d'historique de taille $N$. Le paramètre $S_{\text{max}}$ désigne le nombre maximal de symboles de code $1$ ($s_i = 1$) dans toute sous-séquence contiguë de longueur $L$ :

$$S_{\text{max}} = \max_{0 \le j \le N - L} \sum_{i=j}^{j+L-1} \mathbb{I}(s_i = 1)$$

La fonction de seuil adaptatif $T(S_{\text{max}})$ est conditionnée par $S_{\text{max}} \ge S_{\text{thresh}}$ :

$$T(S_{\text{max}}) = \begin{cases} \theta_{\text{upper}} + (\theta_{\text{lower}} - \theta_{\text{upper}}) \cdot \frac{S_{\text{max}} - S_{\text{thresh}}}{L - S_{\text{thresh}}} & \text{si } S_{\text{max}} \ge S_{\text{thresh}} \ 0 & \text{si } S_{\text{max}} < S_{\text{thresh}} \end{cases}$$

Où les valeurs de configuration par défaut sont définies comme suit :

  • $L = 15$ (sv_auto_cstrafe_sequence_length)
  • $S_{\text{thresh}} = 10$ (sv_auto_cstrafe_success_threshold)
  • $\theta_{\text{lower}} = 20.0$ (sv_auto_cstrafe_lower_overlap_pct_threshold)
  • $\theta_{\text{upper}} = 5.0$ (sv_auto_cstrafe_upper_overlap_pct_threshold)

Une violation est déclarée si et seulement si $T(S_{\text{max}}) > P_{\text{overlap}}$, où $P_{\text{overlap}}$ est le ratio d'échantillonnage des événements de code $2$ :

$$P_{\text{overlap}} = \frac{100}{N} \sum_{i=1}^{N} \mathbb{I}(s_i = 2)$$

Invariant structurel

Pour tout flux d'entrée où le décalage de phase $\Delta t_{\text{trans}}$ est contraint tel que $k = \text{round}(\Delta t_{\text{trans}} / \tau_{\text{tick}}) \ge 1$ :

  1. $s_i \in {2, 3}$ pour tout $i$, ce qui donne $\mathbb{I}(s_i = 1) = 0$.
  2. Par conséquent, $S_{\text{max}} = 0$.
  3. Puisque $0 < S_{\text{thresh}}$, $T(S_{\text{max}}) = 0$ est vérifié de manière identique.
  4. L'inégalité $0 > P_{\text{overlap}}$ est évaluée comme fausse pour tout $P_{\text{overlap}}$ non négatif. Le chemin d'évaluation aboutissant à pfnKickClient reste non exécuté.

2. Limitation du débit de saut (sub_180A7EDB0)

2.1 Spécification de la routine

  • Binaire cible : server.dll (build commercial Win64)
  • Décalage de symbole : 0x1A7EDB0
  • Interface : void* __fastcall sub_180A7EDB0(__int64 pMovementServices, __int64 pMoveData)

2.2 Logique décompilée (sub_180A7EDB0)

root@kitploit:~
void *__fastcall sub_180A7EDB0(__int64 pMovementServices, __int64 pMoveData)
{
    __int64 pPawn = *(_QWORD *)(pMovementServices + 8);
    sub_180C28550(pPawn, 2i64);
    
    if (!*(_QWORD *)(pPawn + 56))
        nullsub_908(pPawn);

    // Évaluation de la coque de collision et du contact au sol
    (*(void (__fastcall **)(_QWORD))(**(_QWORD **)(pPawn + 56) + 1624i64))(*(_QWORD *)(pPawn + 56));
    sub_1803CBD30(*(_QWORD *)(pPawn + 56));
    sub_1803CD4C0(*(_QWORD *)(pPawn + 56), v14);

    if (dword_181FDB358)
        --dword_181FDB358;

    // Vérification du bit IN_JUMP (0x2) dans le masque de boutons de commande
    if ((*(_BYTE *)(pPawn + 88) & 2) != 0 || sub_180C28550(pPawn, 2i64))
    {
        _BYTE *pPenaltyActive = (_BYTE *)(pMovementServices + 16);
        if (*pPenaltyActive)
        {
            float curTime = *((float *)off_181C9F350 + 12);
            float lastJumpTime = *(float *)(pMovementServices + 20);
            float penaltyInterval = sub_18029CE70(&unk_181FDB4D8, 0xFFFFFFFFi64); // sv_jump_spam_penalty_time

            // Évaluation d'intervalle : curTime > lastJumpTime + penaltyInterval
            if (curTime > (penaltyInterval + lastJumpTime))
            {
                if (*pPenaltyActive)
                {
                    sub_180A99DE0(pMovementServices + 16, 0xFFFFFFFFi64, 0xFFFFFFFFi64);
                    *pPenaltyActive = 0; // Effacement de l'état de pénalité
                }

                // Application de l'impulsion de saut
                sub_180A7F550(pMovementServices, pMoveData);

                if (!*pPenaltyActive)
                {
                    sub_180A99DE0(pPenaltyActive, 0xFFFFFFFFi64, 0xFFFFFFFFi64);
                    *pPenaltyActive = 1; // Activation de l'état de pénalité
                }

                if (sub_180C28550(*(_QWORD *)(pMovementServices + 8), 2i64))
                {
                    // Stockage de l'horodatage du saut : lastJumpTime = curTime - frameTime
                    *(float *)(pMovementServices + 20) = *((float *)off_181C9F350 + 12) - 
                                                         *((float *)off_181C9F350 + 13);
                }
                return pMovementServices;
            }
        }

        bool bDebugSpam = *GetConVarByte(&unk_181FDB640, &qword_181FDB648);
        if (!bDebugSpam)
        {
            // Application de l'impulsion de saut lorsque le débogage de pénalité est désactivé
            sub_180A7F550(pMovementServices, pMoveData);
            if (!*pPenaltyActive)
            {
                sub_180A99DE0(pPenaltyActive, 0xFFFFFFFFi64, 0xFFFFFFFFi64);
                *pPenaltyActive = 1;
            }
            *(float *)(pMovementServices + 20) = *((float *)off_181C9F350 + 12) - 
                                                 *((float *)off_181C9F350 + 13);
        }
    }
    return pMovementServices;
}

2.3 Modèle temporel et évaluation d'intervalle

Soit $t_n$ l'horodatage d'exécution de la $n$-ième requête de saut, et $\tau_{\text{penalty}}$ la valeur de sv_jump_spam_penalty_time (typiquement $1$ tick, soit $\approx 15.625\text{ ms}$). La transition d'état pour le drapeau de pénalité $P \in {0, 1}$ et l'activation d'impulsion $I \in {0, 1}$ est définie par :

$$I_n = \begin{cases} 1 & \text{si } P_{n-1} = 0 \text{ ou } t_n - t_{n-1} > \tau_{\text{penalty}} \ 0 & \text{si } P_{n-1} = 1 \text{ et } t_n - t_{n-1} \le \tau_{\text{penalty}} \end{cases}$$

Analyse des entrées haute fréquence vs verrouillées en état au sol

  1. Flux d'entrée haute fréquence : Les flux d'entrée discrets générés sur des frames consécutives produisent des intervalles $t_n - t_{n-1} \approx \tau_{\text{tick}} \le \tau_{\text{penalty}}$. Sous cette condition, $I_n = 0$, supprimant la génération d'impulsion verticale.
  2. Verrouillage de phase en état au sol : Si une commande de saut est émise exclusivement lors de la transition d'état vers FL_ONGROUND ($P_{\text{ground}} = 1$), la durée entre requêtes correspond à la durée de vol balistique $T_{\text{flight}}$ :

$$t_n - t_{n-1} = T_{\text{flight}} \ge \frac{2 \cdot v_z}{g} \approx 300\text{--}500\text{ ms}$$

Puisque $T_{\text{flight}} \gg \tau_{\text{penalty}}$, le prédicat $t_n - t_{n-1} > \tau_{\text{penalty}}$ est vérifié inconditionnellement pour tout $n$, empêchant l'activation du chemin de suppression.


3. Résumé de la remédiation structurelle

ComposantSymbole ciblePrimitive actuelleFaiblesse analytiqueRemédiation proposée
Automatisation d'entréesub_1801C6B30Classification binaire ($k=0$ vs $k \ge 1$) + seuillageLe décalage de phase fixe ($k=1$) donne $S_{\text{max}} = 0$ de manière identiqueÉvaluer la variance d'échantillonnage $\sigma^2(\Delta t)$ sur tous les $k$ ; signaler les distributions à faible variance quel que soit l'index de bac
Limitation du débit de sautsub_180A7EDB0Comparaison scalaire de delta temporel ($t_n - t_{n-1} > \tau$)Invariant sous les délais de phase en vol ($T_{\text{flight}} \gg \tau$)Évaluer la continuité du vecteur de vélocité et la durée de contact au sol ($\Delta t_{\text{contact}}$)
Télécharger l’outil