LeHack 19 - Gladiator

Posted on dim. 07 juillet 2019 in CTF

File : gladiator

Ce challenge se présente sous un binaire. Il propose un jeu de combat :

Welcome Gladiator !
Please select a character :
         1 - Berserker (attack : 20, shield: 5)
         2 - Guardian (attack: 10, shield: 10)

On teste donc le jeu plusieurs fois mais sans succès. On peut gagner, perdre ou faire égaliter, le flag refuse de s'afficher.

On commence donc notre analyse avec IDA. On peut donc voir des fonctions avec pour nom une simple lettre. On commence notre analyse par là. On peut générer du pseudo-code pour chacune d'elle afin de rendre plus facile la compréhension.

En arrivant à la fonction X, on remarque dans le pseudo-code qu'elle ressemble beaucoup à la structure du Berserker :

glad_fonction_asm

P_0 *__cdecl b(P_0 *retstr)
{
  string_0 v1; // ax
  byte *v2; // r12
  __int64 v3; // r13
  string_0 v4; // ax

  v1 = tos2("a");
  v2 = v1.str;
  v3 = *(_QWORD *)&v1.len;
  v4 = tos2(&byte_A01E);
  retstr->l = 100;
  retstr->s = 20;
  retstr->sh = 5;
  retstr->st = 10;
  retstr->a.str = v2;
  *(_QWORD *)&retstr->a.len = v3;
  retstr->la.str = v4.str;
  *(_QWORD *)&retstr->la.len = *(_QWORD *)&v4.len;
  return retstr;
}

On modifie alors une valeur, par exemple l'attaque, pour valider cette hypothèse :

glad_attack_hex

glad_attack_hex_new

On applique le patch, et on relance notre binaire :

Welcome Gladiator !
Please select a character :
         1 - Berserker (attack : 20, shield: 5)
         2 - Guardian (attack: 10, shield: 10)
1
VS Berserker
Select action in the range 0..3 :
         Attack = 0
         Protect = 1
         Heal = 2
         Pass = 3
0
Turn 1 :
         User Action : attack
         Bot Action : heal
        User: Life : 100, Stamina: 9
        Bot Life : -120, Stamina: 9
You Win !

Parfait, on a trouvé le code pour le Berserker. Cependant, on ne sait toujours pas comment débloquer le flag. On avance donc dans notre recherche de fonctions. On arrive à la fonction d :

 if ( p.l == 42 )
  {
    c_array = 4330;
    v6 = 934;
    v7 = 2330;
    v8 = 1502;
    v9 = 4506;
    v10 = 7604;
    v11 = 2746;
    v12 = 6248;
    v13 = 3353;
    v14 = 4068;
    v15 = 968;
    v16 = 5295;
    v17 = 1207;
    v18 = 4878;
    v19 = 2696;
    v20 = 7067;
    v21 = 2954;
    v22 = 6149;
    v23 = 3199;
    new_array_from_c_array(&tmp3, 19, 19, 4, &c_array);
    for ( tmp4 = 0; tmp3.len > tmp4; ++tmp4 )
    {
      i = *((_DWORD *)tmp3.data + tmp4);
      *(_QWORD *)&v1.element_size = *(_QWORD *)&main.element_size;
      *(_OWORD *)&v1.data = *(_OWORD *)&main.data;
      j = *(_DWORD *)array__get(v1, i);
      v2 = char_str(j);
      v27.str = v2.str;
      v27.len = v2.len;
      v_print(v27);
    }
    v3 = tos2("\n");
    v28.str = v3.str;
    v28.len = v3.len;
    v_print(v28);
  }

On voit qu'un test est fait avant d'exécuter le code de la fonction. Comme vu dans la fonction du Berserker, le p.l correspond au point de vie du joueur. On retourne donc éditer la valeur par défaut (100) afin de mettre le 42 testé dans cette fonction.

glad_life_hex_new

On applique notre nouveau patch et on relance une nouvelle fois le binaire :

Welcome Gladiator !
Please select a character :
         1 - Berserker (attack : 20, shield: 5)
         2 - Guardian (attack: 10, shield: 10)
1
VS Guard
Select action in the range 0..3 :
         Attack = 0
         Protect = 1
         Heal = 2
         Pass = 3
0
Turn 1 :
         User Action : attack
         Bot Action : heal
        User: Life : 42, Stamina: 9
        Bot Life : -115, Stamina: 9
You Win !
LH{lUj?]T_VAR94$+N}

YEAH ! Le flag apparaît alors après avoir tué l'adversaire : LH{lUj?]T_VAR94$+N}.