Index ¦ Archives ¦ Atom

How easy is it to detect (and relay) BLE beacons?

Most of the digital contact tracing applications use Bluetooth, and Bluetooth Low Energy (BLE) in order to determine if smartphones (and smartphone users) are close to each other. Smartphones are expected to exchange and record beacons, which are small pieces of data used to uniquely identify each other and allow people that have been in close contact with sick individual to get a notification. Such tracking systems present many privacy risks, and their effectiveness has been questioned (but this is not the subject of this article). One class of attack against BLE based digital contact tracing systems is the class of relay attacks.

Relay Attacks

This class of attack consist in relaying data between users. For example, by listening to beacons that are emitted near an hospital or a medical laboratory, and re-emitting them in a shopping mall, you can increase the « perceived » exposure risk of a shopping mall user, as its phone will believe it is in contact with more people than it actually is, therefore increasing the risk of getting an exposure notification.

Let's note in reality, the relay has to be bi-directional so that the fake retransmitted beacons cannot be filtered against what the original senders received.

Many attack scenarios based on relay attacks have been described in the litterature on covid apps, for example scenario 15 of https://tracing-risks.com/docs/tracing-risks.pdf or point 4.5 of https://eprint.iacr.org/2020/399.pdf.

Feasibility

Are these attack feasible at a low cost, hard to implement? Let's find out. In the following, we focus on one digital contact tracing protocole, the ROBERT protocol, but everything should be applicable to other protocols (such as DESIRE) as well, and we focus on the on the StopCovid app, which will be an implementation of the ROBERT protocol.

Requirements

To implement a relay attack, we need a few components. If we use the analogy of the diagram above, we need:

  1. a listening device, the microphone, hearing the BLE beacons emitted in its vicinity;
  2. a broadcasting device, the loudspeaker, transmitting the data heard by the listening device;
  3. a network component, the cable, transmitting the data from the microphone to the loudspeaker;

An android phone, albeit being pricey, ticks all the prerequisites: it can receive and emit BLE beacons (otherwise, no digital contact tracing apps), and can transmit the data to another phone using a cellular connection. And it can do all of this in parallel, of course.

Detecting BLE beacons on Android

In the ROBERT protocol, HELLO messages have « a total length of 128 bits (16 bytes) ». I believe this means the whole protocol will fit into the Universally Unique Identifier (UUID) field of the BLE beacons, which has exactly this size. Other protocols may use a different method to convey proximity messages: for example, the DESIRE protocol use a scan request / scan response mechanism to be able to transmit up to 32 bytes, requiring two additional messages (one from the receiver, and one from the initial emitter).

But because we are lazy, and because the way beacons are advertised is not exactly specified in the documentation, we can directly go read the source code of the soon-to-be StopCovid Application to check how the beacons are sent on Android:

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* Authors
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Created by Orange / Date - 2020/04/27 - for the STOP-COVID project
*/

// […]

private fun buildAdvertiseData(data: ByteArray): AdvertiseData {
    return AdvertiseData.Builder()
        .addServiceUuid(ParcelUuid(settings.serviceUuid))
        .addServiceData(ParcelUuid(settings.serviceUuid), data)
        .setIncludeDeviceName(false)
        .setIncludeTxPowerLevel(false)
        .build()
}

private fun buildAdvertiseSettings(): AdvertiseSettings {
    return AdvertiseSettings.Builder()
        .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
        .setConnectable(true)
        .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
        .setTimeout(0)
        .build()
}

// […]

All beacons advertised by Android share the same service UUID, which has not yet been made public:

« In your service implementation you will have to declare the settings of the ProximityNotification SDK such as service UUID used to advertise and scan in BLE »

This service UUID will be easy to find once the actual application is released, though.

To listen to BLE beacons, we can use the Android BluetoothLeScanner API and log the results to the console:

private val mScanCallback: ScanCallback = object : ScanCallback() {
    override fun onScanResult(callbackType: Int, result: ScanResult) {
        Log.i("scanResult", result.toString())
    }

    override fun onBatchScanResults(results: List<ScanResult>) {
        for (sr in results) {
            Log.i("scanResult", sr.toString())
        }
    }

    override fun onScanFailed(errorCode: Int) {
        Log.e("scanFailed", "Error: $errorCode")
    }
}

/* StopCovid UUID */
var serviceUuid = UUID.fromString("XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")

/* Filter what's not using this UUID */
val scanFilters = arrayListOf(
    ScanFilter.Builder().setServiceUuid(ParcelUuid(serviceUuid)).build()
)

/* Mostly boilerplate */
val scanSettings = ScanSettings.Builder()
    .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
    .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
    .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
    .setReportDelay(0L)
    .build()

/* […] */

BluetoothLeScanner?.startScan(scanFilters, scanSettings, mScanCallback)

Transmitting BLE beacons to another phone

Now that we have the data, we have to transmit it to another phone. A simple solution is to use a MQTT broker, e.g. Mosquitto as a backend and a MQTT client application on the phone, e.g. Paho. This way, we can easily scale to multiple listener and multiple broadcaster, which would increase the efficiency of the attack. On the screenshot below, we can see an example of a BLE beacon emitted by a Lime electric scooter as received by my phone, and broadcasted to the MQTT broker. Nothing interesting to see here, really.

Advertising BLE beacons on Android

Transmitting the beacons is the most difficult task of this attack, because to be notified as being « at risk », you need to be at close distance (<= 1m) for at least 15 minutes, with someone with the application, and will be later diagnosed. So fire and forget won't do: one needs to transmit what one receives, multiple times. Good thing is, since version 8.0, Android supports Bluetooth 5.0, which enables to broadcast multiple beacons at the same time (well, not exactly the same time, but kind of). To do so, we can use AdvertisingSet which allows to dynamically change the data advertised by the beacons, and use some thread that will update the data every 100ms (for example).

Another interesting point is that one can change the advertisement parameters to advertise with a higher energy (e.g. ADVERTISE_TX_POWER_HIGH) than what the official application is doing (i.e. ADVERTISE_TX_POWER_MEDIUM), which would probably allow the attack to reach more people.

PoC and Caveats

A proof of concept has been developped, and it looks like it works, but much more testing would be necessary to be sure everything actually works with the real Stop Covid application (because, of course, the application is not out there yet, so there's no guarantee I got everything right).

A screenshot showing a phone running Stop Covid receiving its own beacon (retransmitted by another node faking the data) is shown below. For multiple reasons, i'll not publish the source code: it's pretty easy to reproduce with what's written there, and you don't have to trust me, so, reproduce.

Random Mac Addresses

Of course, the MAC address used to broadcast the beacons is not exactly the same as the original one, but as it's expected that the operating system (e.g. Android or IOS) will change them randomly for privacy purpose, it's not really a problem. Also, such a MAC Address could also be spoofed, if more work were put into the proof of concept, using something like an ESP32 microcontroller or even a SDR equipped with BTLE

Latency

Latency is not a problem: a round-trip message between Lyon and Paris will take less than 20ms through the internet, allowing any beacon being heared by such a system to be retransmitted pretty much anywhere in France in less than 1 second. And from what I observe, the Stop Covid App will emit the same beacons for at least a few tens of seconds.

Conclusion

Relay attacks are not new, and the only way to defend against them is to use some kind of bounding protocol that will prevent silently rebroadcasting beacons from one place to another. Such a protocol would use localization information (e.g. GPS localization) or limits imposed by physics (e.g. the speed of light), which is not feasible for contact tracing mechanisms based on bluetooth, or in a way that an user will trust (not using the GPS is a feature for an app such as Stop Covid). And it seems to me relay attacks are not really hard to deploy.

© Rémy Grünblatt. Built using Pelican. Theme by Giulio Fidente on github.