Ir al contenido principal

Hydra android malware (Part 2)

Hydra android malware (Part 2)

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:

 -)Muestra: 2fc050a06dab9dbc019dfdd768580ebe66f178a210fda23a02ce005742c5e262

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


Echamos un vistazo al androidManifest.xml:






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
com.apart.already.UDtBwJwGzZaQnHnFbSyFnTzOaQcXsBeCqBfDiDuAdMnGgCzNpZiWqHa


Vemos que dentro de la clase hay un gran grado de ofuscación:





Creación del script en Frida.

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

    -)Hookeando los métodos ofuscados para ir conociendo la funcionalidad de los mismos, e ir poco a poco conociendo que está haciendo la app.


En mi caso he elegido la 2 opción y he creado el siguiente script,

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.



Gracias a la salida de este script no damos cuenta de los siguientes detalles del aplication entrypoint:

Vemos que en AttachBaseContext() la aplicación es donde está cargando dinámicamente el archivo .dex que contiene la carga maliciosa de la imagen.

En este caso el archivo se encuentra dentro de la carpeta de la app, y se llama acO.json

El método onCreate() hace las siguientes acciones, una vez que ya ha sido cargado el archivo dinámicamente.





Aunque el método sadmaze() tiene mucho "dead code", que complica la vida a la persona que está intentando reversear la aplicación, podemos ir "limpiado" el código y deducir su comportamiento.


Un poco de teoría:

Este tipo de  técnica de empaquetar el archivo .dex dentro de la APK es llamada: Android's Shelling Protection Technologies for Application and Reinforcement.

Esta técnica se basa en tener cifrados los archivos .dex de la APK que queremos "ocultar", así no se puede realizar un análisis estáticos a los mismos, y en tiempo de ejecución, descifrarlos y cargarlos en memoria.

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:

2.1) Obtención del archivo .dex file cifrado.



Crear las 2 carpetas donde se van a guardar el archivo .dex ofuscado y la librería (parece que el desarrollador hizo un poco de copy-paste de otra "técnica" y se le ha quedado ahí)

-) /data/user/0/com.apart.already/app_DynamicOptDex
-) /data/user/0/com.apart.already/app_DynamicLib (no USADO)

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 ();

Luego sacamos el nombre del APK con:

apkFileName = odex.getAbsolutePath () + "/acO.json";

Luego descifra el contenido ofuscado del archivo .dex  original (salmonreal()) , haciendo uso de la reflexión(loungedemise()) para la ejecución de las funciones.
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.BufferedInputStream.read(byte[],int,int) --> Lectura.
-)Para el cifrado(networkokay()) usa XOR como muestra la siguiente imagen  
-)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:


Una vez tenemos ya el archivo descifrado y escrito, ya está preparado para cargar el contenido dinámicamente, y se hace a través de la función DynamicLoad():

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);


                

En el onCreate() se llama a la función sadmaze(com.volume.say.App),  después de eliminar el "dead code" y renombrar los métodos ofuscados se nos queda el siguiente código:


    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;
        }
    }

                


    Comienza el método, con la llamada de android.app.ContextImpl(), que es la clase de implementación del método abstracto Context, en el que permite a la aplicación de android el acceso a recursos, actividades, servicios...

Luego se activan los siguientes 2 campos de esta clase:

    -)mOuterContext

    -)mPackageInfo

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:

    -)mApplication

    -)mActivityThread:

Luego se llama a la clase android.app.ActivityThread, y se accede a los campos:

    -)mInitialApplication

    -)mAllApplications: Obtiene un array list y añade la "nueva" apk (com.volume.say.App)

Luego se usa el <activity>.attach(application, null) ,  el segundo parámetro es null para indicarle que no es un proceso del sistema.


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

En esta parte vamos a detallar las características técnicas de esta muestra ( c958980def57cb6776c12f0d2f41347517f57ec1a304d92fa8d0fe40e55b9814 ) , donde tras hacerle ingeniería reversa identificado que tiene la siguientes características.

El panel de administration C&C se obtienen del fichero de las sharedPreferences pref_name_setting  y del valor del campo <admin_panel_url_>. La información es guardada “cifrada” a través del método wrapConstant(str) que utiliza los valores [5,10,16,20] de la string "sorry!need8money[for`food”  y haciendo XOR con la misma.

Para ello la muestra primero conecta con un C2 inicial (a través de TOR):

Que devuelve un Base64 con C2 al que se debe de conectar la muestra. Paralelamente en la muestra están "harcodeados" algunos C2: 

    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");

Este blog lo comencé a escribir en diciembre de 2021, pero claro al ser andaluz... las siestas son sagradas y cuando lo retomé el C2 estaba caído y la parte dinámica de la app no he podido explicarla en detalle, Sin embargo, he observado que hay algunos blogs bastante bien detallados sobre esta muestra como el de @cryptax, y si no te caen bien los franceses por lo que sea xD, también hay otro de los chicos de Cyble.

Mi idea aquí es puntualizar algunas de las características que tiene esta muestra (estoy seguro que algunas familias reutilizaran código de aquí) y hacer un "remember" de mis tiempos de análisis de aplicaciones en android:

CARACTERISTICAS DE HYDRA:

1)Capacidad de obtener información acerca de los contactos, para intentar reproducirse en los mismos. Obteniendo el listado de todos estos y enviando con el método sendBulkSMS() una url para descargarse la aplicación("bulk_body" en el JSON devuelto del C&C --> SharePreferences<Admin_panel_url_> + “device/check” ) a los mismos.








2)Capacidad de envío de SMS premium?? → 3176828231 si la variable “prem_flag” es activada en las sharedPreferences


3)Posibilidad de esconder el icono de la aplicación.


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



Comprueba si la pantalla está bloqueada y en el caso de que así sea, desbloquea la misma y  lanza el servicio “ScreencastService” que comienza a enviar información de la la información capturada de la pantalla.





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:





Adjunto las siguientes imágenes del blog de Cryptax sobre todas posibilidades que ofrece esta familia:


List of commands understood by the BianLian bot. The commands are keys within a JSON object, and values specify command arguments. The JSON object is sent or received from the C&C.


List of Bian Lian bot responses to commands.





 Yo lo dejo por aquí, porque aunque me había metido un poco mas de lleno en las inyecciones de las aplicaciones, pero  al no tener el C2 activo se me queda un poco limitado el análisis dinámico.


Para la próxima espero sacar al menos todas las evidencias a tiempo :)

Happy Reversing.












Comentarios

Entradas populares de este blog

Radare2- Set de comandos básicos

Buenas a todos y Feliz navidad lo primero. Entre mantecado y mantecado, y motivado por el curso de Ricardo Narvaja (CrackLatinos), me he decidido a publicar una entrada que es mas para mí que para el resto. Pero supongo que puede ser útil para otras personas, de ahí que lo ponga lo mas bonito posible en la web. Vamos a ver un "cheatsheet" con comandos útiles de Radare2 y su funcionalidad, todo esto puede extraerse del --help de las herramientas, pero para los que como yo le gusta ir al grano y ahorrar el mayor tiempo posible, tienen esta entrada. RABIN2: rabin2 -I file_.exe --> Nos proporciona información básica del binario, como arquitectura, y las protecciones del binario como (nx,dep,aslr...) rabin2 -i file_.exe --> Nos muestra los imports del binario. rabin2 -e file_.exe --> Dirección del Entry point. (Dirección virtual y raw offset en el executable) rabin2 -zz file_.exe --> Muestra las strings rabin2 -g file_.exe --> Este comando nos muestr

Hydra Android Malware (Part 1)

 Buenos Días, Hoy escribiré una entrada de análisis de malware en Android después de un tiempo sin escribir por aquí. La idea era coger de nuevo soltura con las herramientas open-source que hay para el análisis de aplicaciones android. Estuve leyendo información sobre alguna familias y me tope con Hydra (siguen apareciendo casos activos en Android (Twitter)) y me puse con ella. Arranqué con la lectura de este blog : Es el "padre nuestro" de la primera parte de nuestro tutorial, lo único que voy a hacer yo aquí en la primera parte es "puntualizar" algunos problemas que me he encontrado durante el análisis al seguir su tutorial. Información de la muestra y Herramientas:  -) Muestra : 46aeb04f2f03ebe7c716fc6e58a5dea763cd9b00eb7a466d10a0744f50a7368f -) 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.  -) Ghidra : Decompilado de librerias Nativas. -) GEF : Debu