SMS OTP Authentication: Not As Safe As You May Think

Posted by Nicolas Desnos on October 05, 2017

Most online transactions require a two-step authentication, and the One-Time-Password (OTP) sent by SMS is often one of those two steps. The purpose of an OTP is to prevent fraud by confirming that the person making the transaction and the credit card owner are one and the same. To do so, a temporary code is automatically sent by SMS to the phone number associated with the bank account used.

Once the OTP SMS is received, the user types it in the transaction interface and he is only then able to finalize his purchase. But is the mobile device (tablet or smartphone) used to send and receive an SMS innocuous? Regrettably, not very. What seemed to be like a strong authentication process when it was first introduced is nowadays easily bypassed by mobile apps.

Our team identified two kinds of mobile applications using the OTP interception technique: the legitimate ones and the malicious ones. While a safe app will intercept a SMS OTP to facilitate transactions and make them fast, a malicious app will intercept it in order to commit banking fraud.


Different Purposes, Same Mechanism



And The Same Permissions Are Required

The more permission an app requires, the more suspicious it looks. However, an OTP interception only requires two permissions to be executed, and one of them (Internet access) is a very common one. As a consequence, apps featuring OTP interception for a malicious purpose do not appear as suspicious at first sight.

  • Permission to intercept SMS: 'android.permission.RECEIVE_SMS'
  • Permission to send the content, using internet or an SMS: 'android.permission.INTERNET' or 'android.permission.SEND_SMS'



A Weak Spot for Applications Stores

The Google Play and the Apple App Store perform first-level checks to prevent malwares from entering their stores. However, the lack of depth of these security scans often lets malicious apps to pass through thanks to the system limits. It is even truer when the first visible layers of an app, the permissions and functions, are not giving any indication about its true nature.

An OTP interception mechanism can be used by both legitimate apps and malwares. As a result, it has the capacity to successfully pass stores’ standard security checks.



Case Study

App name: Postbank Finanzassistent (An impostor app that mimics the official German PostBank’s one)

sha1: 32f85c91e5a1437e93128203c27cd8eeb8bbea19


This app intercepts SMS OTP in two steps:

1 – Interception of SMS with the implementation of an android.content.BroadcastReceiver (org.slempo.service.MessageReceiver)

2 – SMS content sending via (Lorg/slempo/service/MainService) and more precisely using a java.lang.Runnable (org.slempo.service.a.a$1)


1 - Interception via BroadcastReceiver

Required permission'android.permission.RECEIVE_SMS'

A Broadcast Receiver (MessageReceiver) is used to intercept incomming SMS with the 'onReceive' method.

Name of the Broadcast Receiver :

.class public Lorg/slempo/service/MessageReceiver;

.super Landroid/content/BroadcastReceiver;


Method :

.method public onReceive(Landroid/content/Context;Landroid/content/Intent;)V


Description :

(*) This method calls a(Landroid/content/Intent;)Ljava/util/Map to access the SMS

invoke-static {p2}, Lorg/slempo/service/MessageReceiver;->a(Landroid/content/Intent;)Ljava/util/Map;

(*) then calls the method b(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V which formats the information into JSON.

invoke-static {p1, v1, v0}, Lorg/slempo/service/a/f;->b(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V

(-) method that calls the constructor <init>(Ljava/lang/String;Lorg/slempo/service/a/a$a;Landroid/content/Context;)V which saves the information in the field:  Lorg/slempo/service/a/a;->a:Ljava/lang/String;

invoke-direct {v0, v1, v2, p0}, Lorg/slempo/service/a/a;-><init>(Ljava/lang/String;Lorg/slempo/service/a/a$a;Landroid/content/Context;)V


2 - Data sending

Service name:

.class public Lorg/slempo/service/MainService;

.super Landroid/app/Service;


This method instanciate the Runnable MainService$1:

.method public onCreate()V


(*) creation of Lorg/slempo/service/MainService$1

new-instance v1, Lorg/slempo/service/MainService$1;

                invoke-direct {v1, p0},



Runnable Name:

.class Lorg/slempo/service/MainService$1

.implements Ljava/lang/Runnable;


Method Name:

.method public run()V


The call is made in the methode run() 

(*) invoke-static {v0}, Lorg/slempo/service/a/f;->a(Landroid/content/Context;)V

(-) Lorg/slempo/service/a/a;->a()V that runs Runnable Lorg/slempo/service/a/a$1


Runnable Name:

.class Lorg/slempo/service/a/a$1;

.implements Ljava/lang/Runnable;


Method Name:

.method public run()V


(*) Sending via http

Permission: 'android.permission.INTERNET'

invoke-static {v0, v2, v3, v4},                                                                 



(*) Invisible SMS sending

Permission: 'android.permission.SEND_SMS'

invoke-static {v0, v2},




Discover PRADEO SECURITY solution suite:


Topics: Mobile Application Security, Cybersecurity