martes, 27 de diciembre de 2016

Resolviendo el reto Nro 10 Android de ESET Ekoparty 2016 #eko12 - PARTE 2



Como habíamos quedado en la PARTE 1 el CrackmeBaby.apk desencripta un nuevo.apk

content.apk SHA256: 94317deb79ced2ece91b413d142492c76c876a320957a0f21bdf2a8150d6d427





Arrancamos la APP y nos muestra esta imagen, evidentemente algo mal estamos haciendo.





Decompilamos con APK-Multy-tool 

Existen dos activities SuperActivity y SuperbActivity.

Esto lo vemos en el AndroidManifest.xml





Y la actividad que se esta iniciando por default es SuperActivity y vemos en el código que solo muestra una imagen (la de Obi Wan)





SuperbActivity hace cosas mas interesantes y vemos que nunca arranca por lo que vamos a forzar su arranque




Para arrancar la actividad modificamos el AndroidManifest.xml para que arranque la SuperbActivity y luego volvemos a compilar y firmar.




Así vamos a obtener una segunda aplicación que siempre arrancará la actividad correcta para resolver este reto, por lo que a partir de ahora seguiremos trabajando con ella.




La arrancamos y vemos lo siguiente:



Una imagen de Fsociety y un contador decreciente, no tenemos ningún ingreso de datos como en el apk de la Parte 1.


En el código vemos



try
      {
        if ((SuperbActivity.b <= 0.0D) && (SuperbActivity.a < 0))
        {
          SuperbActivity.a += 1;
          SuperbActivity.access$002(SuperbActivity.this, 10.0D * (int)Math.log(Math.pow(SuperbActivity.a, SuperbActivity.a)));
          SuperbActivity.b = SuperbActivity.this.c;
          String str = SuperbActivity.this.getPreferences(4).getString("password", "no_password");
          if (e.SHA1(str).equalsIgnoreCase("249192f0a4f70aa25aa5c7521625f7d6e4021042"))
            new a().execute(new b[] { new b(SuperbActivity.this.getApplicationContext(), d.decrypt(str, "Jhv1Bk0Qi07jOVEeyImynOQmiiz1MvZakazhOaAld-0=")) });
        }
        SuperbActivity.this.t.setText(String.format("Próximo intento: %.1f", new Object[] { Double.valueOf(SuperbActivity.b) }));
        SuperbActivity.b -= 0.1D;
        SuperbActivity.this.g.postDelayed(this, 100L);
        return;
      }
      catch (Exception localException)
      {
        Log.e(SuperbActivity.this.getResources().getString(2131099668), "Oops!");

      }



Podemos observar que por otro lado hay un hash de SHA1 y un d.decrypt que toma como ingreso la cadena Jhv1Bk0Qi07jOVEeyImynOQmiiz1MvZakazhOaAld-0=


Vemos también:


  protected void onCreate(Bundle paramBundle)
  {
......................................
    paramBundle.putString("password", "insert_password_here");
    paramBundle.apply();

  }


Revisando el codigo SMALI veo lo siguiente:


.source "SuperbActivity.java"

  .line 61
    .local v0, "editor":Landroid/content/SharedPreferences$Editor;

    const-string v2, "password"


Significa que existe una shared memory en la cual deberemos poner una clave para que luego sea comparada con el hash del SHA1 y si machea se ejecuta la función d.decrypt("clave",Jhv1Bk0Qi07jOVEeyImynOQmiiz1MvZakazhOaAld-0=)

Busque el hash por internet sin suerte, por lo que intente con hashcat y probar una clave de 10 dígitos incremental....

hashcat64.exe -m 100 -a 3 example0.hash ?s?s?s?s?s?s?s?s?s?s --incremental 

Probé con solo numéricos, con alfa + may/minusculas sin éxito, para luego pasar al set con caracteres espaciales. Hasta los siete caracteres fue rápido y luego paso de horas a días y años... por lo que descartamos esté método debido a que un reto en una confer no nos puede llevar tanto tiempo a menos que tengamos una maquina cuántica...

Probe con hashcat porque al ver a Obi se me vino a la mente "Usa la Fuerza"...fuerza bruta....mmmm.... no...no.... solo fue una trampita :D


La password debería estar en otro lugar, revisando las imágenes de los recursos se las pase al exiftool y en la imagen de fsociety se puede ver lo siguiente en el Thumbnail


exiftool -b -ThumbnailImage image.jpg > my_thumbnail.jpg







Ahora que tengo la password r4bb!th0l3 se la puedo poner en el shared memory con el ADM en /data/data/eset.ekoparty.challenge.crackmeharder/shared_prefs/SuperbActivity.xml







O asignar la password directamente en el SuperbActivity.smali y me olvido.

Lógicamente vuelvo a compilar y firmar la aplicación.


.source "SuperbActivity.java"

  .line 61
    .local v0, "editor":Landroid/content/SharedPreferences$Editor;
    const-string v2, "password"

    const-string v3, "r4bb!th0l3"


Cuando machea el SHA1, como habíamos comentado antes, se 
ejecuta d.decrypt( r4bb!th0l3,Jhv1Bk0Qi07jOVEeyImynOQmiiz1MvZakazhOaAld-0=) 
obteniendo como resultado el string f0ll0w_th3_wh!t3_r4bb!t!

Acá la podemos ver en un dump de memoria previo.




Cada vez que el contador se pone en cero o se da click en el label, el apk llama a la class b y visita un sitio.

        SuperbActivity.this.t.setText(String.format("Próximo intento: %.1f", new Object[] { Double.valueOf(SuperbActivity.b) }));
        SuperbActivity.b -= 0.1D;

        SuperbActivity.this.g.postDelayed(this, 100L);



class b
{
  .................................
  String getUrl()
  {
    return "http://desafioseset.com/";
  }

}


En el LOGCAT se ve lo siguiente





Por otro lado se llama a la Class n a la cual se le pasa el string f0ll0w_th3_wh!t3_r4bb!t!


 SuperbActivity.FLAG = new n().func(localb.getKeyword());



class n
{
  static
  {
    System.loadLibrary("dostuff");
  }


Por lo que si hacemos un DUMP de la memoria.








Obtenemos la FLAG :D

eko_eset_m0b!l35



El APK lo deje modificado para que ingrese directamente a la actividad que corresponde, tome la clave y la imprima en el logcat.







Les dejo el apk original y la modificada con los archivos SMALI que toque.


https://dl.dropboxusercontent.com/u/80008916/Crackmeharder.7z



Eso es todo por este 2016 @Dkavalanche 










lunes, 26 de diciembre de 2016

Resolviendo el reto Nro 10 Android de ESET Ekoparty 2016 #eko12 - PARTE 1


Buenas, decidí terminar el año posteando como resolver este reto Android de ESET que me pareció muy bueno.

Vamos a necesitar APK-Multi-tool, dex2jar, JD-GUI, signapk.jar y un emulador de Android, en mi caso use bigNOX porque tengo una maquina con AMD y el emulador de Google de Windows no permite la aceleración de VM si no se posee un cpu Intel.

El Desafio:

**************************************************
Este es el desafío de reversing en Android.

Para encontrar el flag deberás utilizar técnicas sencillas de análisis de APK y algo de ingenio.

Al comienzo, probablemente debas utilizar algún motor de búsqueda para dar con la contraseña correcta, que es la respuesta a una adivinanza.

Encontrarás pistas que te guiarán en la dirección correcta.

Mucha suerte y happy reversing.


ROMPIENDO CrackMeBabe.apk

SHA256: ca80b0c050233ad6c9d70793ba76effeaffb9ede79508993f965beb04598233d



Cargamos el APK y ni bien lo lanzamos obtenemos la siguiente imagen de un personaje de Tv, por lo que se debe estar haciendo algún tipo de comprobación.





Decompilamos el APK con Multi-tool opción 9, con el obtendremos los archivos SMALI y el classes.dex que luego lo pasaremos por el dex2jar para obtener un .jar para abrirlo con el JD-Gui






d2j-dex2jar.bat classes.dex 

dex2jar classes.dex -> .\classes-dex2jar.jar

JD-Gui




Tenemos varias classes (Login, a, b, c, d, e, f)

Vamos a repasar a ojo de pájaro las principales funciones que me van a permitir sortear el entuerto.


Class f

Función  chk 
(se hacen comprobaciones de que tipo de dispositivo se esta utilizando y lo comprueba con un llamado a la clase d)

 if ((!Build.MODEL.contains(d.h)) && (!Build.MODEL.contains(d.i)) && (!Build.MODEL.contains(d.m)) && (!Build.FINGERPRINT.startsWith(d.k)) && (!Build.FINGERPRINT.startsWith(d.l)) && (!Build.MANUFACTURER.contains(d.n)) && (!Build.PRODUCT.contains(d.c)) && (!Build.MODEL.contains(d.c)))

Función chks(boolean) 

  return Build.VERSION.SDK_INT == 22;

Retorna verdadero si la versión del APLI level es 22 (Android Lollipop 5.0 - 5.1.1)
Por lo que tendríamos que ejecutarlo en un dispositivo con ese nivel de S.O. o emularlo con el AVD correspondiente.



Nombre códigoNúmero de versiónFecha de lanzamientoNivel de API
Apple Pie1.023 de septiembre de 20081
Banana Bread1.19 de febrero de 20092
Cupcake1.527 de abril de 20093
Donut1.615 de septiembre de 20094
Eclair2.0–2.126 de octubre de 20095–7
Froyo2.2–2.2.320 de mayo de 20108
Gingerbread2.3–2.3.76 de diciembre de 20109–10
Honeycomb13.0–3.2.622 de febrero de 201111–13
Ice Cream Sandwich4.0–4.0.418 de octubre de 201114–15
Jelly Bean4.1–4.3.19 de julio de 201216–18
KitKat4.4–4.4.4, 4.4W–4.4W.231 de octubre de 201319–20
Lollipop5.0–5.1.112 de noviembre de 201421–22
Marshmallow6.0–6.0.15 de octubre de 201523
Nougat7.0 - 7.122 de agosto de 201624 - 25

-----------------------------------------------------------------------------------------------------



Class Login en la cual se hacen varias comprobaciones para presentar el frame de login y pasar a la segunda etapa del desafío que es la de desencriptar un recurso y ejecutarlo (otro apk).

Función chkn (boolean) retorna verdadero si la latitud es 40 y la longitud es -74 en la geolocalización del dispositivo.

public boolean chkn()
  {
    int j = 0;
    int i = j;
    if (this.l != null)
    {
      i = j;
      if ((int)this.l.getLatitude() == 40)
      {
        i = j;
        if ((int)this.l.getLongitude() == -73)
          i = 1;
      }
    }
    return i;
  }

Acá tenemos una comprobación importante, se debe cumplir las dos, la chkn (lat:40 y long:-73) y la chks (version del SDK) de la class f para mostrar el frame del login, por otro lado se imprime un hint en el logcat

if ((chkn()) && (f.chks()))


    {
      setContentView(2130968602);
      this.bt = ((Button)findViewById(2131492970));
      this.tx = ((EditText)findViewById(2131492969));
      this.vw = ((TextView)findViewById(2131492971));
      this.vw.setOnClickListener(new c());
      Toast.makeText(this.con, "HINT: *Escucha* lo que Gollum responde y la clave encontrarás, o encuentra el enlace ofuscado que te guiará.", 1).show();
      Log.i(d.z, "HINT: *Escucha* lo que Gollum responde y la clave encontrarás, o encuentra el enlace ofuscado que te guiará.");
      return;
    }

Tenemos un hash md5 que corresponde a una clave a ingresar.


  public void onClick(View paramView)
  {
    if (((paramView instanceof Button)) && (paramView == this.bt))
      try
      {
        paramView = this.tx.getText().toString();
        MessageDigest localMessageDigest = MessageDigest.getInstance("MD5");
        localMessageDigest.update(paramView.getBytes("UTF-8"));
        paramView = String.format("%032x", new Object[] { new BigInteger(1, localMessageDigest.digest()) });
        if (!paramView.equalsIgnoreCase("16cc45d14201e8caf26d48e636b1c48d"))


Buscamos en internet el MD5 y corresponde a  3gg535


Desencripción del recurso "enc" localizado en assets/ mediante la class b  función b.decrypt();





protected String doInBackground(String[] paramArrayOfString)
    {
      try
      {
        File localFile = new File(Environment.getExternalStorageDirectory().getPath() + "/content.apk");
        if ((localFile.exists()) && (localFile.length() > 0L))
          return "Error";
        InputStream localInputStream = Login.this.getApplicationContext().getResources().getAssets().open("enc");
        FileOutputStream localFileOutputStream = new FileOutputStream(localFile);
        b.decrypt(paramArrayOfString[0], localInputStream, localFileOutputStream);
        paramArrayOfString = new Intent("android.intent.action.VIEW");
        paramArrayOfString.setDataAndType(Uri.fromFile(localFile), "application/vnd.android.package-archive");
        Login.this.startActivity(paramArrayOfString);
        this.blnResult = true;
        return "Executed";
      }
      catch (Exception paramArrayOfString)
      {
        while (true)
        {
          paramArrayOfString.printStackTrace();
          Log.e(d.z, paramArrayOfString.getMessage());
        }
      }
    }


-----------------------------------------------------------------------------------------------------

Class d donde se llama a la class e y se desencriptan varios strings que serán utilizados en la class f para comprobar en que tipo de dispositivo especifico se esta ejecutando y desviar un poco la atención del reverser.

package eset.ekoparty.challenge.crackmebabe;

public class d
{
  public static String a = "12321322869";
  public static final String b = e.pqz("htDaDYyZOg==");
  public static final String c = e.pqz("tNrV");
  public static final String g = e.pqz("5Y6OT9PAbvGYtDsxJChCQI0=");
  public static final String h = e.pqz("5dnREIScO57b4GAj");
  public static final String i = e.pqz("5fvTCo+RKq7apg==");
  public static final String k = e.pqz("5dnbEYaCN6KK");
  public static final String l = e.pqz("5cvQFI2fKa+K");
  public static final String m = e.pqz("5f/QG5GfN6WI109KNHoHGcNbxcOzZ57tl8xC");
  public static final String n = e.pqz("5fnbEZqdMbXB62Uj");
  public static final String y = e.pqz("r8rKD9nfca3F439nbTYRH8IA2tThctH5w48NXoLVdHkq1Yo=");
  public static final String z = e.pqz("hMzfHIjQE6SIxmpjcQ==");
}


Decodificadas quedan

a = 12321322869
b = Android
c = sdk
g = 000000000000000
h = google_sdk
i = Emulator
k = generic
l = unknown
m = Android SDK built for x86
n = Genymotion
y = http://lmgtfy.com/?q=gollum+riddles
z = Crack Me Babe

-----------------------------------------------------------------------------------------------------


Class e se encarga de pasar a texto las cadenas base64 encontradas en la classe d

public class e
{
  static String pqz(String paramString)
  {
    try
    {
      paramString = new String(new a(d.a.getBytes("UTF-8")).a(Base64.decode(paramString.getBytes(), 0)), "UTF-8");
      try
      {

        String str = paramString.replace("\"", "");


-----------------------------------------------------------------------------------------------------


Bueno como salimos de este lio....

La clave la tenemos que es 3gg535 quiere decir que lo mas difícil son las comprobaciones de las funciones de las class Login y F

Entonces vamos a modificar el código SMALI  para que nos permita pasar a la pantalla de ingreso de password y lograr desencriptar el recurso enc.

Luego de modificar los .SMALI  re-compilamos con el APK-Multi-tool y firmamos con el signapk.jar 



Modificando Login.SMALI


Código java:

Cambiamos el  if ((chkn()) && b(f.chks())) por  if ((f.chks()) && (f.chks())) 


if ((f.chks()) && (f.chks())) 
    {
      setContentView(2130968602);
      this.bt = ((Button)findViewById(2131492970));
      this.tx = ((EditText)findViewById(2131492969));
      this.vw = ((TextView)findViewById(2131492971));
      this.vw.setOnClickListener(new c());
      Toast.makeText(this.con, "HINT: *Escucha* lo que Gollum responde y la clave encontrarás, o encuentra el enlace ofuscado que te guiará.", 1).show();
      Log.i(d.z, "HINT: *Escucha* lo que Gollum responde y la clave encontrarás, o encuentra el enlace ofuscado que te guiará.");
      return;

    }

En .SMALI sería:

    .line 63
    :cond_1
    invoke-static {}, Leset/ekoparty/challenge/crackmebabe/f;->chks()Z

    move-result v0

    if-eqz v0, :cond_2

    invoke-static {}, Leset/ekoparty/challenge/crackmebabe/f;->chks()Z

    move-result v0


    if-eqz v0, :cond_2


Modificando f.SMALI

Código java:

Modificamos  chks()  para que retorne verdadero si utilizamos una versión menor a 99, con esto podemos probarlo en cualquier API level.



  public static boolean chks()
  {
    return Build.VERSION.SDK_INT < 99;

  }


En .SMALI sería:

    sget v0, Landroid/os/Build$VERSION;->SDK_INT:I

    const/16 v1, 0x63 
                   
    if-ge v0, v1, :cond_0


Compilamos con opción 12 de APK-Multi-tool




Luego firmamos el APK con signapk.jar


set usrc=0
set heapy=512
echo Signing Apk
java -Xmx%heapy%m -jar signapk.jar -w testkey.x509.pem testkey.pk8 in.apk out.apk




Instalamos y ejecutamos el APK.


Y vemos que ya paso de largo las comprobaciones y nos muestra el ingreso de la password.



Ingresamos la password 3gg535 y se descifra el recurso enc.





Para luego solicitar ser instalado.







Instalamos y ejecutamos.... pero lo vemos la próxima.


Les dejo el apk original, el apk parchado y los .SMALI para que pueda hacerlo 
Uds. en sus casas.

https://dl.dropboxusercontent.com/u/80008916/CrackMeBabe.7z


Próxima entrega antes de fin de año :D

Eso es todo por el momento @Dkavalanche 2016 







 Hola, me mudé a medium .... nos vemos!!! FELIZ 2022!!!