-15% na nową książkę sekuraka: Wprowadzenie do bezpieczeństwa IT. Przy zamówieniu podaj kod: 10000

Zhackował Hearthstone. Banalny sposób na rozłączenie gry przeciwnika [historia ciekawego buga, fixed]

08 lutego 2022, 17:08 | Aktualności | komentarze 2

Kliiiiikbait – bo sposób nie był banalny! A nie, czekaj, sposób był banalny, jak już się wiedziało co i jak. No dobra, nie taki banalny.

Ech… zobaczmy o co chodziło w bugu, który został wykorzystany bojowo 3 lata temu i nieco później załatany.

Idąc od końca, efekt na koncie gracza-hackera był taki:

Idąc od początku:

1. Analiza binarki gry:

2. Można zacząć szukać podatności.

  • Próba dobrania się do szczegółów decku należącego do innego gracza? Nie działa (tzn. Blizzard robi odpowiednie sprawdzenie po stronie serwerowej).
  • Używanie niedozwolonego na arenie decku? Nie działa.
  • Używanie innych kart niż dozwolone na arenie. Nieee ;(
  • Stworzenie decku z kartami, których się nie ma, dodawanie dzikich kart do decku? Taaaaaaak! Ale system matchujący traktuje taki deck niestandardowy i nie znajduje żadnego oponenta. Zatem: nope.
  • Podatności klasy integer overflow. Nic.
  • Nic. Nic Nic!

3. Może coś w FindGame() ?

byte[] array = Guid.NewGuid().ToByteArray();
long currentFsgId = FiresideGatheringManager.Get().CurrentFsgId;

bnet.protocol.game_master.Player player = new bnet.protocol.game_master.Player();
Identity identity = new Identity();
identity.SetGameAccountId(gameAccountId);
player.SetIdentity(identity);
player.AddAttribute(ProtocolHelper.CreateAttribute("type", (long)bnetGameType));
player.AddAttribute(ProtocolHelper.CreateAttribute("scenario", (long)scenarioId));
player.AddAttribute(ProtocolHelper.CreateAttribute("brawl_library_item_id", (long)brawlLibraryItemId));
player.AddAttribute(ProtocolHelper.CreateAttribute("deck", deckId));
player.AddAttribute(ProtocolHelper.CreateAttribute("aideck", aiDeckId));
player.AddAttribute(ProtocolHelper.CreateAttribute("request_guid", array));
player.AddAttribute(ProtocolHelper.CreateAttribute("fsg_id", currentFsgId));
...
BattleNet.FindGame(gameProperties, new bnet.protocol.game_master.Player[] { player });

Odpalenie FindGame() już po dołączeniu do gry wywala (samego siebie) z gry. Tj. restart klienta i ponowne podłączenie. Nic wielkiego. Ale co by się stało gdyby gameAccountId podmienić na ID innego gracza? Jeśli dodatkowo ustawię dobry deckId (docelowego gracza), to klient gry tego gracza rozłącza się, restartuje i ponownie podłącza do gry.

Skąd poznać gameAccountID kogoś innego? To gra nam prezentuje w standardzie. Co z deckId? Można użyć tutaj wartości 0 (zero) – odpowiadającej temu deckowi.

4. Do boju.

  • Prosty „mod” ;-) który odpala co 5 sekund funkcję findGame(), feedując ją parametrami:

    gameAccountId = GameState.Get().GetOpposingSidePlayer().GetGameAccountId(); bnetGameType = BnetGameType.BGT_VS_AI; scenarioId = 0xBA2 /Boom Labs/; deckId = 0;
  • Stworzenie nowego konta w grze
  • Odpalenie „moda”
  • Czekanie kilkanaście godzin. Ranga #1 w legendarnym tierze:

Ciężko powiedzieć czy Blizzard (oraz gracze) byli zachwyceni takim obrotem sprawy. No dobra, Blizzard chyba finalnie tak, bo błąd został zgłoszony w ramach programu bug bounty i została wypłacona nagroda $2500.

PS
Pamiętajcie żeby takie „zabawy” wykonywać zawsze etycznie i przypadkiem nie rozwalić całej gry.

~Michał Sajdak

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

  1. KiedyKonkurs

    Michale Sajdaku, kiedy wyniki konkursu na najlepszy artykuł? Ludzie wciąż czekają.

    Odpowiedz
  2. somebody

    Odpowiedź jest prosta: najlepszy artykuł nie został jeszcze nadesłany. Konkurs nadal trwa ;)

    Odpowiedz

Odpowiedz na somebody