COVIDSafe iOS Vulnerability — CVE-2020–12717

Richard Nelson
4 min readMay 14, 2020

--

Summary

COVIDSafe for iOS, versions 1.0 and 1.1 contain a denial of service vulnerability, inherited from the OpenTrace code. The coding error allows an attacker within Bluetooth range to crash the application. This causes no further records of contacts to be stored until the user manually reopens the application. Only when the exploit is stopped, or the user is out of range, is the application able to run again. If attackers (such as a misguided group of activists) were motivated to disrupt the COVIDSafe system, they could use this bug to prevent contact exchanges in busy, popular areas.

Exploit in action

This bug also affected Singapore’s TraceTogether, Alberta’s ABTraceTogether and Poland’s ProteGO.

Technical analysis

When iOS is scanning for peripherals, it will let a relevant application know that it has discovered a peripheral through the application’s CBCentralManagerDelegate. The application receives the advertisement data that the peripheral is advertising. Part of this can include the manufacturer of the device, along with other manufacturer specified data. The first two octets include a company identifier code, which are assigned by the Bluetooth SIG. For example, Apple’s is 0x004c.

The OpenTrace code attempts to extract manufacturer data from here, assuming a minimum size for the manufacturer:

let androidIdentifierData = manuData.subdata(in: 2..<manuData.count)

Unfortunately, the manufacturer data is user controlled data. If the manufacturer data is a size of 1 byte (and only 1 byte), the COVIDSafe application crashes with the following error:

Fatal error: Can’t form Range with upperBound < lowerBound

To form the set of bytes, you need to understand how the advertisement data is structured. Manufacturer data has 31 bytes to use, and can be made up of multiple data elements. Each element has a single byte specifying the length of that element (not including this byte), another byte specifying the type of element, and then the data. 0xFF is the manufacturer data type. For example, if the advertisement data only contained the manufacturer, it would look like:

0x03 0xFF 0x4c 0x00

However, if we form the advertisement data to be:

0x02 0xFF 0x4c

Then the length of the actual data is only 1 byte. This isn’t a valid manufacturer, but is a valid element structure, and it causes the application to crash.

When you then reopen the application manually, the app will initiate a scan, see this peripheral and crash again immediately.

Although COVIDSafe does not correctly scan for peripherals in a background state, this bug can be triggered when the application is in the background as well. It requires that another iOS device acting in a central role scans it first. This causes iOS to give the background app time to run, at which point it may (depending on timing) run a scan, causing the bug to trigger.

The fix for this bug is to simply check the size of manuData prior to using it in this way.

The source code used in the video above can be found on GitHub.

Commentary

Interestingly, I wasn’t actually looking for bugs when I found this. Jim Mussared asked me to look at this line of code to verify iOS behaviour here when interacting with an Android device, and I noticed that this could potentially crash the app. I verified that it would crash, and Jim had the crash reproduced with his device emitting malformed advertisements within 15 minutes. If he wasn’t looking at this code, this bug may not have been found by people with an intent to disclose responsibly.

This is a fairly obvious bug that should have been picked up in an automated scan and/or an in-depth security review.

The model that governments have chosen to build on OpenTrace has made the disclosure process extremely painful. It seems as though (I could be corrected on this) each government received a code drop of OpenTrace at some point in time, and from there on there was little to no communication. There’s no central repository they all build upon, no process for communicating bugs up or downstream, and in fact no security contacts that I could find at all. I tried contacting each entity separately through the traditional support request forms, but did not get a response from any [Edit: The Singapore team have since been in touch and are working on a fix, great news!], apart from the DTA who I happened to have a specific email address for. If they had followed an established upstream/downstream model or all contributed to a monorepo, there would have been significant advantages to all involved.

As much as was my wish, it seemed impossible to coordinate disclosure between all affected entities. And there could be more affected applications that I just don’t know about.

DTA Response

The response from the disclosure of this particular vulnerability was very positive:

  • 6/5/20 22:57: Information of bug shared with DTA
  • 7/5/20 07:54: Response confirming receipt and that they were looking into it.
  • 7/5/20 13:13: DTA confirmed the bug and that they would have a fix in the next release
  • 8/5/20 19:03: DTA said they had fixed the bug and shared a timeframe for fix, and that upgrade advice for users would be settled.
  • 14/5/20: Fix released to users in the App Store.

A fast response, continuous updates with a good outcome.

--

--