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.
-----------------------------------------------------------------------------------------------------
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