Binary SMS – The old backdoor to your new thing

Share this…

Despite being older than many of its users, Short Messaging Service (SMS) remains a very popular communications medium and is increasingly found on remote sensors, critical infrastructure and vehicles due to an abundance of cellular coverage. To phone users, SMS means a basic 160 character text message. To carriers and developers it offers a much more powerful range of options, with sizes up to a theoretical maximum of 35KB and a myriad of binary payloads detailed within the GSM 03.40 specification.

Carriers make use of these advanced features for remote management. They can send remote command SMS messages to trigger and interact with hidden applications on their devices without the user’s consent. Law Enforcement can track a phone with ‘silent’ SMS messages designed not to alert the user. SMS technology underpins a lot of Mobile Device Management (MDM) frameworks.
PDU exploitation
The coupling between a smart-phone’s software and it’s radio is a lot closer than you might think. SMS messages containing malicious payloads can be targeted at listening applications on a device and from there processed by the target software. If the software is poorly written, remote memory corruption and arbitrary code execution are possible. The vehicle for getting a payload to a target application on a phone (or smart device) is the Protocol Data Unit (PDU) which contains framed user data which is forwarded, without inspection, to a logical port on the operating system – with the privileges of the radio (System normally). This is comparable to a computer running open services on the internet without a firewall.

Legalities and options

The GSM spectrum is very expensive private property. You may not transmit (or even receive) in GSM bands without permission. In the UK, it is an offence under the Wireless Telegraphy Act to transmit on licensed bands without permission and furthermore intercepting GSM without a warrant is an offence under the Regulation of Investigatory Powers Act. You should apply for a licence (example from OFCOM below) before commencing GSM testing and will also need a faraday cage to suppress radiation. (For these reasons, GSM interfaces get less scrutiny than TCP/IP for example).

If you feel compelled to write another OpenBTS blog, we recommend not publishing screenshots that suggest you broke the law as this would be very irresponsible and could compromise your company’s reputation.

GSM licence

For users on a budget, you can send SMS PDUs as a regular subscriber over an existing public carrier using a GSM modem but you must check your carrier’s terms and conditions first. Using Vodafone’s Terms and Conditions for example, customers are not allowed to use the service to break the law or use automated means to send texts. Scripting your testing to attack someone else’s phone would definitely not be allowed but manually sending an SMS PDU (or two) to test a phone you own could be OK. It is up to you to satisfy yourself that you are compliant with your carrier’s terms and conditions.

SMS PDU mode

There are two defined modes for SMS: Text and raw PDU mode. In PDU mode, the entire frame can be defined which opens up a huge attack surface in the form of the (user defined) encapsulating fields as well as the prospect for a malicious payload to be received by a target application on the phone. The beauty of PDU mode is a poorly written software application on a handset can be exploited remotely. This is made possible through the Protocol Data Unit (PDU) which allows large messages to be fragmented as Concatenated SMS (C-SMS) and crucially from a security aspect, messages to be addressed to different applications on a mobile using port numbers akin to TCP/IP.

WAP push is a popular example of a binary PDU. A typical WAP push message from your carrier may contain GPRS or MMS configuration settings which after reassembly appears as a special message which requests user permission to define new network configuration values defined within an XML blob. WAP might well be a legacy protocol, but it’s a powerful legacy protocol which can be used for many malicious purposes.

Displaying a text message is optional. Some SMS messages can be sent to your handset without your knowledge and will never appear in your inbox (a silent SMS). Message display can also be forced by setting the ‘Class 0’ type (flash SMS) which will cause the SMS to display in the foreground without user interaction. This public alerting system is used in the US by the emergency services and is typically delivered as a cell broadcast whereby all subscribers attached to a tower receive the SMS simultaneously.

SMS test environment options

When testing SMS PDUs you can do it in your own private test environment with a GSM test licence or over a public carrier. A private environment has several key advantages:

  • Cost – Sending lots of SMS messages can be expensive, especially once you start concatenating messages.
  • Control – SMS messages on a busy carrier are subject to unpredictable delays and contractual restrictions
  • Debugging – a private environment will allow you to monitor messages and responses on the air interface which is essential for early testing.

A public carrier is (potentially) much cheaper and quicker to use, with just a 3G dongle, but forfeits control and is subject to the terms and conditions of your carrier which may not allow testing with their network.

Private SMS test environment

To inspect SMS PDUs on the air interface a GSM base station is required. One of the best known open source base stations is OpenBTS, for which there are many setup tutorials for many different platforms. In our experience of OpenBTS it is indeed quick to setup but it’s PDU handling needs work. It has a Command Line Interface (CLI) for sending both types of SMS but we found the PDU validation routine to be buggy for Mobile Terminated (MT) SMS messages which is exactly what we’re interested in. After spending a long time nailing down the validation issue (and judging by the developer’s comments and try/catch blocks we weren’t alone) we happened across YateBTS which we found to have better support for testing MT-SMS.

YateBTS can be installed on a Raspberry Pi 3 which gives the advantage of having it in a small portable form so you can place it inside the faraday cage for example so it can be accessed by many users on a LAN. You should keep the distance between the radio and the host computer as short as possible and do not daisy chain USB cables as the clock requirements are so precise you will experience stability issues.

Ramsey box

Kit list

All prices are approximate. A cellar or room with double concrete walls could be used instead of a faraday cage providing your licensing authority are satisfied it will suppress emissions sufficiently (>60dB). Twelve inch concrete blocks have 35dB of attenuation at 900MHz.

Ettus USRP B200 with 2 900MHz antennas: £600
Ettus GPS disciplined oscillator (TCXO) with GPS antenna: £500
Ramsey STE3000 RF enclosure: £1500
Raspberry Pi 3: £35
YateBTS: Free
Wireshark: Free
Osmo-trx driver: Free
Ettus UHD library: Free
Total: £2635


Open source BTS tutorials age off quicker than a Gentoo kernel due to the fast moving world of open source GSM and the dynamic dependencies required. We recommend focusing on learning the standard and concepts rather than particular packages but if you want a recent tutorial, you’ll find a comprehensive tutorial for an open source BTS (Yate) on a Pi here.
OpenBTS and YateBTS don’t prescribe hardware or drivers so you can use it with multiple radios, providing they support the precise timing requirements of a GSM base station which are for an error rate of 0.05 ppm. This is more relaxed for pico cells at 0.1ppm.
If you attempt to stand up a BTS without a precision clock source you will find it is unstable. You may get lucky and get your handset to see the network and attach to it briefly at close range but it won’t last and will quickly fall over once you commence testing. A precision clock is essential.
The GPS unit we used requires an external GPS receive antenna and does take a while to acquire a fix (Green LED) initially.

A key configuration change for using a low power PC like a Pi is the third party radio driver which must be defined within ../etc/yate/ybts.conf. We had success with the official Ettus UHD driver but opted for the ARM friendly osmo-trx transceiver compiled with UHD 003.010 defined as Path=./osmo-trx and homed at ../lib/yate/server/bts/osmo-trx


Understanding the many unique aspects of the GSM air interface is not necessary to perform SMS testing but having a basic understanding of paging will help. Getting your handset to attach initially is half the battle, after that the rest is quite straightforward and very much in the realm of computing not RF.

The gsmtap PCAP output is an invaluable tool in testing your setup and monitoring traffic on the air interface. To use it, enter Yate’s web UI and update the monitoring IP to your own, check the gsmtap box, then spin up wireshark on your external interface. This feature can be used remotely which is convenient for users on a LAN. The gsmtap UDP packets for both the uplink and downlink will be sent blindly to port 4729 on your host and will likely elicit ICMP port closed responses.

A healthy BTS will be broadcasting data frames constantly on its Broadcast Channel (BCCH) and subscribers will be able to attach to it providing their International Mobile Subscriber Identifier (IMSI) is allowed under the access rules. When initiating an SMS, the first thing the BTS does is page the handset on the Common Control Channel (CCCH) to test for it’s presence. The handset should respond in kind after which the BTS will send the SMS PDU(s) on the Standalone Dedicated Control Channel (SDCCH).

In the screenshot below, a concatenated 3 part SMS has been sent over our YateBTS by subscriber 12345. Only when the final fragment arrives is it reassembled into a complete SMS. Each fragment is acknowledged separately which is helpful for debugging concatenation issues.

C-SMS in Wireshark

Wireshark filters

Only GSM: gsmtap && !icmp

Only paging activity: gsmtap.chan_type == CCCH && !icmp

Only SMS (Uplink / Downlink): gsm_a.dtap.msg_sms_type == 0x01

Only SMS from originator 12345: == “12345”

Only SMS ack from subscriber: gsm_a.rp.msg_type == 0x02

To send a PDU with YateBTS you can either use the dedicated script in the web interface at /nib_web/custom_sms.php or the more flexible telnet interface on TCP port 5038. Both methods require the IMSI of the recipient which you can find from the registered subscribers list or by monitoring handset registration and paging on the air interface. We wrapped the telnet interface with our own PHP script using the socket API like so:

// PHP
$cmd="control custom_sms called=$imsi $type=rpdu";
$socket = fsockopen("localhost", "5038", $errno, $errstr); 
fputs($socket, "$cmd\r\n"); 

With our SMS web API we were not only able to let other researchers on the LAN send PDUs but also de-skill the knowledge required to perform SMS testing.
The PDU sent on the air interface looks different than the PDU sent over the public network because of differences in SMS-DELIVER (BTS > MS) and SMS-SUBMIT (MS > BTS) formats. To learn more about PDUs see the PDU section.

Public SMS test environment

PDU testing doesn’t have to be expensive. You can send custom PDUs using any GSM device on which you can issue AT modem commands. A practical solution is a 3G dongle such as the Huawei E3533 or even a basic development board such as the Adafruit FONA series.

Huawei E3533
Using the £20 Huawei E3533 as an example, you will need to ensure the device is connected in GSM modem mode, not Ethernet adapter or mass storage etc. To do this with Linux issue a usb_modeswitch command as follows:

usb_modeswitch -v 0x12d1 -p 0x1f01 -V 0x12d1 -P 0x14db -M "55534243123456780000000000000011063000000100010000000000000000"

More device specific commands are available in the forum at

Once you have a /dev/ttyUSBx, connect to it with a serial console like minicom and issue the following Hayes AT commands: AT+CMGF=0 to place the modem into PDU mode.
AT+CMGS=n to prepare to send a PDU of length n bytes, followed by the PDU itself in hexadecimal form.
To obtain the length take the total number of hexadecimal characters, divide it by 2 to get bytes then subtract 8 bytes for the recipient’s number.
Tip: Hit Ctrl-Z to send the PDU. Do not hit enter.

The serial commands are easily scripted using Python’s serial library. Before firing up your Python interpreter, bear in mind your carrier’s terms and conditions regarding automated sending of SMS messages. So long as you are in control of each message’s transmission you should be ok.

Here’s a basic Python client for sending a PDU via a GSM modem.

# Python
ser = serial.Serial(tty, 115200, timeout=5)
if "OK" in
	ser.write("AT+CMGF=0\r") # PDU mode
	ser.write("AT+CMGS=%d\r" % ((len(pdu)/2)-8))
	ser.write('%s\x1a' % pdu)

Protocol Data Units (PDUs)

The GSM 03.40 standard describes Transfer Protocol Data Units (TPDUs) which are used to convey an SMS. The TPDU is used throughout the messages journey across the telecoms network.

There are different types of SMS PDUs. PDUs from the handset to the network are SMS-SUBMIT and could start with byte 0x01, PDUs from the network down to the handset are SMS-DELIVER and could start with byte 0x00 and are normally longer due to the addition of a 7 octet date service centre time stamp. The first byte is a bitmask of multiple flags and contains a lot of information.

PDU encoding is complicated but thankfully there are many free online encoders and programming libraries like Python’s smspdu to validate your PDU against the GSM 03.40 standard. Before jumping straight in and using one it’s important you understand some key fields and the significant difference between DELIVER and SUBMIT PDUs as many online decoders only do SUBMIT and not DELIVER. We’ve tested lots and recommend Python’s smspdu library.

Example validation of a PDU:
python -m smspdu [PDU]

The Protocol Identifier (PID) field refers to the application layer protocol used. The default value 0x00 is used for plain short messages. Setting the PID to 0x64 would be a silent SMS known as a ‘type 0’ SMS which all handsets receive and must acknowledge without indicating its receipt to the user. As previously mentioned, this has been used by law enforcement to actively ‘ping’ a handset on a network.

User data headers and concatenation

When you want to target an application on a phone you need to define the destination port within the User Data Header (UDH) which sits between the PDU header and User Data (UD) and is declared early on with the User Data Header Indicator (UDHI) flag in the first octet.
The UDH is also where Concatenated SMS (C-SMS) fragments are described.

SMS allows for large (binary) payloads to be chunked up and sent as separate messages, not necessarily in sequence. This allows for up to 35,700 bytes of custom data to be sent to an application on a handset as 255 texts (Warning: You get billed per SMS).
To chunk up a large message, it must first be split up into user data fragments not more than 140 octets each. A concatenated fragment is indicated via the UDHI flag in the first byte which indicates the first few bytes of the UD are fragment information which is used to reassemble the fragments later in order.
Each fragment would be sent, preceded by the SMSC header, destination number, and the Protocol Identifier (PID) and Data Coding scheme (DCS) bytes.
The fragment header contains the fragment count, a unique byte which serves as a batch identifier for all the fragments as well as the total number of fragments.

Example UDH declaring 16 bit port addressing AND concatenation:


0A: UDH length minus length field: 10

05: 16 bit application port addressing (Next four octets)

0B84: Destination port: 2948 (WAP push)

5732: Source port: 1664

00: Concatenated SMS

03: Information Element length for C-SMS section: 3

FF: Unique message identifier: 255

02: Fragments total: 2

01: This fragment: 1

For more UDH options see the 3GPP 23.040 standard.


This PDU to MSISDN +441234567890 has the ‘immediate display’ flag set so will pop up as a flash SMS on the recipient’s handset:


Bear in mind that data encoding varies between sections. Some sections are special bit-masks, some are GSM 7 bit encoded and some are just plain old hexadecimal.

Hello world SMS

01: SMS-SUBMIT with default flags (Bitmask)

00: Message type 0 (bits 0-1) Reject duplicates flag set (bit 2) (Bitmask)

0C: Recipient length 12 digits (0x0C)

91: International ISDN/Telephone numbering plan (Bitmask)

442143658709: Recipient MSISDN (+441234567890) (GSM 7 bit encoding)

0000: Protocol identifier 0: Plain old SMS, Data Coding Scheme 0: Flash message (Bit mask)

CC8329BFD065DDF72363904: User data: Hello World! (GSM 7 bit encoding)

GSM interfaces on phones don’t have firewalls

With an Android phone you would see the following activity in the log when an SMS PDU is received. This shows the Radio Interface Layer (RIL) notifying the kernel of an incoming unsolicited PDU of length 168 (21 bytes). The full PDU is written to a SQLITE database, an automatic acknowledgement is sent (SMS-DELIVER-ACK) back to the network and finally the PDU is delivered to Android’sprivileged SMS receiver for onward processing, in this case to the message store although it could be routed to a listening application if a destination port is specified in the UDH.
The only check is within the SmsHandler() which checks if SMS messages are allowed. There is no concept of packet inspection or port filters like an IP firewall.

adb logcat -b radio 

D/GsmInboundSmsHandler( 2988): IdleState.processMessage:1
D/GsmInboundSmsHandler( 2988): Idle state processing message type 1
D/GsmInboundSmsHandler( 2988): acquired wakelock, leaving Idle state
D/GsmInboundSmsHandler( 2988): entering Delivering state
D/GsmInboundSmsHandler( 2988): DeliveringState.processMessage:1
D/GsmInboundSmsHandler( 2988): isSMSBlocked=false
D/GsmInboundSmsHandler( 2988): URI of new row -> content://raw/1
D/RILJ    ( 2988): [9382]> SMS_ACKNOWLEDGE true 0 [SUB0]
D/GsmInboundSmsHandler( 2988): DeliveringState.processMessage:2
D/GsmInboundSmsHandler( 2988): Delivering SMS to:

Targeting port 2948 with random data

In this example, an SMS-SUBMIT containing 1000 characters of malformed WBXML has been concatenated as 7 fragments and targeted at a the WAP push application listening on port 2948. Because of the port number, an assumption about the data type of the payload is made (WBXML). This could be any binary data addressed to any port.

PDU bytes:







41: SMS-SUBMIT with a user data header (Bitmask), Reject duplicates flag set (bit 2), User Data Header flag set (bit 3)

00: Message type 0 (bits 0-1) Immediate display

0C: Recipient length 12 digits (0x0C)

91: International ISDN/Telephone numbering plan (Bitmask)

442143658709: Recipient MSISDN (+441234567890 encoded as 7-bit ASCII)

0000: PID: 0, DCS: 0
9B: User data length (including UDH): 155

0B05040B8457320003FF0201: User Data Header (UDH).
Length: 0x0B
16 bit port addressing: 0x05
Four octets long: 0x04
Destination port: 0x0B84
Source port: 0x5732
Concatenated SMS: 0x00
Three octets long: 0x03
Sequence identifier: 0xFF
Total fragments: 0x02
This fragment: 0x01



81C0E87C3E170381C0E87C3E17018: User Data. Malformed WBXML composed of repeat sequence of letter ‘a’.


SMS is a weak link in a handset’s security. With it you can interact, remotely, with an application on someone’s phone when the phone is not connected to the internet. Significantly it has no firewall so bad packets are always forwarded.

Despite being an old specification it has received much less scrutiny than Internet Protocol for example and many applications (and non-conventional devices now) handle SMS PDUs with a greater level of trust than they afford IP packets for comparison.