DIY: Android Malware Analysis – Taking Apart OBAD (Part 1)

It has been much longer that what I intended for a follow-up post on OBAD analysis, but better late than never. In this post we’ll look at the following:

Let’s give OBAD a run

Last time we discussed about various tools for analysis, setting up the app to be debugged in jdb, identifying anti-emulator code, hacking and compiling AOSP code and then running the emulator with our modified system image to bypass antivm check. So now that you can bypass the anti vm checks, if you run OBAD in the emulator you would see it asking for enabling it as a DeviceAdmin

So it uses the label “System” for itself and if the user denies the request, it will keep asking for it. Once it is activated as a device admin, launcher icon disappears and it does not even appear as a device admin in the Device Admin list that can be seen via Settings -> Security -> Device Administrators.

The figure above shows that there is no app launcher icon visible for OBAD, and that is why in our previous post we mentioned that you have to manually launch the activity as we don’t see a launcher icon for it.  If you go and take a list of device administrators for the emulator/device you would not see it there either. The picture below shows how you would expect to see it listed as a Device Administrator

Here is what you would see in vulnerable Android (more about the vulnerability later)

So we don’t have a launcher icon, and we don’t have it in the device administrator’s list, may be our AV product detected it and deleted it? Let’s check it in the apps list (Settings -> apps), and you will find it there and perhaps we can then uninstall it from there, can you? Let’s take a look at the image below:

The uninstall button is disabled, but one might say ‘Hey! wait a second, I am better than this I can use the “adb uninstall”  command, and if you try that then you will fail and if you look at logcat output you will learn the following:

[email protected]:~/Malware/OBAD$ adb uninstall com.android.system.admin
Failure

[email protected]:~/Malware/OBAD$ adb logcat -d -b main -b events | grep admin | tail -1
W/PackageManager( 277): Not removing package com.android.system.admin: has active device admin

We can’t deactivate the device admin from Settings menu as it is not even listed as a device administrator and hence we can’t uninstall it. Let’s analyze how it does all this.

How to avoid process being killed due to Activity Not Responding (ANR)

In my previous blog entry for analyzing OBAD, we showed how one can trace methods entered and exited, after dealing with the AntiVM check, if you try to trace methods again you would see your process being killed and you will the following in logcat output

W/ActivityManager( 291): Timeout executing service: ServiceRecord{421b03d8 u0 com.android.system.admin/.MainService}
...
W/ActivityManager( 291): Killing ProcessRecord{421b77a0 1129:com.android.system.admin/u0a10053}: background ANR

Here is how you can patch Android code to avoid it:

[email protected]:~/Android/src_4.3_r3$ diff ./frameworks/base/services/java/com/android/server/am/ActiveServices.java.modified ./frameworks/base/services/java/com/android/server/am/ActiveServices.java.orig
1852,1857c1852
<  //Slog.w(TAG, " .... Hijacked ANR appnotresponding call for debugged app");
<  if (!proc.debugging)
<   mAm.appNotResponding(proc, null, null, false, anrMessage);
<  else
<   Slog.w(TAG, "prevented ANR on debuggee app - Hijacked ANR appnotresponding call for debugged app");
<
---
>  mAm.appNotResponding(proc, null, null, false, anrMessage);

=> for broadcast ANR
[email protected]:~/Android/src_4.3_r3/frameworks/base/services/java/com/android/server$ diff  am/BroadcastQueue.java  am/BroadcastQueue.java.orig
956,959c956
<  if (!app.debugging)
<   mHandler.post(new AppNotResponding(app, anrMessage));
<  else
<   Slog.w(TAG, "prevented ANR on (broadcast) debuggee app - Hijacked ANR appnotresponding call for debugged app");
---
>  mHandler.post(new AppNotResponding(app, anrMessage));

How did the launcher icon disappear?

I traced method entries as I described in my last post, and use the following exclude command in jdb to avoid tracing standard java code but still trace the methods invoked by reflection.

exclude android.os.*,org.apache.*,java.lang.D*,java.lang.N*,java.lang.P*,java.lang.U*,java.lang.F*,java.lang.Ru*,java.lang.E*,java.lang.T*,java.lang.V*,java.lang.I*,java.lang.A*,java.lang.S*,java.lang.B*,java.lang.ref.*,java.lang.C*,java.lang.O*,java.lang.S*,java.lang.V*,javax.*,sun.*,com.sun.*,java.s*,java.u*,java.s*,java.n*,java.i*,java.lang.reflect.A*,java.lang.reflect.C*,java.lang.reflect.F*,java.lang.reflect.Method.g*,java.lang.reflect.Method.<*

I noticed a call to android.app.ApplicationPackageManager.setComponentEnabledSetting being done via reflection, which can be examined further in jdb by setting a breakpoint.

Breakpoint hit: "thread=<1> main", android.app.ApplicationPackageManager.setComponentEnabledSetting(), line=1,262 bci=0

<1> main[1] wherei
 [1] android.app.ApplicationPackageManager.setComponentEnabledSetting (ApplicationPackageManager.java:1,262), pc = 0
 [2] java.lang.reflect.Method.invokeNative (native method)
 [3] java.lang.reflect.Method.invoke (Method.java:525), pc = 17
 [4] com.android.system.admin.cCoIOIOo.ollIIIc (null), pc = 748
 [5] com.android.system.admin.cCoIOIOo.onActivityResult (null), pc = 9
 [6] android.app.Activity.dispatchActivityResult (Activity.java:5,322), pc = 7
 [7] android.app.ActivityThread.deliverResults (ActivityThread.java:3,363), pc = 38
 [8] android.app.ActivityThread.handleSendResult (ActivityThread.java:3,410), pc = 172
 [9] android.app.ActivityThread.access$1100 (ActivityThread.java:141), pc = 0
 [10] android.app.ActivityThread$H.handleMessage (ActivityThread.java:1,304), pc = 229
 [11] android.os.Handler.dispatchMessage (Handler.java:99), pc = 20
 [12] android.os.Looper.loop (Looper.java:137), pc = 84
 [13] android.app.ActivityThread.main (ActivityThread.java:5,103), pc = 56
 [14] java.lang.reflect.Method.invokeNative (native method)
 [15] java.lang.reflect.Method.invoke (Method.java:525), pc = 17
 [16] com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:737), pc = 11
 [17] com.android.internal.os.ZygoteInit.main (ZygoteInit.java:553), pc = 70
 [18] dalvik.system.NativeStart.main (native method)
<1> main[1] locals
Method arguments:
componentName = instance of android.content.ComponentName(id=830033869864)
Local variables:
newState = 2
flags = 1
<1> main[1] print componentname
com.sun.tools.example.debug.expr.ParseException: Name unknown: componentname
componentname = null
<1> main[1] print componentName
componentName = "ComponentInfo{com.android.system.admin/com.android.system.admin.cCoIOIOo}"
<1> main[1]

Why don’t we see the OBAD listed as a Device Administrator?

If you are trying this with one of the latest build of Android, you would notice that OBAD is actually listed as a device administrator, the reason is that this vulnerability has already been fixed. To understand the vulnerability we can simply look at patch logs and analyze the patches, I think it would be more interesting to take a look at it without seeing the patch. So what code gets executed when you open up Settings -> Security -> Device Administrators? You can open it up and then take a look at the output from logcat from event buffer you would see:
adb logcat -d -b events

...

I/am_new_intent( 276): [0,1106566944,17,com.android.settings/.Settings,android.intent.action.MAIN,NULL,NULL,274726912]
I/am_resume_activity( 276): [0,1106900904,17,com.android.settings/.Settings]
I/am_on_resume_called( 1118): [0,com.android.settings.Settings]
I/am_create_activity( 276): [0,1107200744,17,com.android.settings/.SubSettings,android.intent.action.MAIN,NULL,NULL,0]
I/am_pause_activity( 276): [0,1106900904,com.android.settings/.Settings]
I/am_on_paused_called( 1118): [0,com.android.settings.Settings]
I/am_restart_activity( 276): [0,1107200744,17,com.android.settings/.SubSettings]

So this tells us where to start looking for the code that lists device administrators, we can search for it on androidxref and find Settings.java and there you will find use of R.id.security_settings, if you search for it you will find it in settings_headers.xml as:

<!-- Security -->
127 <header
128  android:<b>fragment=</b>"com.android.settings.SecuritySettings"
129  android:<b>icon=</b>"@drawable/ic_settings_security"
130  android:<b>title=</b>"@string/security_settings_title"
131  android:<b>id=</b>"@+id/security_settings" />

So let’s take a look at com.android.settings.SecuritySettings, you will see it creating device admin preference category as:

PreferenceGroup deviceAdminCategory= (PreferenceGroup)
261    root.findPreference(KEY_DEVICE_ADMIN_CATEGORY);

If you track KEY_DEVICE_ADMIN_CATEGORY you will get to security_settings_misc.xml and see

43  <Preference android:title="@string/manage_device_admin"
44    android:summary="@string/manage_device_admin_summary"
45    android:persistent="false"
46    android:fragment="com.android.settings.DeviceAdminSettings"/>

So next step is to get to DeviceAdminSettings, browsing around you would get to updateList (note: I am taking you to an unpatched version of updateList) and you will notice that for something to be displayed in the device administrator list it must meet the following conditions:

1. Should be in the list returned by:

getActivity().getPackageManager().queryBroadcastReceivers(
    new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
    PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);

2. Should be visible or active:

DeviceAdminInfo dpi = new DeviceAdminInfo(getActivity(), ri);
    if (dpi.isVisible() || mActiveAdmins.contains(dpi.getComponent())) {
mAvailableAdmins.add(dpi);

With some trivial debugging you would realize that OBAD device admin component is not being returned by queryBroadcastReceivers and further debugging would eventually lead you to manifest file of OBAD saying:
<receiver "System" =".OCllCoO">
   <meta-data "android.app.device_admin" ="@2130968576">
   </meta-data>
   <intent-filter>
    <action name="com.strain.admin.DEVICE_ADMIN_ENABLED">
    </action>
   </intent-filter>
 </receiver>

Notice the use of com.strain.admin.DEVICE_ADMIN_ENABLED, the developer docs say that DeviceAdministrator components should register to receive android.app.action.DEVICE_ADMIN_ENABLED, but the bad guys don’t have to listen to that and they don’t and this is how they hide their device admin. As it is not registered to receive such broadcasts, it does not appear when receivers of it are retrieved in updateList (discussed above).

Looking around a bit I found something interesting that I will share in my next post soon (hopefully within two weeks) so stay tuned and think about this: you give a friend keys to your kingdom/palace/house and he gives his to you, but what you have given him was fake.

Until next time stay safe.

 

More from Malware

RansomExx Upgrades to Rust

IBM Security X-Force Threat Researchers have discovered a new variant of the RansomExx ransomware that has been rewritten in the Rust programming language, joining a growing trend of ransomware developers switching to the language. Malware written in Rust often benefits from lower AV detection rates (compared to those written in more common languages) and this may have been the primary reason to use the language. For example, the sample analyzed in this report was not detected as malicious in the…

Raspberry Robin and Dridex: Two Birds of a Feather

IBM Security Managed Detection and Response (MDR) observations coupled with IBM Security X-Force malware research sheds additional light on the mysterious objectives of the operators behind the Raspberry Robin worm. Based on a comparative analysis between a downloaded Raspberry Robin DLL and a Dridex malware loader, the results show that they are similar in structure and functionality. Thus, IBM Security research draws another link between the Raspberry Robin infections and the Russia-based cybercriminal group 'Evil Corp,' which is the same…

The Ransomware Playbook Mistakes That Can Cost You Millions

If there is one type of cyberattack that can drain the color from any security leader’s face, it’s ransomware. A crippling, disruptive, and expensive attack to recover from, with final costs rarely being easy to foretell. Already a prevalent threat, the number of ransomware attacks rose during the pandemic and nearly doubled in the year between 2020 and 2021, continuing to rise since. Focusing on the extortion price of these attacks, the cost of a ransomware attack can appear finite…

From Ramnit To Bumblebee (via NeverQuest): Similarities and Code Overlap Shed Light On Relationships Between Malware Developers

A comparative analysis performed by IBM Security X-Force uncovered evidence that suggests Bumblebee malware, which first appeared in the wild last year, was likely developed directly from source code associated with the Ramnit banking trojan. This newly discovered connection is particularly interesting as campaign activity has so far linked Bumblebee to affiliates of the threat group ITG23 (aka the Trickbot/Conti group), who are not known to have had a previous connection with Ramnit. This year has so far proven tumultuous…