ZenSecCTF Team

ZenSec CTF Team write up page

19 October 2021

Reverse - Tetris

by Karzemrok

Description

Trouver comment Nitnick du 33 a pu faire un score aussi élevé ! http://54.217.60.203:3000

Fichier

Résolution

Avant de résoudre ce challenge, jouons un peu pour voir les mécaniques de gameplay.

Le jeu est un Tetris classique, à chaque pièce posée, 4 points sont ajoutés.

Au fur et à mesure du jeu, on se rend compte que chaque ligne rapporte 40 pts.

Avec un timer de 3 min, il est évident qu’il va falloir que le score augmente plus rapidement si on veut faire plus de 150666 pts en moins de 3 min.

Nous allons transformer le WASM en JS (avec wasm2js) et en WAT (avec wasm2wat) pour avoir deux vues différentes pour nous simplifier l’analyse du binaire.

wasm2wat challenge/Build/TetrisChallenge.wasm -o challenge/Build/TetrisChallenge.wat
wasm2js challenge/Build/TetrisChallenge.wasm -o challenge/Build/TetrisChallenge.wasm.js

En regardant la version JS, on trouve quelques fonctions intéressantes :

Dans la fonction “_GameController_computeScore”, on remarque qu’il y a un switch sur plusieurs valeurs dont “40”. On peut en déduire que ce switch gère les points en fonction du niveau actuel.

On voit aussi que la fonction “_GameController_increaseScore” est appelée avec la valeur “4” en paramètre.

function _GameController_computeScore_m159BAAA24208AF84DD5ED8B95AD29BD9E6EEDB1F($0, $1, $2) {
  $0 = $0 | 0;
  $1 = $1 | 0;
  $2 = $2 | 0;
  var $6 = 0, $3 = 0;
  $3 = $0;
  label$1 : {
   label$2 : {
    switch (__Z23il2cpp_codegen_subtractIiiEN11pick_biggerIT_T0_E4typeES1_S2_($1 | 0, 1 | 0) | 0 | 0) {
    case 0:
     $6 = 40;
     break label$1;
    case 1:
     $6 = 100;
     break label$1;
    case 2:
     $6 = 300;
     break label$1;
    case 3:
     $6 = 1200;
     break label$1;
    default:
     break label$2;
    };
   }
   $6 = 0;
  }
  _GameController_increaseScore_m5289853DCC0AB993DA5503761DEB20790872E41C($3 | 0, __Z23il2cpp_codegen_multiplyIiiEN11pick_biggerIT_T0_E4typeES1_S2_($6 | 0, __Z18il2cpp_codegen_addIiiEN11pick_biggerIT_T0_E4typeES1_S2_(0 | 0, 1 | 0) | 0 | 0) | 0 | 0, 0 | 0);
  if ($1) {
   _GameController_increaseScore_m5289853DCC0AB993DA5503761DEB20790872E41C($0 | 0, 4 | 0, 0 | 0);
   if (($1 | 0) != (999 | 0)) {
    _GameController_increaseLinesDeleted_mBD1BAEC32F785607B6B3B35C33B4965690E572AA($0 | 0, $1 | 0, 0 | 0)
   }
  }
 }

On peut modifier ces deux valeurs et voir ce qu’il se passe.

wat2wasm challenge/Build/TetrisChallenge.wat -o challenge/Build/TetrisChallenge.wasm

Dès que le premier bloc est posé, le flag s’affiche

Résolution alternative

Changer la mécanique de score est une première façon de faire, mais il y en a d’autres.

Par exemple, nous pouvons regarder la fonction “_GameController_computeFlag” d’un peu plus près. Cette fonction étant appelée à chaque fois, la vérification du score doit être faite pour vérifier si le flag doit être affiché ou non.

function _GameController_computeFlag_m8E22B11FD96D95CC679276481FE7DAC672CFCB38($0, $1) {
  $0 = $0 | 0;
  $1 = $1 | 0;
  var $3 = 0;
  if (!(HEAP8[2286203 >> 0] | 0)) {
   __Z42il2cpp_codegen_initialize_runtime_metadataPm(1045652 | 0);
   __Z42il2cpp_codegen_initialize_runtime_metadataPm(1045752 | 0);
   __Z42il2cpp_codegen_initialize_runtime_metadataPm(1072180 | 0);
   __Z42il2cpp_codegen_initialize_runtime_metadataPm(1071092 | 0);
   __Z42il2cpp_codegen_initialize_runtime_metadataPm(1067764 | 0);
   HEAP8[2286203 >> 0] = 1;
  }
  if (__ZNK54ObjectReader_t5F7C1222253B9F7FBFC6D74D444FF7AF9A6A415227get_bFullDeserialization_17Ev($0 | 0) | 0) {
   if ((__ZNK17SharedTextureData9GetFormatEv($0 | 0) | 0 | 0) >= (__ZThn52_NK13MonoBehaviour9GetScriptEv($0 | 0) | 0 | 0)) {
    __ZN47Timer_t31BE4EDDA5C1CB5CFDF698231850B47B7F9DE9CB14set_disposed_7Eb($0 | 0, 1 | 0);
       [...]
        }
        }
  }

On voit en début de fonction une comparaison >= qui pourrait correspondre à la vérification du score.

Pour le vérifier, on peut inverser la condition et voir ce qu’il se passe.

La comparaison >= correspond à l’instruction “i32.ge_s”. En regardant ici, on trouve que l’opérateur <= correspond à “le_s

On peut donc le modifier dans le fichier WAT et recompiler.

wat2wasm challenge/Build/TetrisChallenge.wat -o challenge/Build/TetrisChallenge.wasm

Dès que le jeu se lance, le flag s’affiche :+1:

<< PWN - PNG Analyzer
Hardware - Trouver le flag dans le badge >>
tags: sthack2021 - reverse - wasm - ctf