Note: This is being published with the permission of Facebook under the responsible disclosure policy. The vulnerability is now fixed.
In Android, WhatsApp “Screen Lock” feature allows the user to lock the app using available device biometric credentials (Fingerprint, Face ID, PIN, and Pattern).
The “Auto Lock” feature will lock the app automatically after user-specified duration. If the user opted for “1 minute”, then the app will be locked after 1 minute of inactivity.
The issue here is, if the user receives a WhatsApp call from someone after 1 minute or later, then the app FAILS to lock. So an attacker can easily bypass the biometric lock just by making a call and rejecting it to access the app completely (read chats, send messages…).
Bypass steps:
tl;dr
Auto-lock implementation sample code:
public class App extends Application { public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
}ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacks() {
public void onActivityResumed(Activity activity) {
if(!excludedActivityList.contains(activity)) &&
SystemClock.elapsedRealtime() - getAppClosedTime() >= USER_SPECIFIED_DURATION) {
lockTheApp();
}
}public void onActivityPaused(Activity activity) {
};
saveAppClosedTime(SystemClock.elapsedRealtime());
}}
“Any screen inactivity listener”: WhatsApp will be auto-locked after timeout even if the user minimized it from any activity like chat, home, or settings screen. To implement this, WhatsApp registers “Activity Lifecycle Callback” in the {Application.class}.
Code Flow:
2. After the call is ended, {CallActivity.class} is closed and the method {onActivityPaused()} is called.
Method: onActivityPaused()
Stores the current “device up time” {SystemClock.elapsedTime()} as {app closed time}.
3. Now when the app is reopened, method {onActivityResumed()} is called.
Method: onActivityResumed()
It calculates the “time difference” between current “device up time” and the stored {app closed time}. If it is > {user specified duration}, then only it will lock the app.
It fails to lock now because the {app closed time} is saved after the call is ended and the “time difference” is < {user specified duration}.
public void onActivityPaused(Activity activity) {
if(!excludedActivityList.contains(activity)) {
saveAppClosedTime(SystemClock.elapsedRealtime());
}
}
Fix:
The method {onActivityPaused()} and {onActivityResumed()} will be called whenever any activity of the app is paused/resumed.
So this issue can be fixed by checking the activity class name and not saving the {app closed time} if it is {CallActivity.class}.