1 Punkte von GN⁺ 2023-12-06 | 1 Kommentare | Auf WhatsApp teilen
  • In Magic: The Gathering Arena konnte man den Gegner zum gewünschten Zeitpunkt zur Aufgabe zwingen und sich so in Matchmaking-Spielen einen Zustand verschaffen, in dem man nicht verlieren konnte
  • Kartenspiele eignen sich normalerweise gut für eine serverautoritativ aufgebaute Architektur, in der der Server den vollständigen Zustand verwaltet, doch der MTGA-Botgegner Sparky war als Logik im lokalen Client implementiert
  • Der auf C# basierende MTGA-Client erlaubte über Reflection den Zugriff auf Laufzeitobjekte und private Felder, und im dekompilierten Code ließen sich Namen wie JoinMatch, ConnectAndJoinMatch und HeadlessClient erkennen
  • In Bot-Matches wurde eine Struktur verwendet, bei der mit derselben PersonaID und demselben JWT beide Sitzplätze verbunden wurden; übertrug man das auf normale Matches, konnte man einen Headless-Client an den gegnerischen Sitz hängen und danach ConcedeGame() aufrufen
  • Der Server wurde später so gepatcht, dass in Matchmaking-Spielen nicht mehr beide Sitzplätze dieselbe Kontoidentität und dasselbe JWT verwenden können; der Fall zeigt, wie sich die Grenze zwischen clientseitiger Bot-Implementierung und Sitzplatz-Authentifizierungsprüfung direkt auf die praktische Sicherheit auswirkt

Warum es schwer ist, Kartenspiele zu hacken

  • Kartenspiele sind rundenbasiert, und zwischen Client und Server werden nicht viele Informationen ausgetauscht, daher passen sie gut zu einer serverautoritativen Architektur
  • Der Server verwaltet den vollständigen Spielzustand und schickt dem Client nur die benötigten Informationen
    • Nicht öffentliche Informationen wie die Hand oder das Deck des Gegners existieren lokal nicht
    • Versuche, die Deckreihenfolge zu ändern oder die nächste gezogene Karte zu manipulieren, sind schwierig, weil der Server die Aktion ausführt und nur das Ergebnis mitteilt
  • Anders als bei Ego-Shootern sind die Aktionen der Spieler in Kartenspielen begrenzt und treten zu festgelegten Zeitpunkten auf
    • In FPS können Informationen wie Gegnerpositionen vorab im Client-Cache liegen, was Formen wie Wallhacks ermöglicht
    • Riots Anti-Wallhack-Blog behandelt einen Ansatz, clientseitige Positionsdaten von Spielern zu reduzieren
  • Auch fehlerhaftes Verhalten lässt sich vergleichsweise leicht erkennen
    • Eine Karte spielen, die man nicht auf der Hand hat
    • Eine Aktion ausführen, obwohl man nicht am Zug ist

Ausgangspunkt der Analyse: Netzwerk und C#-Client

  • Als Ausgangspunkt für das Game-Hacking wurde der Ansatz gewählt, sich die Netzwerkkommunikation anzusehen
  • Manfreds DEF-CON-Vortrag über MMO-Hacking zeigt, dass Reverse Engineering von Netzwerkprotokollen für die Analyse vieler Bugs zentral ist
  • Da MTGA in C# geschrieben ist, konnten Laufzeitobjekte manipuliert werden, statt Funktionen für das Senden und Empfangen von Traffic zu hooken
  • Nicht obfuskierte .NET-Assemblies erlauben es dank Metadata-Token, Funktions-, Variablen- und Klassennamen in menschenlesbarer Form zu sehen
    • Dadurch kommt das Dekompilat praktisch einem Source-Code-Review sehr nahe

Die in JoinMatch entdeckte Sparky-Struktur

  • Bei der Suche nach relevanten Funktionsnamen für die Initialisierung des Match-Beitritts wurde die Funktion JoinMatch gefunden
  • JoinMatch war eine lange Funktion mit mehr als 200 Zeilen, und im unteren Bereich ließ sich ein Aufruf von ConnectAndJoinMatch erkennen
    • Diese Funktion wirkte wie der Ablauf, der Match-Konfigurationsinformationen entgegennimmt und eine Verbindung zum Gameserver herstellt
  • Im selben Codepfad gab es Verzweigungen für MatchType.NPE und MatchType.Familiar
    • NPE steht vermutlich für ein Tutorial-ähnliches Match im Rahmen der New Player Experience
    • Familiar ist das reguläre Bot-Match
  • Die in diesem Zweig aufgerufene Logik war mit Sparky, dem Botgegner von MTGA, verbunden
    • Sparky ist der maskottchenartige Gegner von MTGA in Tutorial- und Bot-Partien
    • In Bot-Matches lief die Bot-Logik innerhalb des Spielclients auf dem lokalen Rechner

Lokaler Bot und HeadlessClient

  • Der eigentliche Logik-Handler des Bots befand sich in der Klasse HeadlessClient
  • HeadlessClient ist ein Headless-Client, der das Spielbrett nicht rendert, sich aber mit dem Server verbindet und die Partie spielt
  • Der lokal erzeugte Bot-Client nutzte dieselben Benutzerauthentifizierungsdaten wie der Spielclient
    • PersonaID als Benutzer-ID
    • Das nach dem Login dem Spiel zugewiesene JSON Web Token
  • In Bot-Matches war die Struktur so angelegt, dass der Server es nicht als Problem ansah, wenn derselbe Client faktisch beide Seiten des Matches verband
  • Ein Sitzplatz (Seat) dient im Spiel dazu, zu unterscheiden, welcher Spieler man ist
    • In Bot-Matches konnten mit denselben Authentifizierungsdaten verschiedene Sitzplätze belegt werden

Wie normale Matches übernommen wurden

  • Es wurde getestet, ob sich die Bot-Match-Logik auf normale Matches übertragen lässt, um beide Sitzplätze zu verbinden
  • Die nötigen Informationen wurden aus Laufzeitobjekten des Spiels geholt
    • aktuelle Match-Konfiguration
    • Host und Port des Match-Servers
    • controllerFabricUri
    • matchId
    • PersonaID
    • Jwt
    • Kartendatenbank
    • Match-Manager
    • Asset-Lookup-System
  • Ausgehend vom eigenen Sitzplatz wurde der gegnerische Sitzplatz berechnet
    • Im Code wurde mit man.LocalPlayerSeatId % 2U + 1U der andere Sitzplatz bestimmt
  • Mit UnityFamiliar.SpawnFamiliar_DEBUG(...) wurde ein Bot mit dem gegnerischen Sitzplatz verbunden
  • Anschließend wurde das erzeugte UnityFamiliar-Objekt gefunden und cheatbot.Client.Gre.ConcedeGame() aufgerufen, um es sofort aufgeben zu lassen

Ergebnis und Patch

  • Diese Methode funktionierte auch in normalen Matches
    • Sie funktionierte sogar dann, wenn der Gegner bereits verbunden war und das Spiel lief
    • Da es sich um Matchmaking-Spiele handelte, erhielt man Belohnungen, als hätte man einen menschlichen Gegner besiegt
  • Der Kern der Schwachstelle war, dass der normale Match-Server die Situation zuließ, dass beide Sitzplätze mit demselben Konto und demselben JWT verbunden wurden
  • Der Server wurde später so gepatcht, dass in Matchmaking-Spielen beide Sitzplätze nicht mehr dasselbe Konto und dasselbe JWT verwenden können
  • Der InstaWin-Code im Anhang implementiert als Unity-MonoBehaviour eine GUI-Schaltfläche; beim Klick sammelt sie die aktuellen Match-Informationen, hängt einen Bot an den gegnerischen Sitzplatz und lässt ihn dann aufgeben

1 Kommentare

 
GN⁺ 2023-12-06
Hacker-News-Kommentare
  • Der erste Anlass, mich wirklich intensiv mit Linux zu beschäftigen, war ShowEQ für EverQuest, mit dem ich mir den Netzwerkverkehr angesehen habe.
    Damals war der Traffic nicht verschlüsselt und enthielt viele nützliche Informationen. Über einen Hub spiegelte ich den Traffic auf eine Linux-Maschine, zeichnete eine Echtzeitkarte der Zone, zeigte Monster-, NPC- und Spielerpositionen an und sogar die Beute, die Monster bei sich hatten, sodass man gezielt bestimmte Monster jagen konnte. Der Vorteil war, dass es manuell lief und daher nicht erkennbar war; schließlich bekam SOE es mit und begann, den Traffic zu verschlüsseln.

    • Die Daten müssen am Ende ohnehin entschlüsselt und gelesen werden, also fing man an, den Client per Reverse Engineering zu analysieren und herauszufinden, wie man sie in Echtzeit entschlüsselt.
      Daraufhin führt die Gegenseite keybasierte Signaturen ein, man versucht wieder, die Keys aus dem Client zu stehlen und die Verschlüsselung zu brechen, und später kommen Anti-Cheat-Systeme dazu – so beginnt das Katz-und-Maus-Spiel.
    • Als Teenager habe ich bei Dark Age of Camelot etwas Ähnliches gemacht; es war sehr lehrreich für Network Sniffing, den Unterschied zwischen Hubs und Switches und für Linux.
      War es den Vorteil im Spiel wert? Nein. Ich habe nicht ernsthaft gespielt, also habe ich zwei Wochen mit der Einrichtung verbracht und es dann etwa eine Woche genutzt, aber als Lernerfahrung war es großartig.
    • Ich erinnere mich, dass ShowEQ genutzt wurde, um mehrere Theorien und Bugs zu belegen, die Verant/Sony immer wieder abgestritten hatte.
      Zum Beispiel die Existenz von hell levels, dass nicht Menschen, sondern Halblinge einen Erfahrungsbonus bekamen und es tatsächlich Unterschiede bei der Erfahrung je nach Volk/Klasse gab, sowie dass die frühe Shaman-Alchemie tatsächlich kaputt war. Und ich glaube, daraus ist dann eqemulator.org hervorgegangen.
    • Ich frage mich, wie Verschlüsselung hier helfen soll. Der Client muss es doch sowieso entschlüsseln, also kann man sich nicht einfach dort dranhängen?
    • ShowEQ funktioniert immer noch, und es gibt auch MySEQ, das unter Windows läuft und den Speicher ausliest.
      Die heutigen Besitzer von EQ scheinen sich nicht besonders darum zu kümmern, daher kann man es ziemlich problemlos nutzen. Allerdings hat keine der beiden Apps je die Beute von Monstern angezeigt, abgesehen von sichtbarer Ausrüstung. Im Lauf der Jahre haben sich auch einige Daten geändert: Früher wurde die exakte Monster-Gesundheit übertragen, heute wird nur noch ein Prozentwert gesendet.
  • Ich verstehe den Teil nicht ganz, dass „ein nahezu vollständiger Bot, der beliebige Partien Magic: The Gathering spielen kann, klein genug ist, um auf einer lokalen Maschine zu laufen“.
    Wenn eine MTG-KI zu schwergewichtig wäre, um sie auf Kundenmaschinen laufen zu lassen, würde man sie vermutlich auch nicht auf Servern betreiben. Bot-Partien in Kartenspielen werden normalerweise nicht pro Partie abgerechnet, also würden die Kosten stark steigen. Server sind auch keine Magie, sondern verwenden meist dieselben x86-CPUs wie lokale Maschinen, oft mit niedrigerem Takt als Desktops. Um Bot-Züge gegenüber lokaler Ausführung zu beschleunigen, müsste man deutlich mehr Kerne einsetzen als der Kunde hat; bei 8–16 zugewiesenen Kernen pro Spieler wäre das zu Spitzenzeiten bei gleichzeitigen Logins ein Albtraum. Wenn der CPU-Spieler kein Multicore unterstützt, müsste die lokale Ausführung ohnehin schneller sein.

    • Gemeint war hier nicht Rechenleistung, sondern Speichernutzung.
      Es heißt, dass die Regel-Engine des Bots klein genug ist, um in den Speicher eines älteren iPhones oder Android-Geräts zu passen. Auf einem Server könnte man viele Zustandsmaschinen oder Regel-Engines im Speicher halten, und für das Ausführen einer bestimmten Anfrage wäre möglicherweise kaum Rechenleistung nötig.
    • Für Desktops stimmt das, aber man sollte auch bedenken, dass MTGA auch auf Smartphones läuft.
    • MTG ist ein Spiel mit einem sehr komplexen Regelsystem.
  • Daniel, nochmals Glückwunsch zum Top-Beitrag. Nachdem dieser Artikel erschienen war, habe ich mich auch am Hacken von MTGA versucht und auf GitHub ein wenig darüber gesprochen [0].
    Für Interessierte: Ich arbeite derzeit inaktiv an einem inoffiziellen MTGA-Client, der bislang kaum Funktionen hat. Ziel ist es, das Spielen von Ranked-Partien zu automatisieren und stärkere Bot-Gegner bereitzustellen. In letzter Zeit ist das wegen anderer Dinge liegen geblieben, und ich weiß auch noch nicht, wie ich eine gute, klar erkennbare UI bauen soll; daher kann ich nicht einmal abschätzen, wann es mindestens nützlich wird. Darüber hinaus interessieren mich weiterhin Daniel s und anderer Leute Geschichten über Game-Hacking. Ich würde gern mehr darüber hören, wie man solche Bugs findet, wie man sich nach dem Hacken keine Sorgen über Bans macht, wie man Bugs öffentlich macht, wie @aethros es angesprochen hat, oder wie man inoffizielle Kartenspiel-Clients strukturiert.
    [0] https://github.com/MayerDaniel/mayerdaniel.github.io/issues/...

  • Zu sagen, man hätte erwartet, dass es viel Overhead verursacht, für ein komplexes Spiel wie MTG einen KI-Gegner zu bauen, ist eine ziemlich große Untertreibung.
    Das Spiel ist nahezu Turing-vollständig, und selbst wenn man Endlosschleifen ausklammert, spielen Leute viel damit herum. Trotzdem dürfte es zur KI-Strategie-Seite bereits einiges an Forschung geben. Und da der Autor den Beitrag selbst gepostet hat, könnte der Titel wohl „Show HN:“ tragen.

    • Show HN ist für Projekte gedacht, die Leute selbst ausprobieren können, nicht für Blogposts: https://news.ycombinator.com/showhn.html
    • „Man kann eine Turingmaschine im Spiel kodieren“ und „es ist schwierig, ein Programm zu schreiben, das in jeder Situation legale Aktionen ausführen kann“ sind überhaupt nicht dasselbe.
      Für Bot-Autoren scheint Letzteres das schwierige Problem zu sein.
    • Es ist tatsächlich Turing-vollständig: https://arxiv.org/abs/1904.09828
    • Durch die Rotation neuer Sets ändert sich das Spiel ständig, aber ich weiß, dass man bei bestimmten Interaktionen Endlosschleifen erzeugen kann oder dies zu verschiedenen Zeitpunkten möglich war.
      Zum Beispiel, indem man mit Eintritts-/Austritts-/Enttapp-Effekten Token-Kreaturen erzeugt und sterben lässt. Solche Schleifen fügen auch nicht immer einer Seite Schaden zu.
    • Ich frage mich, ob es tatsächliche Strategien gibt, die dieser Komplexität nahekommen.
      Ich habe MTG nie gespielt, aber diese Behauptung klingt so, als sei es technisch möglich, wenn man absichtlich eine zustandsbehaftete Struktur baut – nicht, weil man damit gewinnen will.
  • Es macht wirklich Spaß, mit meinem Sohn Oldschool Magic 93/94 mit physischen Karten zu spielen.
    Jedes Jahr fahren wir nach Madrid, um an der 7pts-Singleton-Weltmeisterschaft teilzunehmen. Diesen Sommer wurde mein Sohn Neunter, worauf ich sehr stolz bin. 7pts Singleton ist ein großartiges Format, das vielfältige Deckkonstruktionen ermöglicht und für vergleichsweise überschaubare Kosten ausgewogenes Gameplay bietet (https://7pts-singleton.com)

    • Ein Format, in dem Black Lotus, Ancestral Recall und Moxen legal sind, als günstig zu bezeichnen, ist schwierig.
      Auch wenn man in 7pts Singleton nicht alle davon spielen kann: Schon eine einzige dieser Karten kostet Tausende bis Zehntausende Dollar. Trotzdem schön, dass du mit deinem Sohn das Spiel genießt, und Glückwunsch zur Platzierung.
  • Reine clientseitige Spiellogik, wirklich? Als ich früher ein kleines Spiel gebaut habe, musste ich für die Reaktionsfähigkeit manchmal Spiellogik in den Client legen, aber nichts hindert einen daran, dieselbe Logik auf dem Server erneut auszuführen, also habe ich das so gemacht.
    Bei Echtzeitspielen wie FPS oder RTS kann das schwierig sein, aber bei einem Kartenspiel gibt es dafür keine Ausrede. Bei so einem Kartenspiel sollte der Client nicht mehr Informationen erhalten, als ein realer Spieler sehen kann. Also etwa nicht die Karteninhalte der gegnerischen Hand senden, sondern nur die Anzahl der Karten. Auch Aktionen, die an den Server geschickt werden, sollten sich nur auf einen selbst beziehen; man sollte also nicht die Aufgabe des Gegners erklären können. Wenn ich „Ich gebe auf!“ sage, sollte der Server das so interpretieren, dass ich aufgebe.

    • Aus dem Artikel kann man schließen, dass das Spiel genau so geschrieben ist. Man erhält nur die Informationen, die zum jeweiligen Zeitpunkt nötig sind, und sendet nur die eigenen Aktionen.
      Die Schwachstelle bestand darin, dass man einen zweiten Client starten und sich in der laufenden Partie auf den Platz des Gegners einloggen konnte. Sobald das gelang, konnte man Aktionen des Gegners senden, einschließlich der Aufgabe.
    • Wie im Artikel klar steht, wird das Gameplay vollständig serverseitig verarbeitet.
      Was der Autor ausnutzte, war nicht clientseitige Spiellogik, sondern ein Problem in der Autorisierung und im Code für den Spielbeitritt. Die Aussage „man sollte nicht anstelle des Gegners aufgeben können“ wiederholt im Grunde genau, wie der vom Autor gefundene Exploit funktionierte.
    • Im ersten Drittel des Artikels steht ausdrücklich, dass das Spiel so implementiert ist.
  • In League of Legends gab es bei einer bestimmten Kombination aus Champion und Items einen Division-durch-null-Bug, durch den der Server alle Spieler hinauswarf und dann abstürzte.
    Weil der Ausnutzer zuletzt herausgeworfen wurde, wurde dessen Team als Sieger gewertet, während das gegnerische Team statt eines normalen Ergebnisses Loss Prevented erhielt.

    • Wenn der Gegner kein normales Ergebnis und keine Niederlage bekommt, sollte das Siegerteam auch keinen Sieg erhalten.
  • An diesem Artikel gefallen mir die zugänglichen und zugleich aufschlussreichen Details.
    Was ich allerdings nicht verstehe, ist der Fall, dass während einer laufenden Partie ein Bot verbunden wird. Warum erlaubt das Spiel einen Beitritt mitten in der Partie, und warum wird es als Aufgabe des Gegners gewertet, wenn der Bot aufgibt? Wenn man ein Spiel für drei Spieler erstellt, sollte die Aufgabe von Spieler 3 nicht auch bedeuten, dass Spieler 2 aufgegeben hat.

    • Es wird kein Drei-Spieler-Spiel, sondern bleibt weiterhin ein Zwei-Spieler-Spiel.
      Der Code ermittelt den Sitzindex meines Kontos und lässt den Bot dann mit dem anderen Sitzindex beitreten. Das Problem ist, dass nicht geprüft wird, ob der beitretende Nutzer der korrekte Nutzer für diesen Platz ist. Man könnte auch denken, dass eine erneute Verbindung zu einem bereits verbundenen Platz nicht erlaubt sein sollte, aber wenn ein Spiel auch Mobilgeräte unterstützt, möchte man den Timeout bei Verbindungsabbrüchen wohl ziemlich lang halten. Man will ja keinen Spieler blockieren, der kurz die Verbindung verliert und sich wieder verbindet, bevor der Timeout die alte Verbindung als tot erkannt hat. Ich habe auch in anderen Spielen gesehen, dass beim erneuten Verbinden etwa 10 Sekunden lang „Beitritt zu laufendem Spiel nicht möglich“ erscheint und man erst später wieder verbunden wird. Als Analogie: Man erscheint bei einem MTG-Turnier im örtlichen Kartenladen, schiebt jemanden vom Stuhl, setzt sich hin und ruft „Ich gebe auf!“, und der Judge akzeptiert das als Aufgabe von Spieler B, weil die Person auf diesem Stuhl es erklärt hat.
      1. Sehr wahrscheinlich ist das die Art, wie Wiederverbindungen nach einem Verbindungsabbruch behandelt werden: Die alte Verbindung wird beendet.
      2. Der Bot ersetzt Spieler 2, reicht eine Aufgabe ein, und der Server verbucht sie als Aufgabe von Spieler 2.
    • MTG: Arena erlaubt keine Drei-Spieler-Spiele, daher drängt sich der Bot auf den Platz des Gegners.
      Deshalb konnte er offenbar an dessen Stelle aufgeben. Vielleicht haben die Entwickler schlicht nicht gedacht, dass jemand einen Grund hätte, so etwas überhaupt zu versuchen, und deshalb keine Prüfung eingebaut.
  • Das erinnert mich an die Diablo-2-Zeit, als man dasselbe Login-Paket wiederverwenden konnte, um einen Open-Server-Charakter (LAN) mit den offiziellen bnet-Internetservern zu verbinden.
    Da die Open-Server-Daten vollständig lokal gespeichert waren, konnte man alle möglichen Items erstellen, die eigentlich nicht existieren dürften, und die offiziellen Server akzeptierten sie trotzdem.

  • Ich bin vor Kurzem über MTGA wieder in MTG eingestiegen. Es ist ein Unity-Spiel und nicht il2cpp, also habe ich es schnell dekompiliert; selbst wenn es il2cpp gewesen wäre, wäre das wohl kein großer Schutz gewesen, und ich habe ein paar ziemlich interessante Dinge gefunden.
    Darunter waren Schlüssel für den Epic-Launcher-Build und undokumentierte APIs. Ich will das nicht zum Cheaten nutzen; ich hätte nur gern ein Kampfprotokoll. Also etwa welche Matches man gewonnen und verloren hat und eine Möglichkeit, den Board-State beendeter Spiele noch einmal anzusehen. Diesen Artikel werde ich mir auch ansehen, und ich hoffe, dass er bald gepatcht wird.

    • Diese Schwachstelle wurde bereits gepatcht, bevor der Artikel geschrieben wurde, und MTG wurde darüber informiert.
      Wenn du Protokolle ansehen möchtest, schau dir https://untapped.gg/en an. Ich habe mich ein wenig mit ihnen unterhalten, und im Grunde machen sie genau das, was du willst. Die meisten Informationen stammen aus dem MTG-Debug-Log im MTGA-Anwendungsverzeichnis, du kannst dir also auch selbst einen Tracker bauen, wenn du möchtest. Die Seite erklärt das ebenfalls: https://help.hearthsim.net/en/articles/3620440-how-do-i-supp...
    • https://www.17lands.com/ sammelt Sieg-/Niederlage-Statistiken für Limited-Spiele und speichert außerdem für Limited wie Constructed Spielprotokolle Zug für Zug. Ich habe als Contributor mitgewirkt.
    • Für die Erfassung von Spieldaten ist https://mtgaassistant.net/ meines Wissens ziemlich verbreitet.
    • Soweit ich weiß, gibt es diese Funktion bereits. Gemeint ist wahrscheinlich das Player Log.
      Ich erinnere mich, dass es einige Apps gab, die mithilfe dieses Logs Tracking betrieben haben.