For that I will capture with Wireshark all the communication with the C&C while the malware is running. Then I can export all the 'objects' in the HTTP connection, which means the content of the HTTP request and response.
Now, I have e in a folder all the files with the objects from the HTTP request:
$ ls main
main(1).php main(11).php main(13).php main(15).php main(3).php main(5).php main(7).php main(9).php
main(10).php main(12).php main(14).php main(2).php main(4).php main(6).php main(8).php main.php
$ more main.php
i=McsZtRV7Bv7ZjMSzwk5aIyZEiijP8F38NJcxd5VNElaIVxctxxX9UWCGbUaOIYRxhMxTtA8nBYmT%0A%2FkgJOPilsUZZyvc2swCziOJC5ae17wUorRhyx48b3kIReFjFdcomTsuyE8PNXnthpE3oWi%2F%2BV%2Btg%0AT%2Bcp2CCNstrLbeKReraPgFcgZKAlluZKoVG6SxwgKzzt0MxQFlobMu21L%2BmlA2DJ2pj8buhKtEOs%0AGAH9H6kSzdxaKks6lIynIzHLsLfyIKpHqWeTAynnJdBzcKsCIFeNvWzDdrD7Q3By3JgNN6RrgE2l%0A1z5FuY6CihbBi4hUWN36%2F2Gvtg%2BOpSz6********************zk4US3U2GPhp4C3L5%2FcA4a0nroBhndxpaDIjN4ftS0%0ACxg2100kZN9YsaLUXHvJ6Yg9jY7TJzsd8PhlNq%2FWyl1pP5YoOejbO1cFA7jj0RG%2F9Tp60zj1HpDy%0AIFOb9z5FNRI3JjGh3P4V%2Bg0iVWee2FM5Jk4k2ABcGEK5mS8MJnax3iiLEitzTnYb2zgO2V9uO9Bu%0AMq%2BswCEDk2yLmZJJ1ptAUIwj0pXqchauQ1cbikRrMOD5xTprWuQ62iS6kVtvQosA%2BfnxyresiWbs%0Am4B7s3xb%2BSV7ORouF9tQMk68thyu0NXfnSNJxuXm00FXsXMoFt%2Biw4IbtLLyhPjL3QfpMJqX4X0g%0AMmWMMQ81GIbF5Wv%2FR416%2BRCo%2BXBWDabDnwMWDY0M%2FD3G5DcXa5mVRtDJ8CDQol05s0BH9RsBNQsB%0AMFEev%2BHVGfKFCntlAy2If8lNL5k0Rx39g5QKfGIKEOxbxUfsIe0MG1TEs5ABUZBR3g3Ipz0AY9Ax%0ATekUoKpSdg4l7W7v0DFPoHmKD6WqhpPF%2B%2BFPo2CvkhWRKn0IzWcUbyGK%2Fg6ngmIv2%2F8w8nwSt3w0%0AVEZYdQns5zBFPGo6%2B9pZyceFt77QcBcrTGBheKMCsDlGfua3pGKBcxitkvWWdNmcKEP11TjLkEpb%0A4B83AHatDzOwTmh7iYz07GOU7CQXxI93q65Dc1crEEpH7rycKhc7KUZvkWYcmn8dayifCXDpEr%2Bl%0Akonl7bWWLqGDvFJ617dZ1pe9IGUCLCKmV5aunHGC8Zk9iVwELhfkMEv0a9SoxsnNnUIzSGV5hZZu%0A&s=&
As the HTTP request is URL encoded, I need first to decode it, so I will adapt the python script created in this post to do it automatically. This is the script:
#!/usr/bin/python
from Crypto.Cipher import Blowfish
from Crypto import Random
from struct import pack
from binascii import hexlify, unhexlify
import sys
import urllib
file1 = sys.argv[1]
file_out = sys.argv[2]
blfs_key = open('/path/to/the/blfs.key','r')
url_encode = open(file1,'r')
url_encode_2 = url_encode.read()
url_decode = urllib.unquote(url_encode_2).decode('utf8')
file_ciphertext_base64 = url_decode
file_blfs_key = blfs_key.read()
ciphertext_raw = file_ciphertext_base64.decode("base64")
IV = "12345678"
_KEY = file_blfs_key
ciphertext = ciphertext_raw
KEY = hexlify(_KEY)[:50]
cipher = Blowfish.new(KEY, Blowfish.MODE_CBC, IV)
message = cipher.decrypt(ciphertext)
config_plain = open(file_out,'w')
config_plain.write(message)
With this script it is easy to run a shell command with a loop 'for' to decrypt all the files in the directory. Bare in mind than the HTTP response are not URL encoded, so I will not need to perform that step on some of the files.
Now I should have decrypted all the information from each object. Looking at the first two HTTP POST requests I see this is the case, but for the third one, this is not the case and the data is still encrypted. What's going on here?
A Public Key!! really interesting stuff...
Actually, if I look further in the second HTTP request from the screenshot above I can see the following:
$ more "main(3).php"
a:4....
.....
cjogVGhlIEFuZHJvaWQgUHJvamVjdCB8IGphdmEudmVyc2lvbjogMA==
";s:3:"cmd";s:7:"get_key";s:3:"rid";s:2:"25";s:4:"data";s:0:"";}
This looks to me like the malware sends a request for a key and the server replies with the public key. So the only possibility is that the malware is using that key to encrypt the data so only the C&C can decrypt it with the private key.
To confirm this is the case, I am going to check the source code of the malware with 'androguard' as I explained in previous post.
Looking at the code, I see there is a method with the string 'get_key' and I can see which other method is calling it:
In [10]: d.CLASS_Lorg_thoughtcrime_securesms_h_c.METHOD_c.pretty_show()
########## Method Information
Lorg/thoughtcrime/securesms/h/c;->c()V [access_flags=public]
########## Params
local registers: v0...v2
- return: void
####################
***************************************************************************
c-BB@0x0 :
0 (00000000) const-string v0, 'get_key'
1 (00000004) const-string v1, ''
2 (00000008) invoke-virtual v2, v0, v1, Lorg/thoughtcrime/securesms/h/c;->a(Ljava/lang/String; Ljava/lang/String;)Ljava/lang/String;
3 (0000000e) move-result-object v0
4 (00000010) iput-object v0, v2, Lorg/thoughtcrime/securesms/h/c;->c Ljava/lang/String;
5 (00000014) invoke-virtual v2, Lorg/thoughtcrime/securesms/h/c;->b()Ljava/lang/Boolean;
6 (0000001a) move-result-object v0
7 (0000001c) invoke-virtual v0, Ljava/lang/Boolean;->booleanValue()Z
8 (00000022) move-result v0
9 (00000024) if-eqz v0, 5 [ c-BB@0x28 c-BB@0x2e ]
c-BB@0x28 :
10 (00000028) invoke-direct v2, Lorg/thoughtcrime/securesms/h/c;->d()V [ c-BB@0x2e ]
c-BB@0x2e :
11 (0000002e) return-void
***************************************************************************
########## XREF
F: Lorg/thoughtcrime/securesms/h/i; b (Landroid/content/Context;)V be
T: Lorg/thoughtcrime/securesms/h/c; b ()Ljava/lang/Boolean; 14
T: Lorg/thoughtcrime/securesms/h/c; d ()V 28
T: Lorg/thoughtcrime/securesms/h/c; a (Ljava/lang/String; Ljava/lang/String;)Ljava/lang/String; 8
####################
When decompiling the code I end up with some interesting Java methods:
Looking tat the Java code I can see that the public key is used. But also, looking deeper into the code, I find another interesting method:
private String a(String p9)
{
String v1_0 = 0;
String v0_0 = "";
try {
javax.crypto.Cipher v2_1 = javax.crypto.Cipher.getInstance("RSA/ECB/PKCS1PADDING");
v2_1.init(1, this.d);
String[] v3_2 = this.a(p9, 100);
java.util.ArrayList v4_2 = new java.util.ArrayList();
int v5 = v3_2.length;
} catch (String v1) {
return this.a.c(v0_0);
}
while (v1_0 < v5) {
v4_2.add(android.util.Base64.encodeToString(v2_1.doFinal(v3_2[v1_0].getBytes()), 0));
v1_0++;
}
v0_0 = android.text.TextUtils.join(".", v4_2);
return this.a.c(v0_0);
}
So basically, one method is for encryption and the other for decryption, and both of them are using the same public key. This is really interesting stuff.
So this is whats going on so far:
- The compromised device sends the information encrypted with blowfish to the C&C
- The C&C server replies with OK
- The compromised device requests the public key
- The C&C server replies with the public key
- The compromised device encrypts the information with the public key and sends to the C&C
- The C&C server can decrypt with it's private key
- The C&C server sends data encrypted with the private key ->I need to verify this
- The compromised device can decrypt with the public key > I need to verify this
To verify step 6 and 7, and as very quick PoC, I have created some Java code which takes the public key sent by the C&C and try to decrypt the successive messages sent by the C&C.
Bingo! When I run the code I clearly see it works and my 'guess' was right:
What is the information sent by the C&C? it looks like a new config.xml with new C&C URL..
Very interesting..
Looking to the code again, I see methods which performs the request for a new configuration file:
In [7]: d.CLASS_Lorg_thoughtcrime_securesms_xservices_b.source()
package org.thoughtcrime.securesms.xservices;
class b extends android.os.AsyncTask {
android.content.Context a;
final synthetic org.thoughtcrime.securesms.xservices.XRepeat b;
public b(org.thoughtcrime.securesms.xservices.XRepeat p1, android.content.Context p2)
{
this.b = p1;
this.a = p2;
return;
}
protected varargs String a(String[] p4)
{
org.thoughtcrime.securesms.h.i.a(this.a);
org.thoughtcrime.securesms.h.i.c("CONF", "Check pull off urls", this.a);
org.thoughtcrime.securesms.h.i.b(this.a);
org.thoughtcrime.securesms.h.i.c(this.a);
org.thoughtcrime.securesms.h.i.c("CONF", "Get config data from server", this.a);
org.thoughtcrime.securesms.h.i.j(this.a);
org.thoughtcrime.securesms.h.i.c("DATA", "Send data to server", this.a);
return "OK";
}
protected void a(String p1)
{
super.onPostExecute(p1);
return;
}
protected synthetic Object doInBackground(Object[] p2)
{
return this.a(((String[]) p2));
}
protected synthetic void onPostExecute(Object p1)
{
this.a(((String) p1));
return;
}
}
As the HTTP request to the C&C are encrypted with the Public key, I can't decrypt it. However, I could check in memory the information before is encrypted.
And this is what I found:
a:2:{s:7:"LogCode";s:4:"CONF";s:7:"LogText";s:27:"Get config data from server";}
Which matches the methods I checked previously :)