Summary

This is the first part of a blog series about reverse engineering and exploiting Samsung’s TrustZone. Following parts in the series so far: 2, 3.

This first post covers the basics of the architecture. All of this is public info, nothing new, all of it has been covered in bits and pieces in various publications before. Some of it comes from Trustonic/Samsung materials, some of it from open source software, and some of it from the few great instances of prior research. It’s here as an intro, for completeness.

Later in the series, I summarize the reverse engineering results and explain the vulnerabilities that I have found.

Introduction

There’s been plenty of research on TrustZone already of course. Before last year, it focused mainly on implementations by Qualcomm and Huawei. A common theme of previous research results on these TEEs has been their single-point-of-failure nature: a generic lack of separation of privileges within these TEEs meant that a single vulnerability proved enough to compromise the entire system time and time again (and again etc etc).

The TEE used by Samsung is a bit different. It is created by Trustonic, which is a company entirely focused on trusted operating systems. Their TEE solution has been in development for something like 15 years. I’ll use “T-base” as the name of that TEE throughout this series, although technically Trustonic has renamed the TEE OS to Kinibi last year. (Similarly, Trustonic used to be called MobiCore and Giesecke&Devrient before that. These old names still show up in some bits and places, as you’ll see.)

Recently, I have presented a talk at Ekoparty on this subject (video, slides). The talk focused on the process of reverse engineering the T-base microkernel and the inner workings of T-base and less so on the actual functionality and attack surface provided by Trustlets (the “Trusted Applications” running in the TEE; basically the user-space processes of the Secure World OS).

At the time of the talk, Samsung hasn’t yet fixed all the vulnerabilities I have reported to them (part of the reason why I eventually decided to focus the talk on RE). However, they have in the time since, the last one in the January 2018 Bulletin. So, finally, I have run out of excuses for being lazy and not writing this up! Thank you for your patience :)

Samsung KNOX and T-Base

Since my work was done in the context of Samsung, the first question is how the TEE ties into Samsung’s overall KNOX security architecture. This is quite important, since in the early days of TrustZone, it was pretty much only leveraged for implementing DRM solutions, so from the perspective of the end-user, it was lot more of an attack surface increase than a value add. However, times have changed.

Samsung publishes quite a bit of information (1, 2) on the components they implement using TrustZone technology. The website I linked to actually does a pretty good job of explaining the features they provide, so I won’t repeat all that here. The tl;dr is that the TEE is used in three major ways:

  • security-sensitive features moved into the TEE so that they are not compromised even if Android is compromised (e.g. KeyStore, DRM, Certificate Management, Trusted PIN, Trusted UI, Remote Attestation for MDM),
  • features implemented by the TEE acting as a supervisor of Android in order to mitigate exploitation attempts (TIMA, Warranty based kill-switch of Trustlets),
  • access to hardware devices only permitted from the TEE in order to mitigate hardware-based attacks (e.g. fingerprint sensor, MST for contactless payments, etc.).

KNOX Architecture

All of the above share one trait: each feature is implemented by one (or more) Trustlet. This already tells us that in terms of actual functionality, we mostly should care about Trustlets. However, if we wanted to understand the security properties of these Trustlets (i.e. how they are isolated from each other, from the kernel of the Secure World and from the Non-Secure World), then we would have to dive into the T-base kernel.

I want to emphasize that my work focused on the later. While I will touch on actual functionality of some Trustlets, it is mostly in the context of demonstrating some vulnerabilities.

T-base Architecture

(If you want to read a more marketing-y introduction to T-base from the vendor itself, check it out here.)

The T-base architecture can be a bit overwhelming at first, as it relies on layers on layers on layers of abstractions and also because there really is a ton of information readily available, not in documentation per se, but through the open source code released by Samsung and Trustonic.

At the center of all this is always an Android process (application) talking to a Trustlet via “World Shared Memory” (WSM). The rest is just layers of abstractions and security boundaries provided by Android, the Linux kernel, and the T-base kernel.

Let’s start with the good old diagram of this architecture that has been a mainstay at least since the first published research on the subject (which I recommend you read as an intro by the way!):

MobiCore TEE Architecture

ATF: ARM Trusted Firmware

First of all, we need something to connect the secure (SWd) and non-secure worlds (NWd) on a hardware level. This is facilitated by the SMC instruction (“Secure Monitor Call”) on ARM, which switches execution from NWd to SWd, much like an SVC switches execution from EL0 to EL1. However, we don’t really jump to a handler in SWd EL1 with such an instruction, but instead execution jumps to what is called the Monitor Mode (EL3 for ARM). This is like a proxy whose goal is to marshall SMCs between the SWd and the NWd. In the case of Trustonic, this is NOT part of T-base, so it has to be implemented separately. (For more on ARM ELs, see here).

Samsung (and other vendors as well who use Trustonic by the way, for example certain Xiaomi phones using MediaTek chipsets) uses the ATF (“ARM Trusted Firmware”) implementation for the monitor mode. This is basically an open-source reference implementation by ARM themselves. Luckily for us, Quarkslab has written an excellent post on the topic of reverse engineering ATF, so check it out for more information (Part I and II).

The important thing to note is that not only will the Linux kernel make SMC calls to reach T-base (via ATF), but also we expect the T-base kernel to make SMC calls to talk to ATF. At this point, this is a black box though, we’ll learn about this more in the next post.

(Sidebar: it is not really true that ATF only marshalls SMC calls to T-base. There are some SMC calls that turn into commands implemented by ATF as well; this includes commands that are part of the open-source ATF code but also proprietary vendor-specific commands. This wasn’t the focus of my research.)

T-base: Microkernel

Then, on the right of the diagram we have the SWd, running T-base. By first approximation, there are three parts: the microkernel, Secure Drivers, and Trustlets. We will learn later that this assumption is false, and that the “Runtime Management” box in the diagram actually refers to another piece which is neither a Trustler or Driver, nor the microkernel. More on this in the second post in this series! For now, let’s just keep assuming that everything in the T-base OS that isn’t done by the Trustlets and Secure Drivers is actually done by the microkernel.

So, before we can think about Trustlets, we want to know how we can talk to the microkernel to manage (e.g. load) Trustlets.

To understand the finer details of this behavior, we will have to reverse engineer the microkernel. Samsung stores the T-base firmware in a non-obvious location: packed into the sboot image. Gal Beniamini has already documented a way to extract this image (see the section “Kinibi Revocation” of his blog post), more on this in the second post in this series as well.

Nonetheless, although we won’t know* how the microkernel implements the Trustlet management functionality until we reverse it, we can know quite a bit about the management interface, because this is publicly documented.

T-base calls this the MobiCore Control Protocol (MCP) Interface (MCI). This is built on top of SMC calls, so let’s look at what we know about these.

*Of course, if you have read Gal’s post, then you already know one implementation detail about Trustlet loading: before the variant Samsung introduced in the S8, T-base didn’t implement rollback protection for Trustlet loading.

T-base: SMC Fastcalls

If you are used to other TEE implementations, you might expect that every management functionality (load a trustlet, share memory with a trustlet, open a connection to a trustlet, etc.) will just be another microkernel-implemented supervisor call, directly triggerable via passing the right arguments to an SMC call, much like a Linux ioctl call.

Turns out, Trustonic went for a completely different approach and the reason is to actually preserve the “micro” nature of the microkernel. The implementation can be understood from the Linux kernel source, see drivers/gud/gud-exynos8890/MobiCoreDriver/*.c.

(Sidebar: the driver folder in the source trees is called “gud” because the original company name was “Giesecke und Devrient”.)

In total, there are five SMC fastcalls used by the Linux kernel. MC_FC_INIT is used to configure the queues, MC_FC_INFO can be used to get information (e.g. version info) from T-base, MC_FC_SWAP_CPU can be used to move T-base to a specific CPU core, and finally MC_FC_YIELD and MC_FC_NSIQ are used to schedule the running of T-base.

/* fast call init */
union mc_fc_init {
	union mc_fc_generic as_generic;
	struct {
		u32 cmd;
		u32 base;
		u32 nq_info;
		u32 mcp_info;
	} as_in;
	struct {
		u32 resp;
		u32 ret;
		u32 flags;
		u32 rfu;
	} as_out;
};

/* fast call info parameters */
union mc_fc_info {
	union mc_fc_generic as_generic;
	struct {
		u32 cmd;
		u32 ext_info_id;
		u32 rfu[2];
	} as_in;
	struct {
		u32 resp;
		u32 ret;
		u32 state;
		u32 ext_info;
	} as_out;
};

#ifdef TBASE_CORE_SWITCHER
/* fast call switch Core parameters */
union mc_fc_swich_core {
	union mc_fc_generic as_generic;
	struct {
		u32 cmd;
		u32 core_id;
		u32 rfu[2];
	} as_in;
	struct {
		u32 resp;
		u32 ret;
		u32 state;
		u32 ext_info;
	} as_out;
};
#endif
int mc_fc_nsiq(void)
{
	union mc_fc_generic fc;
	int ret;

	memset(&fc, 0, sizeof(fc));
	fc.as_in.cmd = MC_SMC_N_SIQ;
	mc_fastcall(&fc);
	ret = convert_fc_ret(fc.as_out.ret);
	if (ret)
		mc_dev_err("failed: %d\n", ret);

	return ret;
}

int mc_fc_yield(void)
{
 
	union mc_fc_generic fc;
	int ret;

	memset(&fc, 0, sizeof(fc));
	fc.as_in.cmd = MC_SMC_N_YIELD;
	mc_fastcall(&fc);
	ret = convert_fc_ret(fc.as_out.ret);
	if (ret)
		mc_dev_err("failed: %d\n", ret);

	return ret;
}

These commands are executed via the _smc() function which simply stores the arguments into registers and makes an SMC.

So we know that if we are looking for the SMC handler of the T-base microkernel by following how the VBAR is set, we should find only this simplistic implementation and we are going to have to reverse engineer an undocumented solution inside the microkernel to identify the place where the code actually processes MCP commands.

Of course, this can’t be the complete picture, as the Linux kernel also needs a way to be notified when T-base has responded. This is implemented using interrupts of course: the kernel driver registers an interrupt on the system, which T-base knows about also from the MC_FC_INIT command.

T-base: MobiCore Control Protocol

MCP is a shared buffer based protocol, therefore the only thing that SMCs are used for is to setup MCP queues and to notify T-base that there is a new input in the queues, in effect scheduling SW to run. There are two queues: the command queue is where NWd can load an MCP command itself, the notification queue is where NWd can load the identifier of a Trustlet, in case it wants to notify a specific Trustlet that there is a command waiting for it.

The MCP commands themselves are pretty self-explanatory: we can load/suspend/resume trustlets and map or unmap additional shared memory into the address space of Trustlet instances. The list of MCP commands can be found in the Linux kernel source, see drivers/gud/gud-exynos8890/MobiCoreDriver/mci/mcimcp.h.

/** Possible MCP Command IDs
 * Command ID must be between 0 and 0x7FFFFFFF.
 */
enum cmd_id {
	/** Invalid command ID */
	MC_MCP_CMD_ID_INVALID		= 0x00,
	/** Open a session */
	MC_MCP_CMD_OPEN_SESSION		= 0x01,
	/** Close an existing session */
	MC_MCP_CMD_CLOSE_SESSION	= 0x03,
	/** Map WSM to session */
	MC_MCP_CMD_MAP			= 0x04,
	/** Unmap WSM from session */
	MC_MCP_CMD_UNMAP		= 0x05,
	/** Prepare for suspend */
	MC_MCP_CMD_SUSPEND		= 0x06,
	/** Resume from suspension */
	MC_MCP_CMD_RESUME		= 0x07,
	/** Get MobiCore version information */
	MC_MCP_CMD_GET_MOBICORE_VERSION	= 0x09,
	/** Close MCP and unmap MCI */
	MC_MCP_CMD_CLOSE_MCP		= 0x0A,
	/** Load token for device attestation */
	MC_MCP_CMD_LOAD_TOKEN		= 0x0B,
	/** Check that TA can be loaded */
	MC_MCP_CMD_CHECK_LOAD_TA	= 0x0C,
	/** Map multiple WSMs to session */
	MC_MCP_CMD_MULTIMAP		= 0x0D,
	/** Unmap multiple WSMs to session */
	MC_MCP_CMD_MULTIUNMAP		= 0x0E,
};

(Sidebar: just because the intended functionality is pretty straightforward, it doesn’t mean that implementing all this mmaping of address ranges is also straightforward to do in a secure manner. In fact, this is something that pretty much all Android TEE vendors got wrong: see the [BOOMERANG]((https://www.cs.ucsb.edu/~vigna/publications/2017_NDSS_Boomerang.pdf) paper published by UCSB researchers for details!)

From the perspective of the Linux kernel, these functionalities are exposed via the /dev/mobicore device node, which implements the mc_user_fops operations (defined in drivers/gud/gud-exynos8890/MobiCoreDriver/user.c).

T-base: Trustlets and Secure Drivers

Moving on to the Trustlets, the first thing to point out is that Gal Beniamini’s blog post from last year gives great insight into the implementation of T-base Trustlets, so you may want to read that first.

To lay things out in more detail, read on.

From previous research by Tim Newsham, we know that the Trustlet binaries themselves can be found in /system/app/mcRegistry or /data/app/mcRegistry on a device (or the app directory when you mount the sim2img’d system.img from a Samsung FOTA image).

We also know what the MCLF (“MobiCore Loader Format”), the file format for Trustlets and Secure Drivers, looks like: in kernel sources, see a path like gud/gud-exynos8890/MobiCoreDriver/mci/mcloadformat.h. Here is an IDA loader written by Gassan Idriss that will get you started; Tim Newsham wrote another set of tools (1, 2) that converts an MCLF to ELF.

Distinguishing drivers from Trustlets is easy, there is a version field in the MCLF that shows this. Even quicker, in practice: the ones with a name that starts with “ffffffff00” are the Trustlets, the ones with a name that starts with “ffffffffd0” are the secure drivers. On the other hand, since these are named by their “uuid” instead of a meaningful name, it’s not so easy to tell what binary hides what functionality. Through reverse engineering (in most cases, grepping strings) it isn’t particularly difficult to figure this out though. For an enumeration of my results, see the next post.

While it makes sense that the MCLF format is present in the Android kernel sources (as it is be parsed in NWd during the loading process), it is a bit more surprising but nonetheless welcome that the so-called tlApi and drApi interfaces are also defined in some sources. I don’t know if this was an accident or not, I certainly don’t find this include path in every Samsung kernel source that is published, but there is at least one on github, where this is present.

These are the Apis that Trustlets and Secure Drivers use respectively to talk to the T-base microkernel. This includes functionality to interact with applications in the NWd, an IPC implementation that allows processes in the SWd (Trustlets/Secure Drivers) to communicate with each other, and calls that expose functionality that is typically handled by a kernel, such as mapping memory into address spaces.

As Tim Newsham’s and Gal Beniamini’s blog posts explain, Trustlets call these Apis via a call-gate (similarly to a GOT for ELFs with the exception that it has a single jump target for all API calls), aka the implementation of these calls isn’t compiled into Trustlet binaries. So this is another black box that documentation/sources/previous research didn’t help us with: where and how are these Apis actually implemented? We of course can suspect that these will actually somehow be wrappers for SVC calls, with the system call handlers implemented in the T-base microkernel; but we don’t know the details yet. Similarly, we don’t have a complete picture of all the calls in tlApi/drApi; on one hand, Tim’s blog post is a few years old, on the other hand, the list of Api calls it identifies (just like headers in kernel sources) is incomplete.

Regarding Secure Drivers, I didn’t find too much public information on them besides these headers. For now, it’s enough for us to understand that the point of Secure Drivers is to marshall access to various hardware elements; the Crypto Driver talks to the Crypto Engine, a Secure SPI driver talks over a SPI interface to the Fingerprint Sensor, another talks over a SPI interface to an eSE for secure payments (Samsung Pay), etc. This much is actually explained in Samsung’s documentation.

Regarding Trustlets and tlApis, we can find some public information on this, for example in a presentation by Jan-Erik Ekberg from Trustonic. A picture is worth a thousand words as they say, and this snapshot from his slides (read the slides!) says it all, really:

tlMain example

So the basic behavior of Trustlets is pretty straightforward. With respect to the IPC part (talking to Secure Drivers), this is a little bit more complicated, but as far as the Trustlet side of the interface goes, there is just the tlApi call tlApi_callDriver(), which works much like a Linux ioctl, in that it takes a command id and a pointer to a command structure as its arguments. Once again, this isn’t really documented or captured in Linux kernel sources normally, but nonetheless you can find at least one source tree on github with a header file that defines it.

Now, if you have actually read Jan-Erik’s slides :) you will have learned by now that there is also something called GP (“Global Platform”), a standardization initiative for TEEs, which has created a common API for different TEEs. Trustonic of course supports GP, so they also have APIs that implement exactly those; what they pretty much do of course is provide some glue to turn them into the relevant T-base specific APIs. However, luckily for us, Samsung seems to be using the “legacy” APIs everywhere in their Trustlets, so we don’t worry about this further.

T-base and Android

Finally we arrive at the Android layer. There is still code here authored by Trustonic, who have created a user-space daemon, mcDriverDaemon, to expose the interface to user-space applications. Luckily, understanding the implementation of this driver is pretty straightforward, because for some reason a previous variant of the code is available in open source here.

This daemon accesses the kernel device node via libMcClient.so, the commands of which map clearly to the MCP commands (see here).

Trustonic’s design was that only mcDriverDaemon would be able to access the device node and it would expose the interface via UNIX domain sockets. Trustonic uses the term TLC (“Trustlet Connector”) for this interface.

In the case of Samsung, the picture is messier. On the one hand, the device node is restricted using DAC and MAC (SELinux), but still many privileged processes can access it and some actually use it without going through mcDriverDaemon. Similarly, the UNIX domain sockets can only be accessed by privileged processes. Indeed, Samsung implemented both the direct (libtlc_direct_comm.so) and proxied (libtlc_proxy_comm.so) solution and in fact some processes use both.

Our journey is still incomplete, however. Obviously Samsung uses this setup to implement some TrustZone-backed functionality in Android entirely in privileged processes, like the system_server. But there is ALSO going to be functionality that extends beyond privileged processes to applications without permissions or with permissions that can be acquired by regular applications.

As expected, this is implemented by adding Binder interfaces to various privileged processes. Android of course implements APIs to allow processes to do access control on their Binder interfaces, so we would expect Samsung to do that. But we have to dive into reverse engineering to figure that out, as there is no documentation or relevant prior art left.

Gal has written about one such proxy previously, the otp_server. Others exist too, see for example this presentation on how this is implemented in the case of Samsung Pay. In my work, I focused on another, tlc_server. I haven’t explored other cases.

Hold On A Minute: Talking To Trustlets

Before we pat ourselves on the back for a job well done on enumerating the publicly available details of this architecture, we can’t help but notice a glaring omission.

While we have learned how to setup the WSM (TCI buffer) in Android to send commands to a Trustlet, we have seen nothing regarding the expected format of such commands, let alone guidance (libraries) for implementing common tasks. Is it really the case, that every Trustlet will have its own way?

Although it looks like that there isn’t a generic command structure for Trustlet communication, of course it’s possible that Trustonic shares developer guides for this with their licensees. But we can only find this out by reverse engineering, it seems.

Spoiler alert: in the end, reversing Trustlets told me that while Samsung Trustlets seem to be using a common header format for commands, it isn’t necessarily universal and it doesn’t extend to any kind of standard structure for command payloads across Trustlets.

Summary

Phew, we got through the intro! Let’s recap the various layers of this architecture:

  • Samsung Sboot follows the ATF model. Sboot loads both the EL3 firmware (ATF) and the OS running in EL0+EL1 of the SWd (T-base). After these are loaded, the bootloader switches the processor to NWd mode and loads the application bootloader aboot, which loads Android of course.
  • ATF traps the SMC calls made by EL1 of either world and proxies them between the two.
  • Linux kernel uses a fastcall (SMC instruction with arguments passed in registers) to initialize communication with the T-base microkernel, by way of setting up shared buffer areas that are used as the MCP command queue and the notification queue.
  • Linux kernel uses another fastcall to notify (schedule the running of) T-base. T-base uses an interrupt registered by the Linux kernel to notify the NW when it has finished processing a command.
  • Linux kernel implements the MCP, commands that allow loading and sharing memory with Trustlet instances and exposes this via an ioctl interface of the /dev/mobicore and /dev/mobicore-user devices. It includes sanity checking regarding WSM ranges, to ensure that user-space can’t request memory areas that could lead to Linux kernel corruption/privilege escalation inside NW. (This sanity checking implementation was found to be insufficient in the past.)
  • The libMcClient.so library implements the user-space side of this ioctl interface.
  • User-space processes leverage this library via different libtlc_* shared libraries. Sufficiently privileged processes can use the variant that loads libMcClient.so directly; others get access via the mcDriverDaemon, which in effect exposes the MCP interface through regular UNIX sockets, but adds sanity checks to avoid malformed MCP actions by clients, similar to the Linux kernel.
  • The sockets are still restricted via MAC and DAC mechanisms, so unprivileged applications can not get access to them. Instead, Samsung implements various proxy processes (such as tlc_server), that further expose the mcDriverDaemon interface via Binder. This, finally, allows applications with the right Android permissions to access the specific Binder interfaces to interact with T-base and load and talk to Trustlets.