NDH2k11 Prequals - Rce200
By MaZ on Tuesday, April 5 2011, 16:40 :: Prequals ndh2k11 :: Permalink
Deuxième épreuve de Reverse qui avait pour support une application Android.
Fichier : "RCE200.apk"
Fort de la lecture des slides de Virtualabs concernant le reverse d'application Android, nous savions que les sources Java de l'application étaient disponibles grâce à 2 outils : Dex2Jar (Transformer le .apk en .jar contenant des .class (Bytecode Java)) et JD-GUI (Permet l'affichage des .class en .java).
Nous découvrons que l'application se compose de 4 fichiers :
- "ReverseMe.java"
- "a.java"
- "b.java"
- "c.java"
Tout d'abord, je tiens à préciser que j'ai pas utilisé de device Android pour résoudre cette épreuve ni utilisé d'émulateur, seule la lecture des sources m'a permis de choper le flag.
Allez, c'est parti pour de l'analyse !
Commençons par le fichier "c.java"
package ndh.prequals.rce; public final class c { private static byte[] a = { 90, 5, 88, 88, 13, 13, 90, 90, 10, 4, 9, 11, 93, 90, 11, 15, 93, 95, 5, 93, 5, 8, 8, 88, 90, 95, 9, 14, 90, 8, 13, 94 }; private static byte[] b = { 91, 83, 83, 91, 80, 89, 99, 79, 88, 87 }; private static byte[] c = { 113, 120, 9 }; private static byte[] d = { 93, 82, 88, 78, 83, 85, 88, 18, 79, 76, 89, 89, 95, 84, 18, 93, 95, 72, 85, 83, 82, 18, 110, 121, 127, 115, 123, 114, 117, 102, 121, 99, 111, 108, 121, 121, 127, 116 }; private static byte[] e = { 111, 116, 125, 17, 13 }; public static String a() { return a(a); } private static String a(byte[] paramArrayOfByte) { byte[] arrayOfByte = new byte[paramArrayOfByte.length]; int i = 0; while (true) { int j = paramArrayOfByte.length; if (i >= j) return new String(arrayOfByte); int k = (byte)(paramArrayOfByte[i] ^ 0x3C); arrayOfByte[i] = k; i += 1; } } public static String b() { return a(b); } public static String c() { return a(c); } public static String d() { return a(d); } public static String e() { return a(e); } }
Les attributs a,b,c,d,e sont des strings, mis sous forme de tableau de Byte.
On voit très clairement que la fonction private static String a(byte paramArrayOfByte) {} effectue un XOR chaque caractère de la chaine d'entrée et la clé 0x3C.
Réflexe: passer les strings a,b,c,d,e dans la fonction a() histoire de voir quelles nouvelles chaines on obtient (chars imprimables ou non).
Résultat:
a="f9dd11ff6857af73ac9a944dfc52f41b"
b="google_sdk"
c="MD5"
d="android.speech.action.RECOGNIZE_SPEECH"
e="SHA-1"
Tiens tiens, "MD5", "SHA-1" ça sent le hash là dedans garçon !
Au passage, on remarque avec lucidité que la string a fait 32 caractères, on essaie donc de voir si ce n'est pas le hash MD5 d'une quelconque chaine.
Et là bingo, passcracking.ru nous indique que c'est le hash MD5 de "salope".
Élégant non ?
Comme je l'ai dit plus haut je n'avais pas de device Android durant cette épreuve. A ce moment de l'épreuve, je ne savais pas donc pas que la saisie de notre input se faisait via une méthode de Speech-to-Text. La chaine "android.speech.action.RECOGNIZE_SPEECH" m'a mis sur la piste.
Passons au fichier "a.java"
package ndh.prequals.rce; import android.os.Build; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public final class a { public static String a(String paramString) { try { MessageDigest localMessageDigest = MessageDigest.getInstance(c.e()); //c.(e)="SHA-1" byte[] arrayOfByte = paramString.getBytes(); localMessageDigest.update(arrayOfByte); localObject1 = localMessageDigest.digest(); StringBuffer localStringBuffer1 = new StringBuffer(); int i = 0; int j = localObject1.length; if (i >= j) { localObject1 = localStringBuffer1.toString(); return localObject1; } String str; for (Object localObject2 = Integer.toHexString(localObject1[i] & 0xFF); ; localObject2 = str) { if (((String)localObject2).length() >= 2) { StringBuffer localStringBuffer2 = localStringBuffer1.append((String)localObject2); i += 1; break; } str = "0" + (String)localObject2; } } catch (NoSuchAlgorithmException localNoSuchAlgorithmException) { while (true) Object localObject1 = null; } } public static boolean b(String paramString) { try { MessageDigest localMessageDigest = MessageDigest.getInstance(c.c());//c.(c)="MD5" byte[] arrayOfByte1 = paramString.getBytes(); localMessageDigest.update(arrayOfByte1); byte[] arrayOfByte2 = localMessageDigest.digest(); StringBuffer localStringBuffer1 = new StringBuffer(); String str1 = c.b(); String str2 = Build.PRODUCT; if (str1.equals(str2)) StringBuffer localStringBuffer2 = localStringBuffer1.append(65); int i = 0; int j = arrayOfByte2.length; if (i >= j) { String str3 = localStringBuffer1.toString(); String str4 = c.a(); bool = str3.equals(str4); return bool; } String str5; for (Object localObject = Integer.toHexString(bool[i] & 0xFF); ; localObject = str5) { if (((String)localObject).length() >= 2) { StringBuffer localStringBuffer3 = localStringBuffer1.append((String)localObject); i += 1; break; } str5 = "0" + (String)localObject; } } catch (NoSuchAlgorithmException localNoSuchAlgorithmException) { while (true) boolean bool = false; } } }
Une analyse rapide des méthodes a() et b() de cette classe nous indique que:
- a() nous renvoi le hash SHA-1 de la chaine passée en paramètre
- b() compare le md5 du mot prononcé avec c.a() ( c.a()="f9dd11ff6857af73ac9a944dfc52f41b"=md5("salope") )
Voici le contenu du fichier "b.java"
package ndh.prequals.rce; import android.content.Intent; import android.view.View; import android.view.View.OnClickListener; final class b implements View.OnClickListener { b(ReverseMe paramReverseMe) { } public final void onClick(View paramView) { ReverseMe localReverseMe = this.a; if (paramView.getId() == 2131034114) { String str = c.d(); Intent localIntent1 = new Intent(str); Intent localIntent2 = localIntent1.putExtra("android.speech.extra.LANGUAGE_MODEL", "free_form"); Intent localIntent3 = localIntent1.putExtra("android.speech.extra.PROMPT", "Enter password"); localReverseMe.startActivityForResult(localIntent1, 1234); } } }
Cette classe gère la partie graphique, met en place les Listeners et gère l'évenement onClick (qui correspond au click tactile de l'utilisateur, pour valider sa saisie)
Donc pas grand chose à tirer de cette classe.
Et enfin, voici la classe la plus intéressante "ReverseMe.java"
package ndh.prequals.rce; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.widget.Button; import android.widget.TextView; import java.util.ArrayList; import java.util.List; public class ReverseMe extends Activity { private a a = null; private TextView b = null; protected void onActivityResult(int paramInt1, int paramInt2, Intent paramIntent) { if ((paramInt1 == 1234) && (paramInt2 == -1)) { ArrayList localArrayList = paramIntent.getStringArrayListExtra("android.speech.extra.RESULTS"); if ((!localArrayList.isEmpty()) && (a.b((String)localArrayList.get(0)))) { TextView localTextView = this.b; String str = a.a((String)localArrayList.get(0)); localTextView.setText(str); } } super.onActivityResult(paramInt1, paramInt2, paramIntent); } public void onCreate(Bundle paramBundle) { super.onCreate(paramBundle); setContentView(2130903040); Button localButton = (Button)findViewById(2131034114); TextView localTextView = (TextView)findViewById(2131034113); this.b = localTextView; PackageManager localPackageManager = getPackageManager(); String str1 = c.d(); Intent localIntent = new Intent(str1); if (localPackageManager.queryIntentActivities(localIntent, 0).size() != 0) { String str2 = c.b(); String str3 = Build.PRODUCT; if (!str2.equals(str3)) { b localb = new b(this); localButton.setOnClickListener(localb); } } a locala = new a(); this.a = locala; } }
La méthode onCreate() va juste instancier les élements graphiques de l'application.
La méthode onActivityResult() gère les interactions entre les éléments de cette application, donc entre ce que l'on va prononcer et le fait de réussir ou non l'authentification.
Rapide commentaire des principales instructions de la méthode onActivityResult().
ArrayList localArrayList = paramIntent.getStringArrayListExtra("android.speech.extra.RESULTS"); /* Récupère ce que l'utilisateur a prononcé et le met dans localArrayList */ if ((!localArrayList.isEmpty()) && (a.b((String)localArrayList.get(0)))) /* Si localArrayList n'est pas vide et que l'utilisateur a prononcé "salope" .... */ String str = a.a((String)localArrayList.get(0)); localTextView.setText(str); /* ...alors le hash SHA-1 de "salope" est affiché. */
Le flag de cette épreuve était donc "913beccad686975f8c686d9b3b1ee6bb97c22d6f"
Épreuve originale, accessible et fun !
Que demande le peuple ?

Comments
Unhealthy- Are you aware of the fact that using the inferior
headphones is very unhealthy? If you were not aware of this fact,
it is high time that you did. Making use of inferior headphones
could really hurt your ears and might cause you tinnitus problem.
http://www.ccedelcaribe.com/cheapbe...