Français [NDH 2016] [CRACKING 150 – lol_so_obfuscated] Write Up

Description

Seulement un fichier était donné pour ce challenge: lol_so_obfuscated

Resolution

Comme tout cracking, on fait un peu de reconnaissance pour voir comment on va pouvoir résoudre l’épreuve.

laxa:lol_so_obfuscated:16:02:28$ file lol_so_obfuscated 
lol_so_obfuscated: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=fd77362edbdc23030c91c114ab4d795133722d96, not stripped
laxa:lol_so_obfuscated:16:02:32$ ldd lol_so_obfuscated 
linux-vdso.so.1 (0x00007ffeceb18000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f64f03dd000)
/lib64/ld-linux-x86-64.so.2 (0x00007f64f0788000)

On lance le binaire rapidement pour voir ce qui est attendu :

laxa:lol_so_obfuscated:16:02:42$ ./lol_so_obfuscated 
Usage ./lol_so_obfuscated <flag>
laxa:lol_so_obfuscated:16:04:29$ ./lol_so_obfuscated qwdqw
29 29 10 16 3 
You're wrong.

Puis on passe le binaire sur IDA pour voir ce qu’il se passe :

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // edx@1
  signed __int64 v4; // rcx@1
  char *v5; // rdi@1
  signed int v6; // ebx@5
  char v8; // [sp+0h] [bp-58h]@1
  char v9; // [sp+1h] [bp-57h]@4
  char v10; // [sp+2h] [bp-56h]@4
  char v11; // [sp+3h] [bp-55h]@4
  char v12; // [sp+4h] [bp-54h]@4
  char v13; // [sp+5h] [bp-53h]@4
  char v14; // [sp+6h] [bp-52h]@4
  char v15; // [sp+7h] [bp-51h]@4
  char v16; // [sp+8h] [bp-50h]@4
  char v17; // [sp+9h] [bp-4Fh]@4
  char v18; // [sp+Ah] [bp-4Eh]@4
  char v19; // [sp+Bh] [bp-4Dh]@4
  char v20; // [sp+Ch] [bp-4Ch]@4
  char v21; // [sp+Dh] [bp-4Bh]@4
  char v22; // [sp+Eh] [bp-4Ah]@4
  char v23; // [sp+Fh] [bp-49h]@4
  char v24; // [sp+10h] [bp-48h]@4
  char v25; // [sp+11h] [bp-47h]@4
  char v26; // [sp+12h] [bp-46h]@4
  char v27; // [sp+13h] [bp-45h]@4
  char v28; // [sp+14h] [bp-44h]@4
  char v29; // [sp+15h] [bp-43h]@4
  char v30; // [sp+16h] [bp-42h]@4
  char v31; // [sp+17h] [bp-41h]@4
  char v32; // [sp+18h] [bp-40h]@4
  char v33; // [sp+19h] [bp-3Fh]@4
  char v34; // [sp+1Ah] [bp-3Eh]@4
  char v35; // [sp+1Bh] [bp-3Dh]@4
  char v36; // [sp+1Ch] [bp-3Ch]@4
  char v37; // [sp+1Dh] [bp-3Bh]@4
  char v38; // [sp+1Eh] [bp-3Ah]@4
  char v39; // [sp+1Fh] [bp-39h]@4
  char v40; // [sp+20h] [bp-38h]@4
  char v41; // [sp+21h] [bp-37h]@4
  char v42; // [sp+22h] [bp-36h]@4
  char v43; // [sp+23h] [bp-35h]@4
  char v44; // [sp+24h] [bp-34h]@4
  char v45; // [sp+25h] [bp-33h]@4
  char v46; // [sp+26h] [bp-32h]@4
  char v47; // [sp+27h] [bp-31h]@4
  __int64 v48; // [sp+38h] [bp-20h]@1

  v3 = argc;
  v4 = 5LL;
  v48 = *MK_FP(__FS__, 40LL);
  v5 = &v8;
  while ( v4 )
  {
    *(_QWORD *)v5 = 0LL;
    v5 += 8;
    --v4;
  }
  v8 = 2;
  v9 = 17;
  v10 = 10;
  v11 = 83;
  v12 = 92;
  v13 = 5;
  v14 = 84;
  v15 = 96;
  v16 = 59;
  v17 = 113;
  *v5 = 0;
  v18 = 97;
  v19 = 108;
  v20 = 40;
  v21 = 39;
  v22 = 121;
  v23 = 63;
  v24 = 115;
  v25 = 115;
  v26 = 42;
  v27 = 97;
  v28 = 98;
  v29 = 1;
  v30 = 37;
  v31 = 74;
  v32 = 121;
  v33 = 91;
  v34 = 54;
  v35 = 28;
  v36 = 103;
  v37 = 65;
  v38 = 60;
  v39 = 89;
  v40 = 58;
  v41 = 80;
  v42 = 118;
  v43 = 14;
  v44 = 116;
  v45 = 2;
  v46 = 39;
  v47 = 3;
  if ( v3 == 2 )
  {
    encrypt("lwskdhgkjsqnvkjwxchzeUBVWCXKJBNVWXCKJBGGG", (char *)argv[1]);
    v6 = strcmp(argv[1], &v8);
    if ( v6 )
    {
      v6 = 0;
      puts("You're wrong.");
    }
    else
    {
      puts("You're right.");
    }
  }
  else
  {
    v6 = 1;
    __printf_chk(1LL, 4196728LL, *argv);
  }
  return v6;
}

On peut voir que notre input est chiffré puis comparé à une autre chaîne de caractère. Si celle-ci est égale, c’est qu’on a le bon input.
Derrière ça, j’ai un peu joué avec du ltrace pour voir le comportement d’un input différent sur plusieurs caractères, pour voir si chaque changement changeait la valeur retournée par encrypt(), de la même façon qu’une fonction de hachage.
J’ai testé le principe de regarder le texte comparé sur le strcmp, avec un caractère jusqu’à trouver le caractère qui est égal au premier caractère du contenu que l’on doit avoir. Je passe au caractère suivant et le reste de la comparaison ne change pas derrière. Du coup, on peut bruteforce très rapidement le bon input.

Voici un test avec ltrace, le premier caractère est bon, le suivant n’est pas correct et pourtant cela ne change pas le chiffrement du premier caractère.

laxa:lol_so_obfuscated:16:14:50$ ltrace ./lol_so_obfuscated na
(...)                                                                                                                                   = 10
strcmp("\002\024", "\002\021\nS\\\005T`;qal('y?ss*ab\001%Jy[6\034gA<Y"...)                                                                                                = 3
(...)
laxa:lol_so_obfuscated:16:14:52$ ltrace ./lol_so_obfuscated nb
(...)
strcmp("\002\027", "\002\021\nS\\\005T`;qal('y?ss*ab\001%Jy[6\034gA<Y"...)                                                                                                = 6
(...)

Avec gdb on récupère la chaîne complète de la comparaison du strcmp (puisque tronqué sur le ltrace) dont on a besoin pour faire le bruteforce.
Ici on va pouvoir faire un script plutôt simple, le programme nous imprime la comparaison entre chaque caractère, on sait que le premier caractère chiffré de notre input doit sortir 2 sur la sortie standard.

Voici le script final

#!/usr/bin/python

from pwn import *
import string
import os

flag = ""
test = "\002\021\nS\\\005T`;qal('y?ss*ab\001%Jy[6\034gA<Y:Pv\016t\002'\003"

for a in range(0, len(test)):
    for x in string.printable:
        r = process(["./lol_so_obfuscated", flag + x])
        print "[%d]%c" % (a, x)
        ret = r.recvline()
        r.close()
        if int(ret.split(" ")[a]) == int(ord(test[a])):
            flag += x
            print "flag so far: " + flag
            break

log.info("flag is: " + flag)

Et on récupère le flag : ndh2k16_19ac2d414c11f6f9da5a1d3342e304bc

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *