T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
Challenge 9: Mobile Malware
Submission Template
Submit your solution at http://www.honeynet.org/challenge2011/ by 17:00 EST, Wednesday, June 15th 2011. Results will be released around the third week of June.
Name (required): Emilien Girault
Email (required): emilien.girault@sogeti.com
Country (optional): France
Profession (optional):
_ Student
X Security Professional
_ Other
Question 1. Write an executive summary of this incident.
Possible Points: 3pts
Tools Used:
Awarded Points:
An Android phone has been infected by a malicious app. This app seems to be a regular one (FXware's Currency Guide) but is in reality a bot that spies on the user, and is able to leak his/her SMS, contacts, IMSI, IMEI, send SMS and call arbitrary numbers.
The authors of this challenge provide a network capture and the damaged /data partition of the phone and the goal is to extract the malware and understand its behavior.
Question 2. Provide the phone brand, model, OS name and version.
Possible Points: 1pts
Tools Used: strings, grep, sort, uniq, Google
Awarded Points:
The phone was probably communicating using Wifi, since we can see a lot of IP addresses on the same network in the PCAP file. We can use the User-Agent header sent in HTTP requests to guess the type of each one of them :
It seems to be FXware Currency Guide, but the name has been changed.
We uploaded the APK to VirusTotal. None of the 43 antiviruses detects it.
We computed the digest of the other ones, and they seem genuine.
Question 4. What permissions are requested by the malware(s)? Why it is suspicious?
Possible Points: 1pts
Tools Used: apktool
Awarded Points:
We use apktool to disassemble the application com.fc9.currencyguide-1.apk, and to decode its Manifest.xml file.
Here is the permissions section of the manifest :
Highlighted are the permissions that, used in conjunction with android.permission.INTERNET, are suspicious. Indeed, the app is likely to transmit the user’s GPS coordinates, SMS and contacts, to a web server. More, the app can dial arbitrary numbers and will automatically start when Android boots. This is typical of malwares.
Question 5. Please provide a solution/s to quickly identify any suspicious API
(please define your suspicious API according to your understanding).
Possible Points: 8pt
Tools Used: Dex2jar, JDGUI, grep
Awarded Points:
We used Dex2jar to convert the APK into a JAR file, which can then be opened with JDGUI. We can export the Java source code into a folder, and then use grep to find intents:
./c/b.java:30: Intent localIntent1 = new Intent("android.intent.action.CALL", localUri);
./c/h.java:26: IntentFilter localIntentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
Malicious intents are those that could violate the user’s privacy (access his personal data), or steal money from him (by calling or sending SMS to premium rate numbers).
The use of SMS, contact and cryptography related APIs can be considered as suspicious:
# grep -rin sms .
./c/c.java:26: Uri localUri = Uri.parse("content://sms/");
./c/h.java:26: IntentFilter localIntentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
new byte[]{99, (byte)178, 82, (byte)246,(byte) 250,(byte) 244, 22, 127}));
Cipher localCipher = Cipher.getInstance("DES");
localCipher.init(2, localSecretKey);
String plaintext = new String (localCipher.doFinal(new byte[] { (byte) 42,(byte)167,(byte)73,(byte)214,(byte)71,(byte)27,(byte)136,(byte)254,(byte)50,(byte)90,(byte)90,(byte)213,(byte)193,(byte)87,(byte)209,(byte)28,(byte)239,(byte)154,(byte)72,(byte)51,(byte)186,(byte)50,(byte)238,(byte)242,(byte)32,(byte)214,(byte)41,(byte)139,(byte)73,(byte)230,(byte)181,(byte)92 })); //same thing for the port
Log.d("TestAPK", " => " + plaintext);
} catch (Exception e) {
e.printStackTrace();
}
Once decrypted, the URL of the C&C server is http://faeacdeadbeefada.zonbi.org:443.
However, we can ping the server :
# ping faeacdeadbeefada.zonbi.org
PING faeacdeadbeefada.zonbi.org (173.255.253.196) 56(84) bytes of data.
64 bytes from brns.zonbi.org (173.255.253.196): icmp_seq=1 ttl=128 time=242 ms
64 bytes from brns.zonbi.org (173.255.253.196): icmp_seq=2 ttl=128 time=229 ms
64 bytes from brns.zonbi.org (173.255.253.196): icmp_seq=3 ttl=128 time=244 ms
The IP address belongs to Linode, located in New Jersey, United States.
Question 7. What can you say about the communications model between the
malware and its C&C server?
Possible Points: 2pts
Tools Used: Wireshark
Awarded Points:
The malware periodically fetches web pages on its C&C server, in order to receive orders. However, these communications are somehow obfuscated :
The server listens to the TCP port 443, usually reserved for HTTPS. But the communication occurs in HTTP, so if we want to read it with Wireshark, we have to use the “Decode as” option, and select HTTP.
All requests seem to be encrypted: no sensitive data appear clearly in the capture.
Question 8. If encryption was used for the communication, which encryption
algorithm was used? What was the key used? Explain how you found it.
Possible Points: 4pts
Tools Used: Dex2jar, JDGUI, grep, Python
Awarded Points:
We use grep to find patterns within the Java source code:
We can see that obfuscation techniques has been used (probably Proguard ?), since most of the class, method and package names has been changed to “a”, “b”, “c”, etc.
The Data Encryption Standard algorithm seems to be used in daemon/e/a.java. This class is a wrapper for creating Ciphers and SecretKeyFactories. We can use grep to find cross references to this class :
It is used by daemon/g/a. This class performs decryption operations. It holds two keys, a and b.
public static byte[] a = new byte[8];
public static String b;
private static byte[] c = "HNfCha9".getBytes(); //looks like a key, but it is never used
“b” is initialized in CComService, as the IMSI of the phone. However, we do not have the IMSI yet, and by browsing the code we can see that this key is used only to decrypt internal hardcoded strings, such as the C&C server URL.
The communications between the bot and the server use the second key, “a”, which is initialized in CCcomService :
localh.a(str1, "0963485269741EF69AE45D69F23AA9"); //localh’s type is daemon.b.h
com.fc9.currencyguide.daemon.g.a.a = b.a();
Let’s have a look at the daemon.b.h.a() method. Its second parameter looks like a key :
public final void a(String paramString1, String paramString2)
com.fc9.currencyguide.daemon.f.a locala1 = new com.fc9.currencyguide.daemon.f.a();
com.fc9.currencyguide.daemon.g.b localb = new com.fc9.currencyguide.daemon.g.c(paramString2).a();
The daemon.g.c class is called. When the object is instantiated, “64” is prepended to the “09…A9” string constant. Then, the a() method is called. Roughly, here is the algorithm that is used, in pseudo-code :
P = random_prime(128) //128 is the bit size
G = random(128)
N = random(128) //N is unused
C = 0x640963485269741EF69AE45D69F23AA9 //the constant is used as an hex number
X = G ^ C mod P //modular exponentiation
X, G, P and N are sent over the network in a request to /data.php. The C&C server responds with 3 numbers N, S and X (we’ll call it X2, to avoid the confusion with the first X). Then the malware computes :
K = X ^ C mod P //S and N are unused
K is then converted to a 16-byte array, and the 8 least significant ones (on the right) are used as the DES key for encrypting and decrypting the communications between the server and bot.
This is typically an implementation of the Diffie-Hellman key exchange algorithm. However, here it is vulnerable since C is not random (N should have been used instead). X and P can be found in the PCAP file and we have C, so we can easily find K using the following Python script :
def modExp(a, b, m) :
"""Computes a to the power b, modulo m, using binary exponentiation
Here is a simplified flow graph of the communications between the bot and the C&C server. Redundant calls to /data.php without response have been deleted. The decrypted communications have been truncated in order to fit in the graph. XML data has been converted to function calls to improve readability. The process of extracting them is explained in the next paragraph.
Decrypted communications
In order to automate the decryption of the communications, we use Wireshark to export the packets matching this filter :
ip.addr==172.16.2.101 and tcp.port==443 and http
We export those packets into a pcap file, and then use Scapy to extract the HTTP layer of the packets :
r=rdpcap("http_traffic.pcap")
open("http_streams.bin","wb").write("\n".join([p[Raw].load for p in r if Raw in p]))
The resulting file contains all HTTP requests and responses. We can use the following Python script to extract the embedded encrypted data :
import re
hexstring2list = lambda x: [int(x[2*i:2*i+2],16) for i in range(len(x)/2)]
def extract1():
"""Extract ciphertexts embedded in requests, in the form data=[...]"""
data = [hexstring2list(l[5:-1]) for l in open("http_streams.bin").readlines() if "data=" in l ]
print "static byte[][] extracted_ciphertexts1 = new byte[][]{"
for c in data:
print "new byte[] {",
print ",".join(["(byte)" + str(i) for i in c]),
print "},"
print "};"
def extract2():
"""Extracts ciphertexts embedded in responses from the server, in binary"""
f = open("http_streams.bin","rb").read()
o = re.findall("GMT\r\nContent-Length: [0-9]+\r\n\r\n(.+?)\nPOST", f, re.DOTALL)[1:]
print "static byte[][] extracted_ciphertexts2 = new byte[][]{"
for c in o:
print "new byte[] {",
print ",".join(["(byte)" + str(ord(i)) for i in c]),
print "},"
print "};"
Those two functions generates a Java output that declares two arrays of ciphertexts (byte arrays) that can we can copy and paste to Eclipse, in order to perform decryption :
publicvoid decrypt_all() {
for(int i = 0; i< extracted_ciphertexts1.length; i++) {
try {
21080=Votre mot de passe est strictement confidentiel : conservez-le pr+®cieusement et ne le communiquez pas +á un tiers. Votre mot de passe est 3B6AT4&+33666186296=Le 0645324806 remporte 1 ch+¿que en euro!Composez le 0899650923 pr le retirer imm+®diatement. Jeu sous Controle d huissier (1E35/ap+0E34/mn)&+33644066241=VocalMessenger: Vous avez recu 1 nouveau message vocal a 11:59. Pour le consulter, composez le 0899230625
Code confidentiel: 1701
(1e35+0e34-noSmsEnvStop)&20904=Mobicarte - Compte Principal. Attention, il vous reste moins dÔÇÿune journ+®e pour utiliser votre cr+®dit de 3.93 EUR.&20904=Mobicarte - Compte Principal. Attention, il ne vous reste plus quÔÇÿune journ+®e pour utiliser votre cr+®dit de 4.05 EUR.&20904=Mobicarte - Compte Principal. Attention, il vous reste moins dÔÇÿune semaine pour utiliser votre cr+®dit de 4.17 EUR.&20904=Mobicarte - Compte Principal. Attention, il ne vous reste plus quÔÇÿune semaine pour utiliser votre cr+®dit de 4.29 EUR.&20904=mobicarte: votre ligne est identifi+®e, vous pouvez maintenant choisir votre Bonus et recharger votre compte au #123# (appel gratuit).&20904=Mobicarte: Votre numero de telephone est le 0645324806, valide jusquÔÇÿau 05/01/12.&20904=Bienvenue chez Orange,votre num+®ro mobicarte est le 0645324806. Vous b+®n+®ficiez dÔÇÿun cr+®dit de 5E de communications valable jusquÔÇÿau 05/01/12 en Fce m+®tro.&
Note: The Android SDK and emulator are actually not required, as javax.crypto is a standard Java API.
Question 10. What personal information were leaked during this incident A special
*secret* information was leaked, Explain how and what it was.
Possible Points: 2pts
Tools Used: None
Awarded Points:
The IMSI, IMEI and contacts were leaked by the bot. More, we can find both the phone number of the owner (0645324806) and its password (3B6AT4), which is used to authenticate on Orange.fr.
The malware could also intercept baking credentials, often sent as SMS by banks when online purchasing.
Question 11. What particular techniques are used by the malware to harden analysis
or to evade detection? What unusual behavior can be noticed?
Possible Points: 6pts
Tools Used: JDGUI, baksmali, Android SDK and Emulator, Python
Awarded Points:
Technique 1
As we discussed previously, the malware encrypts some of its own strings to avoid leaking the C&C server URL, as well as the whole communications. This slows down the analysis process, as the reverse-engineer needs to look for the encryption key and decrypts manually the communications.
Technique 2
While browsing the code with tools such as JDGUI and baksmali, we can see that the first operations that the malware does is a detection of its running environment. Indeed, it is able to detect whether it is running inside the emulator or not. The detection code resides in daemon/e/b, and is called in BootReceiver and FC9 :
public static Boolean a()
{
Boolean localBoolean = Boolean.valueOf(0);
if (a.a(Build.DEVICE).equalsIgnoreCase("46a808cfd5beafa5e60aefee867bf92025dc2849"))
localBoolean = Boolean.valueOf(1);
while (true)
{
return localBoolean;
//Similar test on Brand.MODEL, Brand.PRODUCT and Brand.BRAND
if (a.a(Build.MODEL).equalsIgnoreCase("5a374dcd2e5eb762b527af3a5bab6072a4d24493"))
{
localBoolean = Boolean.valueOf(1);
continue;
}
if (!a.a(Build.PRODUCT).equalsIgnoreCase("5a374dcd2e5eb762b527af3a5bab6072a4d24493"))
The last one corresponds to the phone IMSI (208013002954000). Thus, this malware is specific to one device (to one SIM card, actually) and will not work on a different phone. We think the authors of the challenge did that on purpose in order to prevent any use of this malware in real life .
These methods are called from BootReceiver :
public void onReceive(Context paramContext, Intent paramIntent)
{
if (com.fc9.currencyguide.daemon.e.b.a().booleanValue())
return;
b localb = new b(paramContext);
if (!com.fc9.currencyguide.daemon.e.b.a(a.a(localb.b())).booleanValue())
return;
[...] //Start CCcomService
If one of them returns true, the app know it runs inside the emulator, and the CCcomService won’t be launched.
Technique 3
Since the IMSI is used as a key to decrypt hardcoded strings, the same APK won’t be able to run on a device that doesn’t have the same SIM card. Thus, even if we patch the emulator detection routine (by recompiling the APK with Smali), the application won’t work. It will indeed throw this exception when it runs inside another phone (or the emulator) :
javax.crypto.BadPaddingException: Given final block not properly padded
This means that the hardcoded encrypted strings could not be decrypted with the second IMSI.
Question 12. Provide a detail analysis of the malware behavior and features.
Possible Points: 10pts
Tools Used: Baksmali, JDGUI, Bouml
Awarded Points:
During the installation of the application, Android installs the BootReceiver class as a BroadcastReceiver. It will be called each time the phone is booting, as specified in the Manifest.xml :
After verifying that the app is not running inside the emulator, the BootReceiver class starts the CCcomService. This service is also started when the FC9 Activity is started, which runs in background when the user launches the application with Android’s menu :
The CCcomService class implements the main loop of the malware, which is constantly talking to the C&C server. We can identify different stages in the communication, delimited by calls to the method daemon.b.c.a(). Indeed, the malware can be viewed as a Finite-State Machine, and this method implements the transition between states.
States are located in the d enum, and transitions are stored in the f enum, both located in the daemon.b package. Since JDGUI cannot be used to view the labels of each enum value, we can use baksmali to have a human readable view of those files :
We can identify 6 states and 11 transitions. By having a look at daemon.b.c.a(), we can reconstruct the graph of the state machine implemented in the malware :
During the REQUESTING state, the malware sends requests to the C&C server, including its IMEI, IMSI, operator name and location. The server sends back a XML file that the malware parse during the PARSING state, using the SAXParser API (in daemon/b/i). Then, it executes the orders embedded in this file.
Each action is located in the daemon.c package, and inherits from the “f” superclass. Here are the available actions (we renamed them based on the decrypted strings) :
goto: Start the browser and open a given webpage
call: Dial a given number
getsms: Read the SMS stored on the phone, and sends them to the C&C server
vibrate: Vibrate for a given number of miliseconds
smsspy: Sends every SMS received to the C&C server in real time by registering a broadcast receiver
smsunspy: Stop sending SMS by unregister the broadcast receiver
getcontacts: Sends the contacts to the C&C server
sendsms: Send a SMS to a given number (and with a given body)
The “e” class is responsible for dispatching the actions by parsing the XML tags found in the server response. When an action needs to send a result to the server, it uses the daemon.f.a() method, which is a wrapper for the Apache HTTP client.
Bonus Question. Please provide a method to block (or request permission from Android
(similar to UAC concept)) when any suspicious call received from Android.
Possible Points: 8pts
Tools Used:
Awarded Points:
Solution 1
One could develop an app that registers a specific activity or service for all potential malicious intents. This can be done in the manifest of the app, by specifying an IntentFilter that contains tags, and a high priority, such as :
[...]
I am not an expert in Android internals, but I guess it may be possible to write this activity or service such as it asks the user for his permission before starting the appropriate activity.
Solution 2
Another solution could be to modify the source code of the framework, and especially the PackageManager.findPreferredActivity() method, in order to ask the user after selecting the appropriate Activity for a given Intent. One could do the same kind of modification with ContentProvider.acquireProvider() in order to filter any suspicious query on personal data (such as SMS).
The inconvenient of this solution is that it requires recompiling the whole framework.
Solution 3
Each sensitive application could implement a UAC when it receives intents from other applications. For instance, the application responsible for dialing numbers could ask the user systematically when receiving a request from another application, and why not maintain an internal Access Control List. This ACL could also be maintain directly by Android, and filter dynamically all intents made from an application A to a ContentProvider or Activity.
In any case, this requires a dynamic permission checking and user interaction, for which Android static permission system does not seem to be designed:
“Android has no mechanism for granting permissions dynamically (at run-time) because it complicates the user experience to the detriment of security.”