mercredi 4 juillet 2012

NDH 2k12 - Stay Alive - 500 pts

Dernier crackme de cette série de writeup de la NDH 2k12, une application windows phone !

1.       What
On a donc une application Windows phone dans son fichier « xap », en regardant de plus près on voit qu’il s’agit d’un fichier compressé (PK).
On decompresse puis on se retrouve avec les fichiers de l’application :


Si on passe les images, le xaml pour l’interface et le manifest, il reste la dll qui contient le code de l’application, probablement une appli qui demande un mot de passe …
On peut essayer d’ouvrir la dll avec IDA mais on voit rapidement qu’il s’agit de .Net. On switch donc dans notre décompileur de .Net préféré (pour moi Reflector).

private void ndhPasswordLogin_Click(object sender, RoutedEventArgs e)
{
   
string password = this.ndhPassword.get_Password();
   
if (password.Length > 0)
   
{
       
if (string.Equals(this.cryptPassword(password), this.trustedHash()))
       
{
           
base.get_NavigationService().Navigate(new Uri("/LoginError.xaml", UriKind.Relative));
       
}
       
else
       
{
            MessageBox
.Show("Login failed, try again.", "Error", 0);
       
}
   
}
}

On a une fonction qui sera appelé pour vérifier un password. On test si le mot de passe chiffré avec « cryptPassword » correspond au « trustedHash() ». Rien de spécial, continuons …

2.       TrustedHash
Un simple writeLn ou copier/coller et on obtient :

+R2kbpJn0tqCMmUNZzFwQFMU+EW0/Nkoup5Zl4hxoPFRfmk1fhKJhfBkdhNYiDqt23jbRoxDbO3
QHpy6M2kY/8hd3Z1ds8a1StSQkXxNyjZd2mTQPpTR4zmyf9FzK4Y4XUfzw1hUP3qj+dGyKgNjQm
XdtPTOqRVh2T41NSvmK/YV6XzIk6SFdhF9XyXUJOlcII5mXQ/SuHiHmkEoTJiUF3XGN/LkjnNy6pRzZH6s2YC27g7sqqFj71xPY+S3KHLy1y/PwJtz1E7EBzqaXxTxDOE9wiPArIoY9Rl//8RalprwBKeEzZajgbqFz5sEkV6hplGCkiua1FfPXht9Ef8br


3.       CryptPassword

private string cryptPassword(string password)
{
   
string key = "AYOOYOYOAYOOYOYO";
   
string salt = "WOLOLOWOLOLOOOO";
   
string str3 = "ndh2k12!";
   
string str4 = null;
   
string data = null;
   
for (int i = 0; i < password.Length; i++)
   
{
       
string str6 = password[i].ToString();
       
string str7 = null;
       
if ((i % 2) == 0)
       
{
            str7
= this.base64Encode(str3 + str6);
       
}
       
else
       
{
            str7
= this.aesEncode(str6, str4, str3);
       
}
        data
= data + str7;
        str4
= str7;
   
}
   
return this.aesEncode(data, key, salt);
}

On parcours donc notre password, si on est sur un caractère pair on ajoute à « data » base64(ndh2k12 ! + char) sinon on ajoute à « data » le caractère chiffré avec la clé « key »  et le salt « salt ».
A la fin, “data” subit une dernière transformation, aesEncode, toujours avec la même key et salt.

Pourquoi ne pas faire l’inverse !

On part du « trustedHash », on sait qu’en dernier il a été chiffré alors on le déchiffre. On obtient ensuite un certain nombre de blocs suivant la taille du mot de passe hashé.
Pour les blocs pairs, il suffit de le décodé en base64 puis de garder le dernier caractère.
Pour les blocs impairs, il faut le déchiffrer avec le bloc précédent comme clé et toujours le même salt.
Ainsi de suite pour chaque bloc pair et impair …

4.       Résultat
Pour déchiffrer, la fonction s’écrit plus ou moins en copiant la fonction de chiffrement :

private string aesDecode(string data, string key, string salt)
{
   
byte[] bytes = new byte[salt.Length];
    bytes
= Encoding.UTF8.GetBytes(salt);
    Aes aes
= (Aes) new AesManaged();
    aes
.Key = new Rfc2898DeriveBytes(key, bytes).GetBytes(0x10);
    aes
.IV = aes.Key;
    MemoryStream stream
= new MemoryStream();
    CryptoStream stream2
= new CryptoStream(stream, aes.CreateDecryptor(), CryptoStreamMode.Write);
   
byte[] buffer = Convert.FromBase64String(data);
    stream2
.Write(buffer, 0, buffer.Length);
    stream2
.FlushFinalBlock();
   
return Encoding.UTF8.GetString(stream.ToArray());
}

Notre “trustedHash” déchiffré donne :
bmRoMmsxMiFomEpoK3JjVufPG4PHIZOr8A==bmRoMmsxMiF2yXWqntvVZX50EN1IlnTo3w==bmRoMmsxMiF3bZY/Kf1nEbIZJkxufD/aAQ==bmRoMmsxMiFs28dlLHl69r6pmBbjIPgYBw==bmRoMmsxMiEtpGqGLudtlrHht7kVeEfMLA==bmRoMmsxMiFlCW4AQUIJeTVAbcdBXbRDQg==bmRoMmsxMiFlMZm7jEzQutN6OwSAVddgAw==bmRoMmsxMiEtVbvy0FmdISllXQlSmMRR0w==bmRoMmsxMiFpWGsPB3wrW20H79tUp8T47w==

On peut ensuite découper ça en bloc :
bmRoMmsxMiFo
mEpoK3JjVufPG4PHIZOr8A==
bmRoMmsxMiF2
yXWqntvVZX50EN1IlnTo3w==
bmRoMmsxMiF3
bZY/Kf1nEbIZJkxufD/aAQ==
bmRoMmsxMiFs
28dlLHl69r6pmBbjIPgYBw==
bmRoMmsxMiEt
pGqGLudtlrHht7kVeEfMLA==
bmRoMmsxMiFl
CW4AQUIJeTVAbcdBXbRDQg==
bmRoMmsxMiFl
MZm7jEzQutN6OwSAVddgAw==
bmRoMmsxMiEt
Vbvy0FmdISllXQlSmMRR0w==
bmRoMmsxMiFp
WGsPB3wrW20H79tUp8T47w==
 
Le premier bloc décodé donne : ndh2k12!h donc le caractère du passe est : h
Le deuxième bloc déchiffré donne : z
On continue comme ça jusqu’au dernier bloc pour obtenir : hzv-will-never-die

NDH 2k12 - Lost USB key - 1000pts

Un autre crackme, Windows cette fois-ci.

1.       What
On a un fichier « crackme.exe » qui demande un mot de passe …
Voici ce que PeID en pense :



Il est donc packé avec UPX. Pour unpacker, il suffit de lancer : upx.exe –d crackme.exe.
Et voilà, c’est unpacké …
On passe la fonction « start » pour arriver à la demande de mot de passe (bloc jaune) :


Après le « fgets », on a en vert la partie qui va chiffrer (transformer légèrement) notre mot de passe, ensuite on passe dans la partie orange qui comparera notre mot de passe chiffré avec le chiffré du bon mot de passe. Si c’est bon, on passe dans la partie rouge, sinon bleu.

Dans le premier bloc vert [ebp-1Ch] contient l’index du caractère que l’on est en train de transformer, il est d’ailleurs initialisé à 0 juste après le fgets.
Ensuite on calcul la taille de notre mot de passe situé à ebp-32h. On compare ensuite les 2 pour savoir si on est à la fin ou pas.

Pour la partie transformation (2ème bloc vert), on va chercher le caractère du mot de passe à l’index [ebp-1Ch], on le XOR avec [ebp-1Dh] qui vaut 0x93 (voir un peu plus haut : mov byte ptr [ebp-1Dh], 93h), puis on incrémente l’index.

On a donc un XOR avec 0x93. Pour trouver le bon mot de passe, il suffit de récupérer le chiffré du bon passe et de le XORer avec 0x93.

2.       Résultat
On break sur le strcmp, on note le chiffré du bon mot de passe.
Cela donne : FE FC FD F8 A0 EA.
Après le XOR on obtient le flag : monk3y

Comme « le python c’est bon », voiçi le xor en Python :
"".join([chr(int(v,16)^0x93) for v in ["FE","FC","FD","F8","A0","EA"]])