We all know what a SIM card is, or at least we think we do.

It is one of those objects we use every day without thinking too much about it: a small piece of plastic that somehow makes the phone belong to us, to our number, and to our operator.

Inside the SIM there is a microcontroller, a filesystem, applications, access rules, cryptographic material, and a standardized way for the phone to talk to it. A SIM is an active component sitting between the mobile network and the user equipment.

So the natural question is, how secure is it?

Can a SIM card run something that looks like malware?

The short answer is: yes, but not in the same way an Android APK or a desktop executable does.

A SIM applet cannot just open sockets, read your files, or spawn processes. It lives in a very constrained JavaCard and SIM Toolkit environment. But it can still be installed on the card, register for events, receive data from the terminal, and ask the terminal to perform actions through proactive SIM commands.

That is more than enough to make it interesting.

In this post I will build a deliberately harmless “toy SIM implant”: a small SIM Toolkit applet that is triggered by an incoming SMS-PP data download message. When it wakes up, it asks the phone for its local date, time and time zone, and displays the result on screen.

Everything happens in a controlled lab with an Osmocom SIM, a private YateBTS network and a test phone.

lab setup with the phone, Osmocom SIM, reader and YateBTS/bladeRF host

What would “SIM malware” even mean?

Calling something “SIM malware” is a bit loaded, so it is worth being precise.

On a normal phone, malware usually means software running on the application processor: an APK, an iOS exploit chain, a malicious binary, a browser payload. A SIM applet is different. It runs on the UICC, inside a constrained smart card runtime, and communicates with the terminal through standardized commands.

The SIM is trusted by the modem. It participates in authentication, exposes subscriber data under access rules, and can provide SIM Toolkit functionality. If a malicious or unwanted applet is present on the card, it may be able to:

  • add entries to the SIM Toolkit menu
  • react to terminal events
  • receive specific SMS messages forwarded to the card
  • ask the terminal to execute proactive commands
  • make behavior visible at the SIM/modem boundary, where normal mobile apps cannot easily observe it

This is the family of ideas that made research like Simjacker interesting. In that case, the public reporting focused on specially crafted SMS messages used to trigger SIM Toolkit behavior on vulnerable SIM cards, with the SIM then asking the phone to perform actions such as collecting location-related information. The important lesson for this article is not the specific payload or target applet, but the architecture: network-originated input can reach the SIM, and code or applications on the SIM can in turn ask the terminal to do things.

This does not mean that every SIM is a remote execution playground. Real deployments have OTA security, card manager keys, access domains, applet lifecycle restrictions, operator processes, and modem-specific behavior.

But if I control the SIM and the mobile network, can a remote command, like a simple SMS, wake up code running on the SIM?

A little theory

A modern “SIM card” is usually more than a simple SIM in the strict technical sense. Before looking at the APDUs and Toolkit behavior, it helps to separate a few terms that are often mixed together.

Relationship between UICC, SIM, and USIM

The terms SIM, USIM, and UICC are often used interchangeably in everyday language, but they describe different things.

  • ICC: Integrated Circuit Card ICC is the general term for a smart card containing an integrated circuit. A UICC is a particular type of ICC designed to host applications used in telecommunications.

  • UICC: Universal Integrated Circuit Card The UICC is the smart-card platform: the physical card, together with its operating system, file system, security mechanisms, and application environment.

    A UICC may host one or more applications, including a SIM application, one or more USIM applications, an ISIM application, and SIM Toolkit or other operator applications.

  • SIM – Subscriber Identity Module Strictly speaking, the SIM is the subscriber application originally defined for GSM and 2G networks.

    On early GSM cards, the physical card and the SIM application were effectively treated as a single entity. A SIM application can also be hosted on a UICC, mainly to provide compatibility with terminals and procedures based on the older GSM specifications.

  • USIM – Universal Subscriber Identity Module The USIM is an application hosted on a UICC. It was introduced for UMTS and provides newer authentication, security, identity, and service-related functions than the original GSM SIM application.

    A USIM is not itself the plastic card or chip. It is one of the applications selected and accessed by the terminal through the UICC interface.

    A USIM can also contain the parameters and authentication support needed to access GSM networks. However, a legacy 2G terminal that understands only the SIM application may not be able to use a UICC containing only a USIM.

In other words:

ICC
└── UICC
    ├── SIM application
    ├── USIM application
    ├── ISIM application
    └── Toolkit or operator applications

In common usage, people call the removable card a “SIM card.” Technically, however, most modern removable SIM cards are UICCs containing at least one subscriber identity application, usually a USIM.

For the rest of this article I will still use “SIM” in the common, practical sense, because that is how people usually refer to the removable card used in phones. Under the hood, though, the phone does not read it like a memory card. It exchanges APDUs with it, the same general idea used by smart cards.

The terminal can send commands such as SELECT, READ BINARY, VERIFY or AUTHENTICATE. The card replies with data and status words.

SIM Toolkit adds a more interesting mechanism: proactive SIM commands. The SIM cannot directly draw on the phone screen, send messages or query terminal information. Instead, it tells the terminal that it has something to do. The terminal then fetches the command and decides how to execute it.

For example, a SIM applet can ask the terminal to:

  • display text
  • present a menu
  • request local information
  • send an SMS
  • set up calls or data channels, depending on terminal support and policy

The important word here is “ask”. The terminal is still part of the chain, and different phones may behave differently.

For this lab we only use two benign proactive commands:

  • PROVIDE LOCAL INFORMATION, to ask for date, time and time zone
  • DISPLAY TEXT, to show the value on the phone screen

other possible commands are defined here Card Application Toolkit

The trigger is an SMS-PP data download event. This is not a normal user-visible SMS. It is a message delivered by the network in a format that the terminal may forward to the SIM as an ENVELOPE command.

In practice, the chain we want is:

private SMSC -> phone modem -> SIM ENVELOPE -> applet -> proactive command -> phone UI

Lab setup

The lab is intentionally small and local:

  • an Osmocom programmable SIM
  • a PC/SC smart card reader
  • JavaCard and SIM Toolkit APIs
  • pySim-shell and GlobalPlatformPro-style tooling
  • a Linux box running YateBTS
  • a bladeRF used by YateBTS
  • a test phone registered to the private 2G network

The network is not a public operator network. It is a private YateBTS cell used only for this experiment.

The threat model I wanted to simulate is a malicious provider or a compromised provider-side component. A normal user sending an SMS from a phone messaging app usually cannot choose all the low-level SMS fields required for SIM data download. A network-side SMSC is in a much better position to deliver that kind of message.

Installing the applet

Before the SMS part, the applet has to exist on the SIM.

The first step is making sure the reader sees the card. On Linux:

pcsc_scan -r

In my setup the card is a sysmoISIM-SJA2, visible through a PC/SC reader. Once the reader works, I can verify GlobalPlatform access by listing installed content:

PC/SC reader output showing the SIM ATR

java -jar gp.jar \
  --reader "<PCSC_READER_NAME>" \
  --key-enc <KEY_ENC> \
  --key-mac <KEY_MAC> \
  --key-dek <KEY_DEK> \
  --list \
  --verbose

After compiling the JavaCard applet into a CAP file, I installed it with pySim-shell over an SCP02 secure channel:

python3 pySim-shell.py -p <READER_INDEX> --noprompt \
  -e "select ADF.ISD" \
  -e "establish_scp02 --key-dek <KEY_DEK> --key-enc <KEY_ENC> --key-mac <KEY_MAC> --security-level 1" \
  -e "install_cap ./simlab.cap --install-parameters-non-volatile-memory-quota 255 --install-parameters-volatile-memory-quota 255 --install-parameters-stk 010001001505000000000000000000000000"

pySim documentation can be found here osmopysim-usermanual

Installing the CAP file with redacted card keys

The final check is another application list:

PKG: D07002CA44 (LOADED)
     Applet: D07002CA44900101

APP: D07002CA44900101 (SELECTABLE)

Application list showing the installed package and applet AID

At this point the applet is installed and selectable. It also exposes a small SIM Toolkit menu entry called SIM lab, which I keep only as a manual sanity check. The interesting path is the SMS trigger.

The applet

If you want to build your own applet you can refer to this repo for the simple “hello world” program.

The applet registers for unformatted SMS-PP envelope events:

ToolkitRegistry registry = ToolkitRegistry.getEntry();
registry.setEvent(EVENT_UNFORMATTED_SMS_PP_ENV);

When the event arrives, it asks the terminal for local date, time and time zone:

ProactiveHandler proactive = ProactiveHandler.getTheHandler();
proactive.init(PRO_CMD_PROVIDE_LOCAL_INFORMATION, (byte) 0x03, DEV_ID_ME);
proactive.send();

Then it reads the terminal response, formats the returned value, and displays it:

proactive.initDisplayText(
    (byte) 0,
    DCS_8_BIT_DATA,
    scratch,
    (short) 0,
    length
);
proactive.send();

The displayed text looks like this:

DTZ: 26-07-16 18:31:xx TZ=...

This is intentionally boring data, the security-relevant result is that an SMS delivered by the lab network caused code on the SIM to run and caused the phone to execute proactive SIM commands (SIM version of popping calc.exe).

Triggering it from YateBTS

Once the phone is registered to the private cell, YateBTS can send SMS messages to the subscriber from its web tools.

YateBTS SMS form

YateBTS log showing the phone registering to the private PLMN

For normal messages, this behaves like a small SMSC. For this experiment, the useful feature is the binary/RPDU mode: instead of writing a text SMS, the lab operator can provide a raw RPDU that YateBTS delivers to the registered IMSI.

The message is crafted as a SIM data download style SMS. The structure I used to build the message is the one described in the old SecurityGrind series Dissecting SIM Jacker, especially the exploitation article, which apparenlty is no longer online. That write-up breaks down an OTA-style SMS used in the Simjacker context: an SMS carrying a User Data Header, a command packet, security header fields, a Target Application Reference, and then the secured data that an application such as the S@T Browser would interpret.

At a high level, the payload construction looked like this:

SMS/RPDU wrapper
  -> SMS-PP data download indication
  -> binary/class-2 style delivery to the card
  -> optional user-data header / command-packet structure
  -> bytes that the terminal can forward to the UICC as an ENVELOPE

The fields I cared about were the ones that influence the delivery path rather than the visible message content:

  • the SMS is delivered as an MT SMS from the lab network
  • the protocol identifier is chosen for the SIM data download path
  • the data coding scheme is selected so the message is treated as SIM-specific binary data
  • the user data is structured so the terminal has something to forward to the card
  • the applet receives the resulting EVENT_UNFORMATTED_SMS_PP_ENV

From the YateBTS side, the successful run is very visible. The SMS is queued, delivered, acknowledged by the phone, and marked as finished:

MT SMS 'ybts/sms/<id>' ... finished

YateBTS log showing the successful MT SMS delivery

On the phone side, while the SIM Toolkit UI is open, the applet displays the date/time/timezone value returned by the terminal:

DTZ: 21-08-21 11:44:... (yes, the local time was not configured)

 phone popup showing the DTZ value returned by the terminal

That is the proof I wanted.

Takeaway

The SIM is not just a passive identifier.

It is a smart card, it can host applications, and under the right conditions it can react to events coming from the mobile network. The constraints are real, but so is the execution model.

That is enough to make them worth understanding.

References

  • Osmocom Simtrace2: https://osmocom.org/projects/simtrace2/wiki
  • Osmocom pySim: https://github.com/osmocom/pysim
  • YateBTS: https://yatebts.com/
  • GlobalPlatformPro: https://github.com/martinpaljak/GlobalPlatformPro
  • SecurityGrind, Dissecting SIM Jacker - Part 4 of 4: Exploitation: https://securitygrind.com/dissecting-sim-jacker-part-4-of-4-exploitation/ (404 not found)
  • ETSI TS 102 221: UICC-terminal interface
  • ETSI TS 102 223 / 3GPP TS 31.111: Card Application Toolkit
  • Harald Welte and his forum support https://discourse.osmocom.org/
  • Tomasz Lisowski’s simurai project https://github.com/tomasz-lisowski/simurai