Skip to content

READ_SMS

Grants access to all SMS messages stored on the device. The primary use in malware is OTP interception -- reading one-time passwords sent by banks and online services to complete unauthorized transactions or account takeovers. Also used for SMS forwarding (exfiltrating entire message history to C2), contact harvesting from message metadata, and confirming premium service subscriptions. Despite years of platform restrictions and Google Play policy changes, SMS-based 2FA remains widespread enough that this permission is still a high-value target for banking trojans.

Technical Details

Attribute Value
Permission android.permission.READ_SMS
Protection Level dangerous
Permission Group android.permission-group.SMS
Grant Method Runtime permission dialog
Introduced API 1
Play Store Policy Restricted since January 2019 to default SMS handlers and approved use cases

What It Enables

Access to the SMS content provider at content://sms/. The app can read all stored messages across inbox, sent, drafts, and outbox:

Cursor cursor = getContentResolver().query(
    Uri.parse("content://sms/inbox"),
    new String[]{"address", "body", "date", "read", "type"},
    null,
    null,
    "date DESC"
);

while (cursor.moveToNext()) {
    String sender = cursor.getString(cursor.getColumnIndex("address"));
    String body = cursor.getString(cursor.getColumnIndex("body"));
    long timestamp = cursor.getLong(cursor.getColumnIndex("date"));
}
cursor.close();

The SMS content provider schema:

Field Content Abuse Value
address Sender/recipient phone number Contact graph extraction
body Message text OTP codes, personal data
date Timestamp (milliseconds since epoch) Timing attacks, recent OTP targeting
read Read/unread status (0/1) Target unread OTPs first
type Inbox (1), Sent (2), Draft (3), Outbox (4) Filter for incoming messages
thread_id Conversation thread identifier Group messages by contact
person Contact ID if sender is in contacts Cross-reference with contact data

Targeted OTP Query

Malware often filters for recent messages from known banking short codes or containing OTP patterns:

String selection = "date > ? AND body LIKE ?";
String[] args = {
    String.valueOf(System.currentTimeMillis() - 120000),
    "%verification code%"
};
Cursor cursor = getContentResolver().query(
    Uri.parse("content://sms/inbox"),
    new String[]{"address", "body"},
    selection,
    args,
    "date DESC"
);

Abuse in Malware

OTP Interception

The primary abuse case. Banks and online services send authentication codes via SMS. Malware reads these to complete unauthorized transactions or account takeovers.

Two complementary approaches:

Approach Permission Mechanism Timing
Retroactive READ_SMS Query content://sms/inbox for recent messages After OTP arrives, with slight delay
Real-time RECEIVE_SMS BroadcastReceiver with SMS_RECEIVED action and high priority Immediate, can suppress notification

Most families use both: RECEIVE_SMS for real-time interception with notification suppression, and READ_SMS as a fallback to scan the inbox if the broadcast receiver misses a message.

SMS Forwarding to C2

Malware reads the entire SMS inbox and forwards all messages to C2. This captures OTPs, bank transaction alerts, personal messages, and any SMS-based verification. Some families set up a periodic task (via AlarmManager or WorkManager) to continuously exfiltrate new messages.

Premium SMS Subscription Fraud

Joker (also known as Bread) uses READ_SMS to intercept confirmation codes for premium service subscriptions it initiates silently. The malware simulates clicks on premium subscription web pages, reads the confirmation SMS code, and completes the subscription -- all without user awareness. Over 1,800 Joker-infected apps were removed from Google Play between 2017 and 2023, according to Zimperium research.

SMS Worm Propagation

FluBot read the victim's SMS history and contact list to craft targeted smishing messages, sending itself to all contacts via SMS. The worm spread across Europe from late 2020 until Europol disrupted its infrastructure in May 2022. FluBot replaced the default SMS app to intercept all incoming messages, capturing banking OTPs while simultaneously using the SMS channel for propagation.

Notable Families

Family SMS Abuse Source
FluBot SMS worm: reads contacts, sends smishing messages, replaces default SMS app for OTP interception Europol
Cerberus OTP theft via SMS reading combined with notification listener for redundant capture ThreatFabric
Joker Reads SMS to extract confirmation codes for premium service subscriptions initiated silently Zscaler
TrickMo Real-time SMS forwarding to bypass banking 2FA, 40+ variants identified with 16 droppers Zimperium
BRATA SMS interception for banking fraud, prompts user to set malware as default SMS app, wipes device post-theft Cleafy
MoqHao SMS-based distribution (smishing via Roaming Mantis), reads and exfiltrates SMS on infected devices McAfee
Anatsa SMS interception for transaction authorization codes during on-device fraud ThreatFabric

Google Play SMS Policy Crackdown

In October 2018, Google announced restrictions on SMS and Call Log permissions. Enforcement began January 9, 2019:

  • Only apps designated as the default SMS handler, Phone handler, or Assistant handler may declare READ_SMS, RECEIVE_SMS, SEND_SMS, or Call Log permissions
  • All other apps must remove these permissions from their manifest or submit a Permissions Declaration Form with justification
  • Apps that failed to comply were removed from the Play Store
  • Limited exemptions granted for specific use cases (e.g., backup apps, dual-SIM managers)

This policy crippled many legitimate apps but also forced malware to adapt. Post-2019 banking trojans increasingly shifted to:

  • Notification listener for OTP capture (does not require SMS permission)
  • Accessibility service to read notification content from the screen
  • Overlay phishing of authenticator apps to capture TOTP codes
  • Sideloading distribution to avoid Play Store policy entirely

Alternatives When READ_SMS Is Unavailable

When READ_SMS is difficult to obtain (Play Store restrictions, user suspicion), malware uses alternative channels:

Alternative Permission Required Mechanism Limitations
Notification listener BIND_NOTIFICATION_LISTENER_SERVICE Reads OTP from notification text Requires user toggle in Settings, only sees notification content
Accessibility service BIND_ACCESSIBILITY_SERVICE Reads any on-screen text including SMS notifications Requires user toggle, subject to restricted settings on Android 13+
Overlay on authenticator SYSTEM_ALERT_WINDOW Phishes TOTP codes by overlaying Google Authenticator or similar apps Only captures app-based 2FA, not SMS
Push notification interception BIND_NOTIFICATION_LISTENER_SERVICE Captures push-based OTPs from banking apps that moved away from SMS Same limitations as notification listener

The shift toward notification-based OTP interception has reduced reliance on READ_SMS in newer malware families. BIND_NOTIFICATION_LISTENER_SERVICE is easier to justify to the user ("We need notification access for this feature") and is not subject to Google Play's SMS policy. See Notification Listener Abuse.

Android Version Changes

Android 4.4 (API 19)

Only the default SMS app can write to the SMS content provider. Non-default apps can still read all messages with READ_SMS. This was the first attempt to limit SMS abuse, but it only affected write operations.

Android 6.0 (API 23)

Runtime permission required. Before this, READ_SMS was granted silently at install time. Under the original permission group model, granting READ_SMS also granted RECEIVE_SMS, RECEIVE_MMS, and RECEIVE_WAP_PUSH (all in the SMS group). This meant a single "Allow" tap gave malware the complete SMS interception toolkit.

Android 8.0 (API 26)

Google Play policy begins restricting SMS permissions to apps with core SMS functionality. This predates the formal January 2019 enforcement but signals the direction. Apps must be declared as default SMS handler or have an approved use case.

Android 10 (API 29)

Permission groups split to be more granular. READ_SMS no longer automatically grants access to Call Log or phone number data. Each permission in the SMS group must be individually justified, though granting one still prompts for the group on the runtime dialog.

Android 13 (API 33)

Runtime permission model unchanged at the platform level, but Play Store review is significantly stricter about justifying SMS access. Photo picker and other scoped access APIs reduce legitimate reasons for broad permissions, making SMS permission requests more suspicious during review.

Frida Monitoring Script

Monitor SMS content provider queries at runtime:

Java.perform(function () {
    var ContentResolver = Java.use("android.content.ContentResolver");

    ContentResolver.query.overload(
        "android.net.Uri",
        "[Ljava.lang.String;",
        "java.lang.String",
        "[Ljava.lang.String;",
        "java.lang.String"
    ).implementation = function (uri, projection, selection, selectionArgs, sortOrder) {
        var uriStr = uri.toString();
        if (uriStr.indexOf("sms") !== -1 || uriStr.indexOf("mms") !== -1) {
            console.log("[SMS Query] URI: " + uriStr);
            console.log("  projection: " + projection);
            console.log("  selection: " + selection);
            if (selectionArgs !== null) {
                console.log("  selectionArgs: " + selectionArgs);
            }
            console.log("  sortOrder: " + sortOrder);
            var trace = Java.use("android.util.Log")
                .getStackTraceString(Java.use("java.lang.Exception").$new());
            console.log("  caller: " + trace);
        }
        return this.query(uri, projection, selection, selectionArgs, sortOrder);
    };

    var SmsManager = Java.use("android.telephony.SmsManager");
    SmsManager.sendTextMessage.overload(
        "java.lang.String",
        "java.lang.String",
        "java.lang.String",
        "android.app.PendingIntent",
        "android.app.PendingIntent"
    ).implementation = function (dest, sc, text, sentIntent, deliveryIntent) {
        console.log("[SMS Send] to: " + dest);
        console.log("  body: " + text);
        this.sendTextMessage(dest, sc, text, sentIntent, deliveryIntent);
    };
});

Detection Indicators

Manifest signals:

<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />

High-priority BroadcastReceiver registration (real-time interception):

<receiver android:name=".SmsReceiver" android:exported="true">
    <intent-filter android:priority="999">
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

A BroadcastReceiver for SMS_RECEIVED with high priority (attempting to receive before the default SMS app) is a strong indicator of real-time SMS interception.

High-confidence malware indicators (combination of):

  • READ_SMS + RECEIVE_SMS + INTERNET (SMS exfiltration)
  • READ_SMS + RECEIVE_BOOT_COMPLETED + FOREGROUND_SERVICE (persistent SMS monitoring)
  • SEND_SMS + READ_CONTACTS (SMS worm propagation, FluBot pattern)
  • READ_SMS + RECEIVE_SMS without being the default SMS app
  • Content provider queries to content://sms/ in background services
  • String patterns matching OTP regex (\b\d{4,8}\b, "verification code", "confirmation code")

See also: SMS Interception | Notification Listener Abuse