Retour au Blog

L'exploit de performance serveur UEFN expliqué : Blindez le Netcode de votre Unreal Engine

Publié le 24 février 2026
L'exploit de performance serveur UEFN expliqué : Blindez le Netcode de votre Unreal Engine

Tout développeur Multiplayer connaît le scénario catastrophe : un seul utilisateur malveillant se connecte à votre serveur, effectue une séquence d'actions apparemment anodines, et soudain votre tick rate chute de 60Hz à un chiffre. Le serveur entier s'immobilise, affectant des dizaines de joueurs innocents.

Récemment, un exploit critique de performance serveur UEFN a été signalé sur les forums Unreal Engine par le développeur Vysena Woyka. Le rapport décrit une technique reproductible à 100 % qui provoque une dégradation sévère à l'échelle du serveur dans les cartes Unreal Editor for Fortnite (UEFN). L'exploit s'aggrave à mesure que d'autres joueurs rejoignent la partie, ne nécessite absolument aucun outil tiers et peut potentiellement causer une instabilité totale du serveur en cas d'exécution prolongée.

Comme les étapes exactes de reproduction sont gardées privées pour éviter un abus généralisé, de nombreux développeurs se demandent : Comment un tel exploit fonctionne-t-il réellement sous le capot ? et plus important encore, Comment protéger mes propres Unreal Engine dedicated servers personnalisés contre des attaques similaires ?

Dans cette analyse technique approfondie, nous allons disséquer l'architecture de la dégradation des performances côté serveur dans Unreal Engine. Nous explorerons les vecteurs courants utilisés par les joueurs malveillants pour étouffer les dedicated servers, comment implémenter une validation stricte côté serveur en C++, et comment architecturer votre infrastructure pour une résilience maximale.

L'anatomie d'un exploit de serveur Unreal Engine

Pour comprendre comment un joueur peut faire tomber un serveur sans outils de hacking externes, il faut comprendre comment Unreal Engine gère sa boucle de jeu principale (Main Game Loop). Les Unreal Engine dedicated servers sont principalement single-threaded en ce qui concerne la Game Logic. Alors que des tâches comme la Physics Simulation (via le moteur Chaos) et le chargement asynchrone peuvent être déportées sur des worker threads, la fonction Tick centrale de vos Actors, la Replication Serialization et l'exécution des RPC (Remote Procedure Call) se déroulent toutes sur le Game Thread.

Si un serveur tourne à 30 ticks par seconde (30Hz), il dispose d'exactement 33,3 millisecondes pour traiter tous les inputs des joueurs, mettre à jour le Game State, calculer la physique et sérialiser les données réseau pour la frame suivante. Si un joueur peut forcer le serveur à exécuter une opération qui prend 50 millisecondes, le tick rate du serveur chute instantanément à 20Hz.

Lorsque votre tick rate chute de manière aussi drastique, vous n'obtenez pas seulement du lag visuel, mais une divergence d'état catastrophique. Nous avons largement couvert les retombées de ce phénomène dans notre guide technique sur The Unreal Engine Multiplayer Sync Bug Ruining Your World States And How To Fix It.

Sans utiliser d'injecteurs de mémoire ou d'éditeurs de paquets, les exploits de performance in-game reposent généralement sur l'un des trois vecteurs suivants : RPC Flooding, Physics/Collision Overload ou Replication Saturation.

Vecteur 1 : RPC Flooding et échecs de validation

Le moyen le plus courant de faire planter ou de dégrader un serveur Unreal Engine est de spammer les Server RPCs. Si un client lie un Server RPC à sa molette de souris ou à un input avec un framerate débloqué, il peut envoyer des centaines de requêtes par seconde au serveur.

Si votre Server RPC contient une logique complexe — comme le spawn d'un Actor, l'exécution d'un line trace (Raycast) ou l'itération à travers de grands tableaux — le serveur est forcé d'exécuter cette logique coûteuse des centaines de fois par frame.

Unreal Engine fournit la macro WithValidation pour les RPCs, mais de nombreux développeurs ne l'utilisent que pour vérifier si un pointeur est valide, ignorant complètement le Rate Limiting.

Le correctif : Implémenter un RPC Rate Limiter en C++

Pour protéger votre serveur, vous devez implémenter un Rate Limiting strict sur toutes les communications client-serveur. Voici une approche éprouvée pour réguler les Server RPCs en utilisant un Actor Component personnalisé en C++.

D'abord, nous définissons notre logique de limitation dans le fichier header :

// RateLimiterComponent.h
#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "RateLimiterComponent.generated."

UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class MULTIPLAYER_API URateLimiterComponent : public UActorComponent
{
    GENERATED_BODY()

public:	
    URateLimiterComponent();

    // Checks if the action is allowed. Returns false if the client is spamming.
    UFUNCTION(BlueprintCallable, Category = "Security")
    bool CanExecuteAction(FName ActionName, float CooldownTime);

private:
    // Maps action names to the last time they were executed
    TMap<FName, float> LastExecutionTimes;

    // Threshold for maximum allowed actions per second before flagging the player
    const int32 MaxActionsPerSecond = 20;
    int32 CurrentActionCount;
    float LastResetTime;
};

Ensuite, nous implémentons la logique de validation dans le fichier CPP. Notez comment nous utilisons le temps du serveur (GetWorld()->GetTimeSeconds()) pour garantir que le client ne peut pas falsifier son heure locale pour contourner le cooldown.

// RateLimiterComponent.cpp
#include "RateLimiterComponent.h"

URateLimiterComponent::URateLimiterComponent()
{
    PrimaryComponentTick.bCanEverTick = false;
    CurrentActionCount = 0;
    LastResetTime = 0.0f;
}

bool URateLimiterComponent::CanExecuteAction(FName ActionName, float CooldownTime)
{
    // Only run this logic on the server
    if (!GetOwner()->HasAuthority())
    {
        return false;
    }

    float CurrentTime = GetWorld()->GetTimeSeconds();

    // Reset the global action counter every second
    if (CurrentTime - LastResetTime >= 1.0f)
    {
        CurrentActionCount = 0;
        LastResetTime = CurrentTime;
    }

    // Global spam check
    CurrentActionCount++;
    if (CurrentActionCount > MaxActionsPerSecond)
    {
        UE_LOG(LogTemp, Warning, TEXT("Player %s is exceeding global RPC limits!"), *GetOwner()->GetName());
        return false;
    }

    // Specific action cooldown check
    if (LastExecutionTimes.Contains(ActionName))
    {
        float LastTime = LastExecutionTimes[ActionName];
        if (CurrentTime - LastTime < CooldownTime)
        {
            // Client is spamming this specific action
            return false;
        }
    }

    // Update the execution time and allow the action
    LastExecutionTimes.Add(ActionName, CurrentTime);
    return true;
}

Maintenant, lorsque vous implémentez votre fonction Server_PerformAction_Validate, vous pouvez rejeter dynamiquement le RPC si le client le spamme :

bool AMyPlayerController::Server_PerformExpensiveAction_Validate()
{
    // If the rate limiter returns false, the RPC is rejected and the client is disconnected
    if (URateLimiterComponent* RateLimiter = GetComponentByClass<URateLimiterComponent>())
    {
        return RateLimiter->CanExecuteAction(FName("ExpensiveAction"), 0.5f);
    }
    return true;
}

Vecteur 2 : Physics et Collision Overload

Un autre vecteur d'exploit courant (et fortement suspecté dans les environnements sandbox comme UEFN) est la surcharge physique. Si les joueurs peuvent spawner des objets, lâcher des items ou manipuler des Physics Bodies, ils peuvent intentionnellement empiler des centaines d'objets dans un espace restreint.

Lorsque des Physics Bodies se chevauchent, le moteur Chaos tente de résoudre les collisions. Si 500 objets sont forcés dans le même espace de coordonnées, le calcul de résolution de collision croît de manière exponentielle, provoquant un blocage total du CPU sur le serveur.

De plus, si ces objets ont bGenerateOverlapEvents activé, le serveur déclenchera OnComponentBeginOverlap des centaines de milliers de fois par frame.

Le correctif : Culling de collision agressif

Pour prévenir la dégradation du serveur basée sur la physique, vous devez décorréler la physique visuelle de la validation des collisions côté serveur.

  1. Désactiver les Overlaps sur les items lâchés : Si un joueur lâche un item, désactivez bGenerateOverlapEvents sur le serveur après qu'il se soit immobilisé.
  2. Limiter les Spawns : Codez en dur une densité maximale d'objets physiques par secteur de grille.
  3. Réguler la logique d'Overlap : Si vous devez utiliser des overlaps, n'exécutez pas de logique complexe directement dans l'événement d'overlap. À la place, activez un flag et traitez l'overlap dans un batch contrôlé pendant la fonction Tick.

Vecteur 3 : Replication Saturation et étranglement de la bande passante

Le système de réplication d'Unreal Engine est puissant, mais il est aussi lourdement dépendant du CPU. Le serveur doit itérer sur chaque Actor répliqué, vérifier s'il est pertinent pour un client spécifique, comparer ses propriétés avec le dernier état reconnu et sérialiser les changements.

Des joueurs malveillants peuvent exploiter cela en modifiant rapidement des variables répliquées (comme les données de personnalisation de leur personnage ou l'état de l'inventaire). Cela force le serveur à sérialiser constamment de gros blocs de données, saturant à la fois le CPU et les limites de bande passante du serveur.

Le correctif : Optimiser NetUpdateFrequency

Ne laissez jamais NetUpdateFrequency à sa valeur par défaut (100.0) pour les acteurs non critiques. Vous devez adapter dynamiquement la fréquence de réplication en fonction de la proximité des joueurs et de l'état de l'action.

De plus, vous devriez utiliser DefaultEngine.ini pour imposer des limites de bande passante strictes sur votre dedicated server. Cela empêche un seul client malveillant de forcer le serveur à traiter des flux de paquets massifs :

[/Script/OnlineSubsystemUtils.IpNetDriver]
MaxClientRate=15000
MaxInternetClientRate=10000
NetServerMaxTickRate=30
LanServerMaxTickRate=30
ConnectionTimeout=15.0
InitialConnectTimeout=30.0

En plafonnant MaxClientRate, le serveur rejettera simplement les paquets excédentaires d'un client tentant d'inonder le canal réseau, préservant ainsi les cycles CPU pour les joueurs légitimes.

Résilience de l'infrastructure : Gérer l'inévitable

Même avec un code C++ parfait, des exploits zero-day surviendront. Lorsqu'un exploit comme le bug de performance serveur UEFN frappe votre jeu, vos nœuds de serveur vont inévitablement atteindre 100 % d'utilisation CPU et crasher.

Si toute l'architecture de votre flotte de serveurs est vulnérable à un point de défaillance unique, vous risquez une perte permanente de joueurs. Construire une infrastructure résiliente avec un routage de fallback approprié est une chose que nous préconisons fortement, comme nous l'avons discuté dans notre analyse architecturale de The Stop Killing Games Campaign Vs Live Ops Architecting Server Fallbacks.

Lorsqu'un serveur crashe à cause d'un exploit, votre backend doit instantanément détecter le nœud mort, lancer une nouvelle instance et migrer gracieusement les joueurs affectés vers la file d'attente de matchmaking sans perdre leurs données persistantes.

Construire cela vous-même nécessite la mise en place de load balancers personnalisés, de database sharding, d'orchestration de conteneurs (comme Kubernetes) et de gestion de certificats SSL — facilement 4 à 6 mois de travail d'ingénierie dédié. Avec horizOn, ces services backend sont préconfigurés. Notre infrastructure surveille automatiquement la santé des serveurs, auto-scale les instances en fonction de la charge CPU et gère le routage des sessions de joueurs, vous permettant de vous concentrer sur la correction du code du jeu plutôt que de lutter contre votre infrastructure.

5 bonnes pratiques pour la stabilité du serveur

Pour protéger votre jeu Multiplayer Unreal Engine contre les exploits de performance, implémentez immédiatement ces cinq règles architecturales :

  1. Implémenter des quotas RPC stricts : Ne faites jamais confiance au taux d'input du client. Utilisez le composant rate limiter C++ détaillé ci-dessus pour imposer des cooldowns stricts sur chaque Server RPC.
  2. Assainir les vecteurs de mouvement : Les speed hacks et les exploits de téléportation fonctionnent en envoyant des vecteurs massifs au serveur. Bridez toujours les requêtes AddMovementInput et SetActorLocation côté serveur par rapport à la vitesse de déplacement théorique maximale du personnage.
  3. Utiliser le Replication Graph : Si votre jeu supporte plus de 40 joueurs, le système de réplication par défaut deviendra un goulot d'étranglement. Implémentez l'Unreal Engine Replication Graph pour regrouper spatialement les acteurs et réduire drastiquement la charge CPU des tests de pertinence.
  4. Désactiver les visuels côté serveur : Les dedicated servers ne devraient jamais charger d'UI, de systèmes de particules ou d'animations de skeletal mesh. Assurez-vous que les paramètres de votre projet suppriment strictement ces assets du build du dedicated server pour libérer de la mémoire et des cycles CPU.
  5. Surveiller le Tick Rate dynamiquement : Implémentez un sous-système côté serveur qui surveille le delta time moyen. Si le serveur détecte que le tick rate tombe en dessous de 15Hz pendant plus de 5 secondes, il devrait automatiquement mettre en pause les tâches de fond non essentielles (comme le spawn d'IA ou la génération d'événements ambiants) pour récupérer.

Conclusion

Le récent exploit de performance serveur UEFN est un rappel brutal que le développement de jeux Multiplayer est intrinsèquement un exercice de cybersécurité. Vous ne pouvez pas simplement supposer que les joueurs interagiront avec votre jeu comme prévu. Chaque RPC, chaque interaction physique et chaque variable répliquée est un vecteur d'attaque potentiel.

En adoptant un modèle "Server-Authoritative, Client-Distrusted", en optimisant profondément votre logique de réplication C++ et en implémentant des limites de taux strictes, vous pouvez blinder votre jeu contre ces types de crashs de performance catastrophiques.

Lorsque vous combinez un code de jeu blindé avec une infrastructure de serveurs auto-scalable et auto-réparatrice, vous créez un environnement où les exploits deviennent des désagréments mineurs plutôt que des désastres fatals pour le jeu. Prêt à scaler votre backend Multiplayer sans le casse-tête du dev-ops ? Essayez horizOn gratuitement et laissez-nous gérer l'orchestration de vos serveurs.


Source : [CRITICAL] Server Performance Exploit