Catégories
CTF interIUT 2020 Reverse Write-ups

CTF inter IUT – Formulaire d’inscription

A la fin du formulaire d’inscription pour le CTF inter IUT 2020, organisé par l’ENSIBS, on trouve un pastebin étrange. On remarque directement l’entête “f0VMRg” : c’est un binaire chiffré en base64.

On récupère donc le contenu de ce pastebin et on le décode pour obtenir le binaire.

$ curl https://pastebin.com/raw/4EPhcMFz > fichier
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 22496    0 22496    0     0  57096      0 --:--:-- --:--:-- --:--:-- 57096
$ base64 -d fichier > outpout
$ file outpout
outpout: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=42cea2e7f59f3f0e03866190b1a955d9002ebe17, not stripped

On a donc ici affaire à un ELF 64 bits pour processeurs Intel, non strippé. On sort alors notre désassembleur favori (ce sera ici IDA Pro) et on se lance dans l’analyse statique de ce binaire.

Au premier coup d’oeil à la fonction main, on remarque que la chaine “Please enter the flag > ” va être affichée suite à quoi un input sera attendu.

mov     eax, 25h ; '%'
add     eax, 1

On voit ici que seulement la chaine entrée doit apparemment faire 37 caractères, plus un octet pour le retour à la ligne, “\n”, lié à l’appui de la touche d’entrée.

Cette hypothèse est confirmée par la suite : on observe l’appel à la fonction ‘strlen’ de la libc pour obtenir la taille de l’input, dont le résultat sera comparé à 0x25 soit 37.

mov     rax, [rbp+s]
mov     rdi, rax        ; s
call    _strlen
mov     [rbp+var_4C], eax
mov     eax, 25h ; '%'
cmp     [rbp+var_4C], eax
jnz     FAIL

On peut ensuite utiliser le decompiler d’IDA pour afficher un pseudo code. On arrive au résultat suivant.

int __cdecl main(int argc, const char **argv, const char **envp){
  void *v3; // rsp
  int result; // eax
  const char **v5; // [rsp+0h] [rbp-60h]
  int v6; // [rsp+Ch] [rbp-54h]
  int v7; // [rsp+14h] [rbp-4Ch]
  char *s; // [rsp+18h] [rbp-48h]
  __int64 v9; // [rsp+20h] [rbp-40h]
  int i; // [rsp+28h] [rbp-38h]
  int j; // [rsp+2Ch] [rbp-34h]

  v6 = argc;
  v5 = argv;
  printf("Please enter the flag > ", argv, envp, argv);
  v9 = 37LL;
  v3 = alloca(48LL);
  s = (char *)&v5;

  if(fgets((char *)&v5, 38, stdin)){
    v7 = strlen(s);
    if (v7 == 37){

      for(i=0 ; i < v7 ; ++i)
        s[i] = (s[i] + 24) ^ 0x2A;
      for(j=0 ; j < v7 ; ++j){
        if(s[j] != *((_DWORD *)&FLAG + j)){
          puts("https://tinyurl.com/snyyqw7 !");
          return 1;
        }
      }
      puts(aBienJou);
      result = 0;
    }else{
      puts("C'est non !");
      result = 1;
    }
  }else{
    puts("C'est non !");
    result = 1;
  }
  return result;
}

On voit donc ici que pour chaque caractère de l’input, on va ajouter 24 et xorer le tout avex 0x2A. Cette nouvelle chaine sera alors comparée caractère par caractère aux caractères se trouvant dans le buffer FLAG, se trouvant dans la section .rodata (constant data).

Examinons le contenu de celle-ci.

readelf -x .rodata outpout 

Vidange hexadécimale de la section « .rodata » :
  0x00002000 01000200 00000000 00000000 00000000 ................
  0x00002010 00000000 00000000 00000000 00000000 ................
  0x00002020 25000000 00000000 00000000 00000000 %...............
  0x00002030 00000000 00000000 00000000 00000000 ................
  0x00002040 77000000 4c000000 41000000 4b000000 w...L...A...K...
  0x00002050 70000000 41000000 b9ffffff 63000000 p...A.......c...
  0x00002060 a1ffffff a6ffffff 5d000000 bbffffff ........].......
  0x00002070 62000000 a7ffffff 5d000000 a1ffffff b.......].......
  0x00002080 aaffffff 66000000 aeffffff 63000000 ....f.......c...
  0x00002090 5d000000 acffffff 62000000 a6ffffff ].......b.......
  0x000020a0 5d000000 67000000 aaffffff 53000000 ]...g.......S...
  0x000020b0 a0ffffff 61000000 5d000000 54000000 ....a...]...T...
  0x000020c0 aeffffff 66000000 55000000 67000000 ....f...U...g...
  0x000020d0 bfffffff 506c6561 73652065 6e746572 ....Please enter
  0x000020e0 20746865 20666c61 67203e20 00687474  the flag > .htt
  0x000020f0 70733a2f 2f74696e 7975726c 2e636f6d ps://tinyurl.com
  0x00002100 2f736e79 79717737 20210000 00000000 /snyyqw7 !......
  0x00002110 4269656e 206a6f75 c3a92021 0a456e76 Bien jou.. !.Env
  0x00002120 6f79657a 206e6f75 73206c65 20666c61 oyez nous le fla
  0x00002130 6720656e 204d5020 73757220 54776974 g en MP sur Twit
  0x00002140 74657220 40435446 5f496e74 65725f49 ter @CTF_Inter_I
  0x00002150 55542070 6f757220 70726f75 76657220 UT pour prouver 
  0x00002160 766f7472 65207461 6c656e74 00432765 votre talent.C'e
  0x00002170 7374206e 6f6e2021 00                st non !.

On extrait juste FLAG de cette section .rodata :

0x00002040 77000000 4c000000 41000000 4b000000 w...L...A...K...
0x00002050 70000000 41000000 b9ffffff 63000000 p...A.......c...
0x00002060 a1ffffff a6ffffff 5d000000 bbffffff ........].......
0x00002070 62000000 a7ffffff 5d000000 a1ffffff b.......].......
0x00002080 aaffffff 66000000 aeffffff 63000000 ....f.......c...
0x00002090 5d000000 acffffff 62000000 a6ffffff ].......b.......
0x000020a0 5d000000 67000000 aaffffff 53000000 ]...g.......S...
0x000020b0 a0ffffff 61000000 5d000000 54000000 ....a...]...T...
0x000020c0 aeffffff 66000000 55000000 67000000 ....f...U...g...
0x000020d0 bfffffff ....

On retrouve bien ici nos 37 caractères. Il est maintenant temps de la résolution de ce challenge : nous allons ici utiliser python.

FLAG = ['77000000', '4c000000', '41000000', '4b0000007', '70000000', '41000000', 'b9ffffff', '63000000', 'a1ffffff', 'a6ffffff', '5d000000', 'bbffffff', '62000000', 'a7ffffff', '5d000000', 'a1ffffff', 'aaffffff', '66000000', 'aeffffff', '63000000', '5d000000', 'acffffff', '62000000', 'a6ffffff', '5d000000', '67000000', 'aaffffff', '53000000', 'a0ffffff', '61000000', '5d000000', '54000000', 'aeffffff', '66000000', '55000000', '67000000', 'bfffffff']

# Remet à l'endroit les octets (77000000 --> 0000000077) et convertit le résultat en int
FLAG_lillte_endian_TO_INT = (int(i[:2],16) for i in FLAG)

# Calcule le flag en prenant chaque int du tableau XORé avec 0x2a, le tout moins 24
FINAL_FLAG = ''.join((chr(((i)^0x2a)-24) for i in FLAG_lillte_endian_TO_INT))
print("[+] FLAG : ", FINAL_FLAG)

On exécute notre script et on a la flag.

$ python3 RE_CTF_interIUT.py 
[+] FLAG :  ENSIBS{1st_y0u_sh4l1_n0t_5har3_fl4g5}

Fin du challenge ! Je me contenterais sur ce challenge d’un second blood (tweet ici), à défaut d’avoir inscrit mon équipe au plus tôt… Merci d’avoir suivit ce petit writeup et à bientôt !