Last Friday I delivered a presentation at Insomni'hack about embedded security on how to break 'modern' embedded systems using fault injection and side channel analysis.
In this post, I will summarize my presentation and provide links to additional reading material and, when possible, open source software/hardware references.
Traditional embedded systems are awfully insecure in a hostile environment: they usually run unauthenticated code from flash/rom memories, contain a JTAG debugging interface allowing runtime control, etc. In addition to the lack of runtime protection, traditional systems store data in flash-like memories either in the clear or encrypted with a key that you can also find in the firmware.
Thus, in this scenario both data and code integrity and confidentiality can be easily compromised by an attacker with access to the device. Modern systems are most usually based on a complex System On Chip solution as shown in this figure:
In such a system, several techniques are used in an attempt to solve these security challenges:
- Secure Boot: this mechanism refers to a device that boots from internal code (e.g. an internal boot ROM) and performs some kind of authentication of the firmware to be loaded on the device. Obviously, for this to work the system needs to guarantee that the internal boot ROM cannot be easily tampered with and that code is properly authenticated.
- Secure data storage: in order to protect sensitive data, modern devices often include a secure storage in the form of some internal flash or One-Time-Programmable (OTP) memory. Access control rules are implemented on this storage in order to ensure sensitive data is only accessible to those parts of the chip that require it.
- Key stores: As a specialization of secure data storages, key stores allow the system to securely store cryptographic keys. Often these stores allow the main CPU to write keys into them and to instruct the on-chip cryptographic coprocessors to use them. Therefore, in this way keys remain secret even if one achieves runtime control of the system.
- Memory protection: Many modern systems implement a protection mechanism for their main DRAM memory. In order to avoid attackers from reading data off RAM chips, sniffing the bus or modifying the data easily, they implement DRAM scrambling / encryption mechanisms. These mechanisms are usually quite weak due to timing restrictions, but nonetheless pose some barrier to the attacker.
- Debug interface protection: In addition to the above, debug interfaces such as JTAG are not (supposed to be) left open on secure systems. Since usually the system developer wants to be able to debug the target during development time and in case of errors, the most common solution involves setting up a password protection scheme.
Thus, the JTAG interface is usually locked until a password/key is presented in some way. This can be either a hardcoded key, device-specific key, or even a challenge-response protocol.
The remainder of the presentation (and thus this post) focuses on how to get to these secret keys starting from a device implementing all these mechanisms.
Of course, depending on how well these mechanisms are implemented, it might be very well possible to achieve this by means of logical attacks (e.g. overflows, improper bound checking, etc.).
However, for the sake of argument we are going to assume that this is not possible and see what we can do at the more physical/hardware level.
Step 1: Achieving runtime control
Say we have no easy external interface providing unauthenticated data to the target that we can exploit (i.e. no browsers, no networking, no filesystem parsing... ).
What do we do? If you have been reading this blog before, you probably know I am thinking about fault injection. As I described in that post, we can make a device fail by bringing it just outside its normal operating conditions. This can be achieved by modifying the voltage supply to introduce short glitches (aka VCC glitching), by injecting optical energy (laser/optical fault attacks), EM energy, etc.
Now, if we time our faults precisely at the moment when the internal BootROM is verifying the integrity of external code, we might be able to bypass it and run unauthenticated code.
In absence of specific countermeasures, it is almost always possible to achieve this. Therefore, it is important to implement appropriate protection and detection mechanisms in order to guarantee the integrity of boot code. See this post for references on how to do this.
Also, keep an eye on Die Datenkrake, which promises an open source hardware and software design that can be used for fault injection purposes amongst others.
Step 2: Getting to the keys
So once we have runtime control of the target, we are usually allowed to encrypt/decrypt data at will. However, what we really want is to obtain the key so we can share it with our friends, sell it to the highest bidder, or just post it on twitter for the lulz.
So again, if we cannot find an easy way to obtain the keys through logical means, we will resort to side channel information. We can do this in two ways: by monitoring the chip or by injecting faults on it.
Step 2.a : Keys through side channel information
When a chip is functioning, information about its operation is leaked to the surrounding environment. Think of it as hearing stuff that you are not supposed to be hearing (e.g. what your neighbours where doing last night 😉 😉 ).
Even though you are not supposed to hear it, you actually do hear it. So you know what they are doing, and you make assumptions about what's happening in there.
The same happens with chips. When a chip is functioning, it takes more or less energy from the power supply depending on what it is doing. It also takes more or less time to compute, creates stronger or weaker EM signals (remember tempest?) around it or emits tiny photons depending on the activity it is carrying out.
Now, this activity is of course related to the process it is performing (e.g. encrypting some data) but also to the data it is using (e.g. input data, keys, output data...).
So what you do is you ask the chip to use that precious key he is not willing to show to you, and you monitor it while it is doing so. Kind of like the polygraph tests three-letter-agencies like to perform on super-bad terrorists.
How do we link this to the keys you are asking? Here is the trick: we ask the chip to perform lots of operations with the key using random input data. At the same time, we record side channel traces (let's say power consumption traces, using an oscilloscope).
Now, due to the fact that the power consumption is linked to the data used for the computations (and thus the key), we will use statistics to find out what the key is. What we do is we split the key in small chunks used by the algorithm (e.g. in DES 6 bits of key are mixed with 6 bits of data and fed into the S-boxes).
We refer to these internal results as intermediate values, and we create a model of how these values will leak to the power consumption. For instance, we assume that the Hamming Weight of these values is leaked in the power consumption. This means that whenever these values are computed, the consumed power varies depending on their HW (and probably also other variables).
Now, since there is a dependency between the intermediate values and the power consumption, we can use statistics to find out the amount of dependency. Since those intermediate values depend on small chunks of key, we can try all the possibilities and find out how much statistical dependency there is between the power traces and those intermediate values. The key guess showing more statistical dependency will probably be the correct key.
If we repeat this process for all the key chunks, then we obtain the whole key. Now you might be asking... how do I do this at home? Well, if you are a security test lab you can buy products like Inspector (from my employer ;-p ) or CRI Workstation.
But if you are not, you can take a look at this page which contains information from a presentation delivered at BlackHat EU 2013: http://www.newae.com/blackhat. You can also take a look at OpenSCA, a Matlab-based framework for side channel analysis.
Step 2.b: Getting keys through fault injection
So as I said above we can also get keys through fault injection. Depending on how the system is designed, it might be possible that the memory cells containing the keys are actually mapped in the memory space of the system.
In those cases, an access control mechanism is placed in between the bus initiators and the bus target (i.e. the memory itself) in order to identify whether the request is allowed or not.
As you can imagine, glitching this access control check would result in access to the secret keys for initiators that should otherwise have no access to them.
But sometimes you won't be as lucky and you won't have the ability to request a read of the key (even if it was denied) and attempt to bypass the access control checks... simply because the key is not mapped anywhere in memory.
What do you do then? DFA is the answer, my friend. With Differential Fault Analysis you can inject faults into the cryptographic algorithm itself, and by observing the changes in the output you can recover the secret keys. See this post for more information.
Conclusion and further reading
As you can see, when a device is under the control of an attacker (aka user in some cases ;-p ), there are a number of attacks that need to be considered in addition to the usual software/logical vulnerabilities.
By abusing environmental variables such as power/energy supply and consumption, radiation, etc. one can exploit hardware that would otherwise be secure.
Therefore, as an embedded system designer it is important to protect your code and hardware. Even in the presence of countermeasures, it is critical to test those countermeasures and verify that they actually do what you intended them to do (the company I work for can help you there 😉 ).
In order to protect against SCA attacks, you need to make the environmental variables statistically independent of your secret data or hide that dependency somehow. You must know that most countermeasures are covered by CRI's patents, so you might want to check with them before you implement them in your products.
In order to protect against fault injection, you need to introduce redundancy in your computations and make your software AND hardware resilient to induced errors. You can take a look at this paper for some ideas on how to do this at the application level.
Additionally, for more reading material on the subjects you can take a look at CHES (mostly side channel analysis) and FDTC (mostly fault injection) from the last couple of years. The DPA book is also a very good read with lots of background information on DPA attacks.
Of course, if you have any concrete inquiries you can address me in the comments, on twitter or elsewhere.