SMS of Death

· SMS of Death

7 min read Original article ↗

Android Telephony · Zero-Click · Remote DoS

A single malformed WAP Push SMS crashes com.android.phone on Android, dropping active calls (including emergency calls), with zero interaction from the victim.

Download APK Source

⚠ Working proof-of-concept, authorised testing only. Read the warning & license before downloading.

Component
WapPushOverSmsframeworks/opt/telephony

Interaction
Nonezero-click, silent

Vector
Remote SMSWAP Push, port 2948

Affected
Android 12–17patched in 14–17, Oct 2026 update

Reporter
Mark B

Reported
5 May 2026Android & Devices VRP

Disclosed
29 Oct 2026public writeup

Recovery
ADB onlynormal user: bricked

01 / The vulnerability

An unchecked cast turns a 5-byte header into a negative array size

When Android receives a WAP Push message, WapPushOverSms.decodeWapPdu() reads the WSP header length as a uintvar. The value comes back as a long, is cast to int with no range check, and is then used directly as the size of a new byte array:

// WapPushOverSms.java:217  header length read as long, cast with no bounds check
int headerLength = (int) pduDecoder.getValue32();
    ...
// WapPushOverSms.java:244  the cast value is used as an array size
byte[] header = new byte[headerLength];

A 5-byte uintvar of 88 80 80 80 00 decodes to 0x80000000. Cast to a signed 32-bit int, that is -2147483648. Allocating new byte[-2147483648] throws an uncaught NegativeArraySizeException on the telephony main thread, killing com.android.phone:

E AndroidRuntime: FATAL EXCEPTION: main
E AndroidRuntime: Process: com.android.phone
E AndroidRuntime: java.lang.NegativeArraySizeException: -2147483648
E AndroidRuntime:   at ...WapPushOverSms.decodeWapPdu(WapPushOverSms.java:244)
E AndroidRuntime:   at ...WapPushOverSms.dispatchWapPdu(WapPushOverSms.java:339)
E AndroidRuntime:   at ...InboundSmsHandler.processMessagePart(InboundSmsHandler.java:1169)
E AndroidRuntime:   at ...InboundSmsHandler$DeliveringState.processMessage(InboundSmsHandler.java:627)

The message is delivered as a data SMS and is never shown to the user: there is nothing in the inbox, and the victim never sees the sender's number.

Affected versions. The code sits in Android's core telephony framework and has shipped in many releases. The crash has been reproduced from Android 12 (the reboot loop in issue #2) up to Android 16. Google's October 2026 fix was released for Android 14 through 17, so devices on older versions outside the security-update window, such as Android 12 and 13, run the same code and do not receive the fix.

02 / Impact

Remote, silent, and recoverable only with effort

  • Zero-click. The victim does nothing. No tap, no open, no notification.
  • Silent. Nothing appears in Messages; the attacker's number is never shown.
  • Calls drop, including emergency calls. Active voice calls terminate with DisconnectCause CS_DEATH as the process restarts.
  • Telephony blackout. 1-2 seconds of no service per message while com.android.phone restarts.
  • Carrier redelivery amplifies it. Unacknowledged messages are re-sent by the carrier, re-crashing the phone; a single message caused intermittent outages for up to a week in testing.
  • ~20 messages render the device unusable for an extended period.
  • Persistent denial of service. The malformed message stays queued and is retried on every boot, so the phone can crash on startup until it is cleared (recovery below).

Tested across multiple makes and models (Pixel 9 on April 2026 firmware, various Samsung Galaxy and A-series) with a 100% reproduction rate.

Recovery over ADB, contributed by @sakisdog

After hitting the reboot loop on Android 12, @sakisdog documented a less destructive recovery (issue #2). The poison messages sit in the pending inbound queue (content://sms/raw) and are retried on every boot, so Android's RescueParty reboots the device into recovery roughly every 90 seconds. Deleting just those rows over ADB, in the short window after boot completes, breaks the loop with no factory reset:

# while sys.boot_completed == 1: find the WAP Push rows (destination_port=134020), note the address
adb shell content query --uri content://sms/raw --projection _id:destination_port:date:deleted
# let the shell edit SMS storage, delete only those rows, then reset the permission
adb shell cmd appops set com.android.shell WRITE_SMS allow
adb shell "content delete --uri content://sms/raw --where \"address='<SENDER>'\""
adb shell cmd appops set com.android.shell WRITE_SMS default
# last resort, erases ALL SMS/MMS and APN settings:
adb shell pm clear com.android.providers.telephony

Recovery credited to @sakisdog. It still needs a computer and ADB, and you should not choose "wipe data" at the recovery prompt, so for a non-technical user mid-loop it remains out of reach.

This is the distinction the report turns on. It is not a transient crash: it is a remote, zero-click, persistent denial of service that, left untreated, reboots the phone into a recovery wipe-data prompt every ~90 seconds, driving a normal user to a factory reset to recover, which is the exact shape of a "Destructive Remote DoS" in the VRP's published rules.

03 / Why this is serious

A remote attacker can cut a phone off from Triple Zero

Dropping calls is not an inconvenience when one of those calls is to emergency services. While a device is under this attack, or stuck in the reboot loop, it cannot reliably place a call to 000. The attack is remote, needs only the victim's number, and shows them nothing.

Australia treats access to emergency calling as a mandatory property of a mobile service, not a nice-to-have. Under the Telecommunications (Emergency Call Service) Determination, administered by the ACMA, carriers are required to identify mobile phones that cannot reach Triple Zero, help the customer move to a working device, and stop providing service to the affected handset. Telstra states plainly that it is required to block phones that can no longer call 000, notifying the customer and blocking the device if nothing is done within 28 days.

The consequence is perverse: the regulatory remedy for a phone that can't reach 000 is to cut it off the network, not to patch the OS. So an attacker who can remotely induce that condition on an Android handset can, in effect, get the victim's own device barred from carrier service until it is updated. The regulation exists precisely because loss of emergency-call access is considered a public-safety failure; this bug manufactures that failure on demand.

The framework was tightened in 2024 following the 3G shutdown, which left some phones able to make ordinary calls but unable to reach emergency services, the same functional state this vulnerability produces.

Sources: ACMA: proposed amendments to the ECS Determination · Telstra: Emergency Call Service Determination · Dept. of PM&C: impact analysis

04 / The fix

One bounds check

The remedy is a range check before the cast in WapPushOverSms.java (line 217), provided to Google on the day of the first report:

long rawHeaderLen = pduDecoder.getValue32();
if (rawHeaderLen < 0 || rawHeaderLen > pdu.length) {
    result.statusCode = Intents.RESULT_SMS_GENERIC_ERROR;
    return result;
}
int headerLength = (int) rawHeaderLen;

What Google shipped

Google fixed this crash, but not with the bounds check above. On 15 June 2026 an Android engineer authored commit 735d967, "Fix remote DoS in decodeWapPdu," which shipped in the October 2026 Android security release for Android 14 through 17. Rather than validate the length, it widens the caught exception so the crash is swallowed, and deletes the triggering message so it cannot re-crash on carrier redelivery or on boot:

// WapPushOverSms.java: widen the catch to cover NegativeArraySizeException
- } catch (ArrayIndexOutOfBoundsException aie) {
+ } catch (RuntimeException e) {

// InboundSmsHandler.java: a failed decode now deletes the poison message
+ try { success = processMessagePart(tracker); }
+ catch (RuntimeException e) { deleteFromRawTable(...); }  // breaks the boot loop

This fix is part of the October 2026 Android security update. Devices updated to the October 2026 patch level or later are protected; devices on earlier patch levels remain vulnerable.

05 / Proof of concept

Source & APK

The proof-of-concept Android app and full source code are published on GitHub. The app crafts the malformed WAP Push PDU and sends it as a data SMS to a target number.

Warning: this is a working weapon. The repository contains a proof-of-concept that can remotely disrupt and brick mobile phones. It is published strictly for security research, education, and defensive testing.

Only run it against devices you own or are explicitly authorised to test. Using it against anyone else is a crime, and interfering with a person's ability to reach emergency services (000) is a serious criminal offence in Australia and most countries.

The software is provided "as is", with no warranty, under the Apache License 2.0. The author accepts no liability for any use, misuse, or damage. By downloading the APK or the source, you accept full responsibility for what you do with it.