Serious Security: TPM 2.0 vulns – is your super-secure data at risk?

Even if you’re not entirely sure what a TPM is, you’ll probably know that if you want to run Windows 11, you need one.

More precisely, you need a TPM 2.0 (although there’s an official Microsoft workaround to get by with TPM 1.2, the previous, incompatible version of the technology).

TPM is short for trusted plaftorm module, a encryption-and-cybersecurity gizmo that was invented by an industry grouping known as the TCG, short for trusted computing group, whose controlling members, known as Promoters, are AMD, Cisco, Dell, Hewlett Packard Enterprise, HP, Huawei, IBM, Infineon, Intel, Juniper, Lenovo, Microsoft and Toyota.

TPMs are sometimes implemented as a miniature plug-in board (usually with 14 or 20 pins in two rows of 7 or 10) that plugs into a designated TPM socket on your computer’s motherboard.

Hardware TPMs of this sort contain a tiny, dedicated coprocessor with its own secure storage that provides a range of security-related functionality, including hardware random number generation, trusted creation of cryptographic keys, and secure digital signatures.

Other TPMs work by building the functionality into the regular firmware of your computer, or even by running a software-level emulator.

Obviously, a software TPM that runs as a Unix daemon or a Windows service under your regular operating system is handy when you want to run multiple VMs, or virtual machines, in order to simulate multiple computers on a single device. But a software TPM can only be activated once your operating system has loaded, so you can’t use this solution to install Windows 11 on a computer without a hardware-level or firmware-level TPM. Windows 11 insists that you have a TPM ready and active before Windows itself starts up.

It’s all about security (and other things)

One reason for forcing users to have a TPM is to secure the bootup process to stop attackers tampering with your BIOS or computer firmware and installing malware that loads before the operating system itself even gets going.

Another more controversial reason for requiring a TPM, especially in consumer laptops, is to use it for what’s known as DRM, or digital rights management.

DRM is accepted by many people as a reasonable solution to cut down on piracy, but opposed by others because it can provide a way for vendors to lock down or restrict your access to content of your choice.

Whether you welcome DRM or not (or simply don’t care), or whether you think a TPM gives you a potentially harder-to-hack Windows system than a computer without one…

…is largely irrelevant, because Microsoft insists that you have one to run Windows 11.

(There are hacks that claim to bypass this requirement, but we can’t recommend these tricks, and even in virtual machines, we’ve had unsatisfactory results when trying them out.)

Simple security can be complex

Unfortunately, and as you’ve probably guessed by now, the diminutive size of TPM hardware devices belies an extraordinary complexity that makes it hard for anyone, even the TCG itself, to create a compliant implementation that’s free from bugs.

The TPM Library 2.0 specifications alone, which form just a tiny part of the hundreds of different TCG specification documents, come in four parts, split into six documents – confusingly, there are two Part 3s and two Part 4s, one sub-part consisting of documentation alone, and the other consisting of interleaved code and explanation.

To give you an idea of the scale of TPM 2.0, the official specification files at the time of writing [2023-03-07] are:

Microsoft’s GitHub copy of the TCG “reference implementation” includes 5MBytes of source code totalling about 100,000 lines of C split into nearly 500 files.

On top of that, you need to import in a number of cryptographic algorithms from some other library and compile them into your TPM code.

You can’t rely on cryptographic functions supplied by your operating system, because a TPM chip is designed to operate independently of the rest of your computer, so it doesn’t depend on anything that could easily be replaced, subverted or left unpatched.

Microsoft’s source tree lets you pick by default from LibTomCrypt, OpenSSL and wolfSSL as your underlying code provider for symmetric encryption, hashing and big-number arithmetic. (Precise calculations involving numbers with hundreds or thousands of decimal digits are needed to implement public-key encryption algorithms such as RSA and Elliptic Curve cryptography.)

Beware lurking bugs

Amongst all this complexity, of course, lurks an unknown number of bugs, including two CVE-numbered vulnerabilities discovered in November 2022 by researchers at security spelunking company Quarkslab.

(We don’t know whether you pronounce that company name kwork slab or kworx lab; we suspect it’s the latter but secretly hope it’s the former.)

Quarkslab, admittedly with a dramatic flourish, announced the bugs as follows (their emphasis and capitalisation):

Two vulnerabilities found by Quarkslab in the TPM2.0 reference implementation and reported in November 2022 are now publicly revealed and could affect Billions of devices.

Who can be affected? Large Tech vendors[, and] organisations using Enterprise PCs, many servers and embedded systems that include a TPM.

In fact, the official TPM Library 2.0 “Errata” bulletin lists numerous other bugs along with these two, but as far as we know, the vulnerabilities reported by Quarkslab are the only two that received official CVE designation: CVE-2023-1017 and CVE-2023-1018.

Loosely speaking, these bugs are two sides of the same coding coin:

The reported vulnerabilities occur when handling malicious TPM 2.0 commands with encrypted parameters. Both vulnerabilities are in the CryptParameterDecryption function, which is defined in the Part 4: Supporting Routines – Code document. […]

One of the vulnerabilities is an out-of-bounds read identified as CVE-2023-1018. The second one is an out-of-bounds write identified as CVE-2023-1017. These vulnerabilities can be triggered from user-mode applications by sending malicious commands to a TPM 2.0 whose firmware is based on an affected TCG reference implementation.

Additional instances may be identified because of the TPM Work Group ongoing analysis and may result in a larger scope of potential vulnerabilities included in TCGVRT0007.

A “quick-fix” for these bugs was rapidly published for libtpms, a popular software-based TPM implementation that can be used to provide as many virtual TPMs as you like for multiple virtual machines:

The lines marked in green were added as patches against the flaws, and we’ll explain them quickly now.

The underlying problem with the unpatched code is that the function CryptParameterDecryption() receives redundant and potentially inconsistent information about how much data to process when decrypting the parameter buffer that’s sent in.

The function parameter bufferSize tells you how big the memory buffer is into which decrypted data will be written.

But the first two (or, depending on how the code is compiled, four) bytes of the buffer itself tell you how much space there is for decrypted data.

The original code therefore extracts those first bytes from the buffer and uses it as a counter to see how much actual data to decrypt…

…without bothering to check that there actually are two or four bytes available in buffer (as denoted by bufferSize) to start with.

This bug could result in a read overflow, with the code accessing bytes that it shouldn’t, which is why the updated code now includes a pre-flight check that the buffer has enough bytes to store the count value.

Even if the buffer does safely contain at least enough data for the length count, thus preventing a read buffer overflow, the original code consumes some of the bufferSize bytes in buffer, by extracting the bytes denoting the decryption length and advancing the buffer pointer accordingly.

But the code doesn’t decrease the value of bufferSize to match the fact that the buffer pointer has now been moved along in memory.

(If you “burn” the top two cards of a pack before starting to deal in a card game, you no longer have 52 cards left – you only have 50; if you’re dealing a poker hand, you’ll probably be OK, but if you’re dealing for a round of bridge, two of the players are going to end up short-handed.)

This bug could result in a write overflow, with decryption continuing past the end of the buffer and modifying two or four bytes that could belong to another process in the TPM’s memory.

More patches required

In fact, those patches alone are not enough, as the TCG’s bulletin warned above, and the libtpms code has already been updated again, though the additional patches have not yet made it into an official release:

This time, the similarly-defective “partner function” CryptParameterEncryption() has been updated, too.

As you can see above, the original version of the encryption function didn’t even have a bufferSize parameter, and always simply grabbed and computed the effective buffer length via the buffer pointer.

This means that the function prototype needed changing, which meant in turn that anywhere in the TPM code that called this function needed updating as well.

Fortunately, the code paths into the formerly buggy code are easy to trace backwards and retrofit with the additional security checks required.

What to do?

  • Reference implementations aren’t always correct. If you have any hardware or software products of your own that rely on this TPM Library code, you’ll need to patch them. Sadly, the TCG hasn’t yet provided patches to its own code, but has merely described the sort of changes it thinks you should make. If you’re wondering where to start, the libtpms project is a handy place to look, because the developers have already started digging away at the danger-points. (Work your way through at least ExecCommand.c, SessionProcess.c and CryptUtil.c.)
  • If in doubt, ask your hardware vendor for vulnerability information. Lenovo, for example, has already provided some information about products that include TPM code based on the reference implementation, and where to look for security bulletins to quantify your risk.
  • Avoid letting untrusted callers tell you how to manage memory. If you’re passing buffer pointers and sizes into trusted code, make sure you check and sanitise them as much as possible, even if it comes with a performance cost (e.g. copying buffers in controlled ways into memory arranged to suit your own security needs), before processing the commands you’ve been asked to carry out.