Buenas de nuevo,
En esta segunda parte del tutorial de Hydra, vamos a coger una muestra mas reciente, y vamos a ahondar en las características técnicas de la muestra.
Información de la muestra y Herramientas:
-)JADX: Para poder leer el codigo Java/kotlin decompilado de la aplicación.
-)Apktool: Para hacer el dissasembler de la APK, y poder parcherla y reconstruirla.
-)Genymotion: Para emular la aplicación
-)Burp Suite: para capturar las comunicaciones
-)Frida: Para TODO :)
Análisis Estático
Antes de comenzar con el archivo AndroidManifest.xml, como explicamos en la entrada anterior, vamos a introducir un poco de teoría de como es cargada y ejecutada una aplicación sobre el sistema operativo Android.
Al igual que en la entrada anterior, hay referencias a actividades que no se encuentran presentes en el código de la aplicación al abrirlo con la herramienta JADX.
1)Unpacking
Por lo tanto, nos vamos al application entrypoint:
Con este grado de ofuscación, existen 2 maneras de poder conocer el comportamiento de la aplicación:
-)Cambiando el AndroidManifest.xml añadiendo el archivo como debuggeable y recompilando la aplicación con apktool. Tras instalar la "nueva" aplicación podemos debuggear la aplicación usando "smalidea" con AndroidStudio, o usar del debuggeador de JADX (este último me ha dado problemas).
Hemos hookeado todos los métodos ofuscados que son llamados dentro de los métodos AttachBaseContext() y OnCreate(), y hemos ido obteniendo una idea general de que está haciendo la aplicación.
Con la salida de nuestro script, podemos ver que métodos de la clase se van ejecutando primero e ir teniendo una idea del flujo del programa.
En este caso el archivo se encuentra dentro de la carpeta de la app, y se llama acO.json
El proceso de cifrado y empaquetado del archivo .dex (original) es el siguiente:
1)En este caso el archivo con el código que el desarrollador de malware quiere ofuscar se encuentra dentro de la carpeta assets: con el nombre Ac0.json.
2) El "Shelled"(packed) APK, extrae y descifra el código de Ac0.json y lo carga en memoria antes de que comience la interacción con el usuario. Por lo tanto, el mejor momento es antes de que sea disparado el método onCreate().
Vamos a ir viendo los paso a seguir dentro de este punto:
protected void attachBaseContext (Context base) {
super.attachBaseContext (base);
try {
//Create two folders payload_odex, payload_lib private, writable file directory
File odex = this.getDir ("app_DynamicOptDex", MODE_PRIVATE);
File libs = this.getDir ("app_DynamicLib", MODE_PRIVATE);
dexPath = odex.getAbsolutePath ();
libPath = libs.getAbsolutePath ();
apkFileName = odex.getAbsolutePath () + "/acO.json";
Comienza indicando al programa el path de los assets con la función obsoleta addAssetPath(), abriendo el asset que contiene el .dex cifrado ("ac0.json") y escribiendo una vez decifrado en el archivo /data/user/0/com.apart.already/app_DynamicOptDex/acO.json.
assetPath = android.content.res.AssetManager.addAssetPath("/acO.json"); Assets[] = android.app.ContextImpl.getAssets(); Original_apk = AssetManager.open("ac0.json"); File_to_write = File("/data/user/0/com.apart.already/app_DynamicOptDex/acO.json"); byte [] dexdata = this.readDexFileFromAsset(); //Make dynamicLoad this.DynamicLoad(dexdata);
La función readDexFilefromAsset(), se encarga de Lectura/descifrado/escritura a través del uso de las APIs:
-)java.io.FileOutputStream.write(byte[]) --> Escritura en el nuevo archivo.
Para obtener la los primeros bytes del descifrado, hemos usado el siguiente script en FRIDA.
Key:
ArrayMap mPackages = (ArrayMap) RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread,"mPackages");
WeakReference wr = (WeakReference) mPackages.get(packageName);
// Cree el objeto DexClassLoader del apk empaquetado, cargue la clase y el código nativo en el apk (código c / c ++)
DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath,libPath, (ClassLoader) RefInvoke.getFieldOjbect("android.app.LoadedApk", wr.get(), "mClassLoader"));
// Establece el mClassLoader del proceso actual en el DexClassLoader del apk empaquetado
RefInvoke.setFieldOjbect("android.app.LoadedApk", "mClassLoader",wr.get(), dLoader);
private boolean sadmaze(String str) {
try {
Application application = (Application) Class.forName(str, true, getClassLoader()).newInstance();
Class<?> cls = Class.forName(android.app.ContextImpl().trim());
cls.getCanonicalName();
Field declaredField = cls.getDeclaredField(mOuterContext().trim());
declaredField.getClass().getDeclaredClasses();
declaredField.setAccessible(true);
declaredField.isEnumConstant();
declaredField.set(null, application);
declaredField.getAnnotations();
Field declaredField2 = cls.getDeclaredField(mPackageInfo().trim());
declaredField2.getClass().isPrimitive();
declaredField2.setAccessible(true);
declaredField2.toGenericString();
Object obj = declaredField2.get(null);
Class<?> cls2 = Class.forName(android.app.LoadedApk().trim());
cls2.isAnonymousClass();
Field declaredField3 = cls2.getDeclaredField(mApplication().trim());
declaredField3.getClass().getDeclaredFields();
declaredField3.setAccessible(true);
declaredField3.getType();
declaredField3.set(obj, application);
declaredField3.getAnnotations();
Field declaredField4 = cls2.getDeclaredField(mActivityThread().trim());
declaredField4.getClass().isEnum();
declaredField4.setAccessible(true);
declaredField4.getModifiers();
Object obj2 = declaredField4.get(obj);
Class<?> cls3 = Class.forName(android.app.ActivityThread().trim());
cls3.getClassLoader();
Field declaredField5 = cls3.getDeclaredField(mInitialApplication().trim());
declaredField5.getClass().getEnumConstants();
declaredField5.setAccessible(true);
declaredField5.isEnumConstant();
declaredField5.set(obj2, application);
declaredField5.getName();
Field declaredField6 = cls3.getDeclaredField(mAllApplications().trim());
declaredField6.getClass().getComponentType();
declaredField6.setAccessible(true);
declaredField6.getName();
ArrayList arrayList = (ArrayList) declaredField6.get(obj2);
arrayList.getClass().isAnnotation();
arrayList.add(application);
arrayList.remove((Application) getApplicationContext());
Method declaredMethod = Application.class.getDeclaredMethod(attach().trim(), Context.class);
declaredMethod.getAnnotations();
declaredMethod.setAccessible(true);
declaredMethod.getExceptionTypes();
declaredMethod.invoke(application, null);
application.onCreate();
return true;
} catch (Exception unused) {
return false;
}
}
Luego se obtiene la clase android.app.LoadedApk(), que se utiliza para crear información de la nueva ".apk" y se activa los siguientes 2 campos:
En el siguiente enlace podemos obtener algo mas de información de este proceso.Me gustaría conocer más acerca del funcionamiento de estos campos, si alguien conoce algún "paper" o reseña interesante y le gustaría compartirla, es bienvenida.
2)Características de la muestra ac0.json
http://dashb646pjn3hfl6.onion/api/mirrors (actualmente caida)
ADMIN_PANEL_URLS_1("https://babosiki.buzz"),
ADMIN_PANEL_URLS_2("https://trustpoopin.xyz"),
ADMIN_PANEL_URLS_3("https://trygotii.xyz"),
ADMIN_PANEL_URLS_4("https://trytogoi.xyz");
4)Requerir la habilitación del accessibility service:
5) Comprueba los permisos para realizar “ScreenCast”: Para realizar esto utiliza el paquete de android “android.media.projection.MediaProjection”.
6) Configurar la aplicación como default SMS_handler:
Lo que le permite leer todos los SMS que son recibidos y enviar información del número y el contenido de los SMS al C&C o enviar SMS específicos con información indicada a través de FCM :
7) InjAccessibilityService
Este servicio tiene el permiso accessibility asociado, y por lo tanto da una gran cantidad de posibilidades a los desarrolladores de malware.
Dentro de este servicio nos vamos a focalizar en el método onAccessibilityEvent(), este método es llamado cada vez que ocurre un evento en el dispositivo.Vemos que dentro de este método se hace lo siguiente:
- )Obtenemos el PackageName
- )Obtenemos el tipo de evento:
- )Si el evento es 0x32 (TYPE_WINDOW_STATE_CHANGED) modificamos el valor de campo currentAppId, para tener siempre localizada que aplicación está corriendo.
Ademas, utiliza el servicio accesibility para poder aprovechar las ventajas de este permiso en funcionalidades como:
- )ScreenCastComponent
- )SwitchSoundComponent
- )Disable Samsung/Huawei and general security protections
Dependiendo del modelo del dispositivo llama a un método o a otro, para bypassear posibles protecciones extras de esos modelos:
-)Samsung --> Para bypassear algún mecanismo de protección en las setting ( con la información de los botones no he sido capaz de saber qué protección es)
-)Huawei --> Para bypassear la función de "com.huawei.systemmanager" aplicaciones protegidas.
-)Xiaomi --> Gestionar el Xiaomi autostart.
- )Si el SDK > 23
Utiliza las posibilidades del servicio de accesibilidad para modificar la configuración del dispositivo y añadir la aplicación como "DO NOT DISTURB ACCESS" a la aplicación a través de (android.provider.Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS).
Con el uso de este modo aunque el teléfono se encuentre en modo no molestar, la aplicación maliciosa seguirá teniendo acceso a las notificaciones.
- )Obtención del código PIN:
Desde el C&C + el servicio accesibility, se puede forzar al usuario a modificar el PIN en el dispositivo y reemplazarlo por el indicado desde el C&C, esto permite a los atacantes poder “capturar” el dispositivo y pedir un rescate para “desbloquearlo” o desbloquear el dispositivo cuando sea requerido en otros servicios.
- Bloqueo con overlay de pantalla:
Uso de OverlayLayout Param, para establecer "bloquear" el dispositivo a imagen del usuario.
- Forzar la creación de un nuevo patron del movil:
Se llama al metodo onAccessiblityEvent() de la clase PinComponent donde comprueba que la clase sea "com.android.settings.password.ConfirmLockPassword" y si el el getInputText=129 (pin de letras y numeros no visibles) o el caso del getinputText=18(Solo pin numérico)
8)Uso del USSD code *101#, este código se utiliza para devolver la cantidad de saldo disponible que tenemos en el teléfono, y la envía al panel de control.
9) Uso de Socks5 Module:
Este módulo es usado para cerrar un servidor SSH usando JSCH (Java secure Channel), una librería que implementa SSH2 en puro Java, pudiendo configurar un proxy que puede correr sesiones SSH usando port forwarding al 34500 haciendo la comunicación más difícil de detectar.
9) Descarga de un nuevo dex para comprobar si GPP es activo:
Comentarios
Publicar un comentario