Breaking HTTPS in the IoT: Practical Attacks For Reverse Engineers

As the old joke goes, the ‘S’ in “IoT’ stands for security. While (Internet of) Things can vary wildly in design robustness and overall security, many embedded devices nowadays have at least the basic protections in place. Happily, the egregious security mistakes of the past are now becoming less and less common. Despite the stereotype, Things in the IoT aren’t quite as bad as they used to be (pun intended).

For instance, the use of insecure communications (e.g., unencrypted HTTP), is now only found in a minority of Bishop Fox client product assessments, which gives a somewhat positive (and admittedly biased) picture of IoT security trends. In a twist of irony, the increasingly common implementation of encrypted communications to repel attackers is also an obstacle for pen testers assessing the security of the products, since the data is now hidden to everyone but the client and server. Overall, it’s a win for security, but it’s required us to develop new tactics for getting into that data.

In my time at Bishop Fox, I’ve had to overcome this problem on many, many hardware assessments, with Things ranging from consumer gadgets to networking equipment to Internet-connected industrial control systems. Regardless of the specific implementation, the goal at the start of every assessment is the same: decrypt HTTPS traffic so I can understand what the system is doing and why. Once I have this understanding, I can begin to attack the device itself, upstream services, and sometimes even other devices.

In this post I’ll show you three attack techniques for performing Man-in-the Middle attacks against production-grade, HTTPS-protected Things. For these examples, we’ll assume you’re redirecting all the device’s traffic through an HTTPS-aware proxy (like Burp), and that you have no administrative control over the device. All you have at the start is a view of the unintelligible encrypted stream, showcasing the full spectrum of unprintable ASCII characters:

 

Unintelligible encrypted stream

 

FIGURE 1 - Pictured: Frustration

THE DEFAULT ATTACK - "IT JUST WORKS"

Let’s first talk about the easiest and most successful type of TLS hijacking.

The Attack

  1. Insert the HTTPS attack proxy inline so that a Man-in-the-Middle attack is performed by the proxy, with default settings.

That’s it. No fancy configuration, no weird attacks.

 

HTTPS attack proxy inline

 

FIGURE 2 - This is what success looks like

This is always the first attack you should try, because it requires no special changes on your part, and is depressingly effective, succeeding against about 30% of all devices we test.

Why This Works

By default, intercepting proxies will automatically generate server certificates for incoming requests, signed by a randomly generated, non-trusted certificate authority (CA). The reason it would work is that the Thing’s HTTPS client is not performing any validation, and it will accept literally any certificate it receives, even one that is obviously wrong and forged. The HTTPS libraries used by Things often provide a way for developers to disable validation of the connection (e.g., LibCurl’s CURLOPT_SSL_VERIFY_PEER) for development and debugging. Getting a full HTTPS server set up and working in a development environment is often cost-prohibitive, so developers frequently opt to just disable validation until a proper staging environment is available. This same configuration will often sneak its way into production, resulting in quick and easy decryption of traffic.

THE LOOK-ALIKE CA ATTACK - "CLONING THE ROOT OF TRUST"

This type of attack takes advantage of mistakes in the certificate validation routines, specifically targeting the root CA certificate.

The execution of this attack is simple after a bit of setup. It can be run as an all-purpose dragnet against multiple devices and different connections all at once, so it’s a suitable second choice if the “It Just Works” attack above fails to produce results. In about 20% of our assessments, this style of attack will succeed.

The Attack

  1. Generate a new certificate and key pair, where the certificate is self-signed and has all the same fields as a major root certificate authority certificate.
  2. Insert this cloned certificate and key into the HTTPS proxy as a root signing CA for all requests and connections.
  3. For each HTTPS connection, generate a new server certificate signed by the cloned certificate.

Why This Works

To understand this attack, an overview of the HTTPS connection negotiation and validation process is helpful. Essentially, each internet-facing HTTPS server has an identifying certificate that is signed and validated by a chain of other certificates. These are generally issued by entities called certificate authorities (CAs), and the top of this chain is any one of a few trusted root CA certificates. The certificates for each of these root CAs are generally static and are distributed as a set.

During the HTTPS connection process, the client (in our case, the Thing) will check the top-level root CA certificate against the certificates that it has in its local database to make sure that they match. If they match, it proves that the entity sending the entire chain can be trusted. If not, then the connection is terminated with an error to the user. Although seemingly obvious, the way in which this certificate comparison is done is incredibly security-relevant, and we can take advantage of mistakes in this comparison.

In this specific attack, we’re creating a near-copy of a real CA’s certificate and using that as the root of trust for all our connections. If the certificate comparison doesn’t properly validate the root CA certificate against what it has in its local store, then the attack can succeed.

 

Real certificate diffed against its clone

 

FIGURE 3 - Real certificate diffed against its clone

We’ve done the hard part for you and released a set of scripts to generate certificates and key pairs for this attack automatically, so just run one of these to get your own PKCS12(. p12) file, ready to import into Burp and other HTTPS proxies.

THE INCORRECT NAME ATTACK - “THE OL’ SWITCHEROO”

For our last attack type, we’re going to take advantage of HTTPS clients that don’t check the name listed in a certificate against the actual name of the server to which they’re trying to connect.

Of the three types of attacks detailed here, I’ve found this to be the least effective (about a 10% rate of success), but it can still work in some cases.

The Attack

  1. Identify the HTTPS connection of interest, specifically noting the domain name of the server the Thing is connecting to.
  2. Gain ownership of a domain, either by buying a new one based on the name above, or by reusing one you already own.
  3. Generate a valid, trusted certificate (and chain) for the domain you own. You must go through a certificate issuing company for this, with prices ranging from “free” to “expensive.”
  4. Insert the certificate, it’s corresponding key, and the full valid chain into the attack proxy, and bypass any automatic certificate generation for all connections.

Why This Works

This attack is successful when the HTTPS client fails to match the server name (e.g. DNS name) against the server name listed in the certificate it received from the server (e.g., the COMMON_NAME). The relevant flag in curl is CURLOPT_SSL_VERIFY_HOST, but this failure can happen in any library, even if there’s no similar flag or option. In this case, the HTTPS client requires a valid certificate and chain, so the two attacks above won’t work, but doesn’t necessarily check to make sure the connection is actually going where it’s intended.

Improving Your Chances

There are a few ways to upgrade the effectiveness of this attack, as the ways that it can cause security failures in the Thing are numerous and subtle.

First, you can try changing the name of the domain in the certificate that you present so that it causes the name comparison to fail in specific ways. For example, assuming your device connects to the service at https://api.thingcompany.com, you could present a certificate chain for the server api.thingcompany.com.mydomain.com. Or you might try to see if any regex mistakes were made by presenting a certificate chain for apixthingcompany.com, as the regular expression "api.thingcompany.com" will match this. While it might seem odd for these types to attacks to succeed (as you’d expect a simple strcmp() call to be secure and accurate), the usage of wildcard certificates means that the name comparison functionality of an arbitrary HTTPS library likely isn’t straightforward as you would think.

 

def is_cert_correct(server_name, recvd_cert):
    if server_name == recvd_cert.common_name:
        return true
    else:
        if recvd_cert.common_name.contains("*."):
            # It's a wildcard cert, so it matches anything, right?
            return true
        else:
            raise CertificateNameInvalidException()

 

FIGURE 4 - Some definitely correct certificate checking code

A second way to improve this attack is to change the issuing root CA for the certificate chain you present. While Let’s Encrypt is a great free certificate service, not all embedded devices trust them as a valid CA. In these cases, we switch to using one of the bigger (and more expensive) CAs that are more likely to be trusted by a wide range of devices.

YOU'RE IN. WHAT'S NEXT?

Once one or more of the attacks above have succeeded, you can see exactly what the Thing is doing, and when. From here, you have insight into the APIs and services used by the Thing, and a number of vulnerability classes and attacks are open to you. Some of my favorites are listed below:

Backdoored Software Updates

Firmware updates are often distributed over HTTPS, and if the developer trusts that the HTTPS stream will never be compromised, you may be able to modify the firmware image on-the-fly in order to compromise the device and gain administrative control. See if you can add a user or two, or turn on debugging, or run a reverse shell back to a command and control host.

API Authorization Flaws

If a Thing API isn’t used by any mobile devices or normal web services, it may not have the same level of QA and scrutiny applied to it as other, more traditional web APIs. Take advantage of this hidden treasure trove of requests to hunt for authorization flaws that let you view and change devices other than the one you own (with permission, of course).

Insecure Setup and Decommissioning

The process by which a Thing is setup and decommissioned is often complex, and mistakes in this workflow design can lead to devastating security issues. A Thing often needs to tell its cloud service to create or delete relationships with user accounts or other Things, and if done incorrectly, can cause widespread outages and data loss. Because this process is often opaque, decoding and understanding it can be significant for reverse engineering efforts.

NONE OF THOSE WORKED. NOW WHAT?

Unfortunately, it’s not an uncommon occurrence for all attempts at decrypting the HTTPS stream to fail. The attacks above improve your chances, but if the Thing is well built and fails correctly, then you may need to find other avenues of attack. You can see if there is any sort of fallback behavior where the Thing connects to an insecure version of the service (like HTTP if HTTPS fails enough), but that’s becoming less common these days. Alternatively, you can try the tool sslcaudit, which performs some interesting client attacks of its own, and it was the inspiration for some of the techniques above.

You could also try physically attacking a Thing. (Those attacks can gain you root access.)

My advice to you is to do what I do: put my extensive consultant skills to use, and politely ask the developers for the API docs and an architecture walkthrough. That’s the type of time-saving hacker technique they don’t teach you in the dark web.