SafeWA Tracing Vulnerabilities

2021-05-03

Introduction

Research has shown contact tracing apps to be an effective mitigation control when trying to contain the spread of COVID-19. While all Australian states have implemented electronic contact tracing measures, some Australian states have granted business the freedom to choose their own technology platform. The West Australian (WA) government has introduced SafeWA and mandated its use in many situations. The resultant pervasiveness of the system, combined with the nature of recorded data and the system’s important role in public safety makes it a subject of interest for security research.

A quick bit of OSINT research indicates GenVis was engaged for a cool 3M AUD. Health Support Services (HSS) clarified that they are responsible for coordinating the development, implementation and support of SafeWA and work in partnership with GenVis to do so.

A contact tracing threat model

Given their recent proliferation, contact tracing app threat modelling has been of interest to academia with a lot of attention given to the privacy-forward decentralised approaches championed by apple and google. In contrast, the state-based apps (including SafeWA) use a centralised check-in model. My personal assessment is that the greatest potential threats to this kind of application are:

  • Information leak of check-in activity
  • Data manipulation of check-in activity

Exploiting any weakness in these areas would be of intense interest to a sophisticated adversary due to the malevolent potential for:

  • Tracking movements of persons of interest (e.g. politically exposed persons)
  • Sabotaging or discrediting COVID-19 response efforts

These threats were explored during my research.

Disclosure timeline

2021-01-28  Initial report submitted to ACSC
2021-02-08  Sought update from ACSC (no response)
2021-02-16  Reported findings to GenVis (via LinkedIn)
2021-02-18  HSS contacted me for further information
2021-03-10  Sought updated from HSS (no response)
2021-04-03  Suspected release date of remediation
2021-04-28  HSS confirmed resolution
2021-04-30  Pre-publish disclosure sent to HSS
2021-05-03  Agreed disclosure

Versions impacted

Vulnerabilities were identified using Android app v1.0.3 and v1.0.5. Most of the vulnerabilities were ultimately present in the underlying API and therefore orthogonal to the app version in use.

SafeWA Overview

Inferred SafeWA architecture

Without any official documentation available describing how SafeWA is put together, it was helpful to assemble an inferred view of the system based on observations during my investigation.

(Approximation, not vetted by any other party)

SSL certificates from an Amazon CA, “AWSALB” cookies in the webserver response headers, and reverse DNS entries are indicative of a web front end hosted in AWS. Twilio json payloads and mongo objectIds were observed in SafeWA API requests and will be discussed in further detail later. Stack traces and assets within the android APK indicated Flutter was chosen as the mobile app framework.

Both the production hosts and development environments were readily identifiable from public Certificate Transparency (CT) data.

production hosts:

1
2
safewa.health.wa.gov.au
api.safewa.health.wa.gov.au

dev/test hosts:

1
2
3
4
5
6
uat.safewa.health.wa.gov.au
api.uat.safewa.health.wa.gov.au
test.safewa.health.wa.gov.au
api.test.safewa.health.wa.gov.au
dev.safewa.health.wa.gov.au
api.dev.safewa.health.wa.gov.au

Of note, the dev/test hosts are publicly accessible. While I didn’t consider this a finding it’s arguably not best practice and may provide the opportunity to explore new features or content before they are made officially available to the public. I focused the remainder of my investigation on the production environment.

Anatomy of a check-in QR code

A check-in journey begins with a user scanning a venue’s QR code. Keen eyed WA residents may have noticed that the SafeWA QR posters seem to vary in complexity, reflecting a variation in length of encoded content.

Typical SafeWA Short SafeWA Not SafeWA

Some locations displayed “check-in” QR codes that were unaffiliated with SafeWA, the smallest example above contains a simple link to https://artgallery.wa.gov.au/internal/register and returns an error if attempting to scan with the SafeWA app.

A typical SafeWA QR code contains a full URL with a base64 encoded JWT in the path, along with query string parameters describing the particular location (e.g. courtyard) within a venue (e.g. Big Sports Bar) that is being scanned.

1
https://safewa.health.wa.gov.au/qr-code/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZW51ZUlkIjoiNWZiZGY3MmVkOWZmNDIyMGQ3NjgzMjc1Iiwic2NhbkxvY2F0aW9uSWQiOiI1ZmJkZjcyZWQ5ZmY0MjAwOTQ2ODMyNzciLCJpYXQiOjE2MDYyODUxMDIsImV4cCI6MjIzNzAwNTEwMn0.6I1dIwO3wSDxEaUJEweURjpqdJyrMYksrSMjqq1Sk8s?scanLocation=5fbdf72ed9ff420094683277&venue=5fbdf72ed9ff4220d7683275

Using full URLs as the payload content of a QR code enables the opportunity to use (Android) App Links or (iOS) Universal Links to launch the contact tracing app from a successful scan with other barcode scanning apps. It’s unclear if there was a conscious design decision to not enable/implement this functionality. (HSS commented that their approach is aligned with most other state driven QR systems)

When we examine the JWT from the URL path there’s some obvious redundancy in venueId and scanLocationId parameters, with a mismatch being a potential attack angle to explore:

1
2
3
4
5
6
7
8
9
{
"alg": "HS256",
"typ": "JWT"
}.{
"venueId": "5fbdf72ed9ff4220d7683275",
"scanLocationId": "5fbdf72ed9ff420094683277",
"iat": 1606285102,
"exp": 2237005102
}.[Signature]

The short SafeWA QR codes contain only the JWT and appear to be the result of screenshots from the ‘business user’ mode of the app. They can be scanned by the app just like a typical QR code.

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZW51ZUlkIjoiNWZjYjUyMDU1ZDczMzcwZWY5NThhZDU3Iiwic2NhbkxvY2F0aW9uSWQiOiI1ZmNiNTIwNTVkNzMzNzkyODI1OGFkNTkiLCJpYXQiOjE2MDcxNjAzMjUsImV4cCI6MjIzNzg4MDMyNX0.DBwTXHi7GKwnaNZwIr3K2cF36Jz49wMP_7S3Cwk1uao

With no difference in the internal structure of the JWT:

1
2
3
4
5
6
7
8
9
{
"alg": "HS256",
"typ": "JWT"
}.{
"venueId": "5fcb52055d73370ef958ad57",
"scanLocationId": "5fcb52055d7337928258ad59",
"iat": 1607160325,
"exp": 2237880325
}.[Signature]

Findings

1. Missing authorisation on venue check-in requests

After scanning a QR code, the app would record the check-in activity using a combination of information from the scanned code, along with user information from the current logged in session.

POST /venues/5fc5c7c2d6f593a7e7649a58/enter

1
2
3
4
{
  "userId""5fc3c1123fdbc8a3c2f10ac6",
  "scanLocationId""5fc5c7c2d6f5935aec649a5a"
}

While normally an end user would have no control over these parameters, as long as a bearer token is provided from /users/login we can submit any values we like. There appeared to be minimal validation on any of the provided values. It was possible to generate a check-in event for totally invalid venue and location Ids and have them accepted by the API.

POST /venues/000000000000000000000000/enter

1
2
3
4
{
  "userId""5fc3c1123fdbc8a3c2f10ac6",
  "scanLocationId""000000000000000000000000"
}

Response:

1
2
3
4
5
{
  "success"true,
  "message""recorded scan event",
  "venue"null
}

HSS described the above behaviour as intentional;

While the API returns a successful response, errors are logged on the backend. This approach was taken so as to not leak information and to handle cases where a business may have deleted/recreated a scan location but neglected to reprint their QR code poster. It was deemed better to collect some data than none at all. This was done to ensure effective contact tracing could occur, which is of course the point of SafeWA.

However, the degree to which the venue details property is populated in the response still effectively leaks whether or not a valid venue/location was supplied.

Most concerning however was a missing authorisation check between the userId specified in the POST payload and the userId associated with the bearer token used for authenticating the request. Given any userId (including invalid ones), I could use a bearer token for my user to record a check-in on the other userId’s behalf. With a sufficiently large dictionary of userIds and locationIds, it would be possible for an adversary to generate an entirely fictitious set of check-in activity. (In contrast to my observations, HSS disputes this finding, commenting that the userId has always been derived from the bearer token)

Fortunately the authorisation was behaving correctly on the /users/entry-events API (which ensured you could only retrieve your own recent check-in activity).

2. Predictability/Enumeration of user, venue and locationIds

Examining the venueId and scanLocationId shows a high degree of bitwise overlap.

1
2
5fcb52055d73370ef958ad57 - venueId
5fcb52055d7337928258ad59 - scanLocationId

Instead of being randomised identifiers, these appear to be somewhat predictable MongoDB ObjectIds. As a result there’s a couple of minor information leaks present here:

  • Surfacing these internal Ids to the mobile apps reveals information about the technology in use (i.e. MongoDB, the count of DB servers)
  • The timestamp for when a business location was registered can be extracted from any SafeWA QR code poster (e.g. 2020-12-05 09:25:25Z for the above example)

More interestingly, the predictability of a MongoDB allows for the potential of a brute force attack. Leveraging the missing authorisation identified above, we can attempt bulk check-in requests for arbitrary locationIds until we get a hit.

While a full 12 bytes would be impractical to scan, we don’t actually have to scan the full address space. By starting with some known valid Ids, adjacent Ids can then be discovered. There are some tools that can assist with this kind of attack, however the presence of a MongoDB cluster (as evidenced by the multiple machine identifiers) does complicate this approach.

Presumably there is some form of API Gateway or Web Application Firewall in place that will detect and block brute force attempts like this (I avoided any high request volume tests to minimise potential disruption). There is some geoblocking (Albania and New Zealand were blocked) being utilised, with access seemingly restricted to Australian hosts only. The geoblocking will hinder an adversary’s ability to utilise an overseas botnet. (HSS indicated that there are unspecified controls in place to mitigate these kinds of enumeration attacks)

3. Missing QR code validation

Physical attacks on contact tracing QR codes seem outlandish until they happen. The SafeWA app does perform some device-side validation of a scanned QR code and is able to detect/reject a non SafeWA QR code (such as the artgallery.wa.gov.au example earlier). The QR codes being scanned by the app are ultimately untrusted input, however by virtue of the JWT inside the QR code’s payload, consumers can verify the HMAC of the JWT to determine if the QR code being scanned was originally generated by SafeWA.

Unfortunately, as the JWT is not actually included in the venue entry request, it’s possible to generate invalid QR codes that scan successfully and are accepted by the API.

n/a

The exploitability of this in isolation is low, but does contribute to allowing an adversary to skip physically visiting a large number of locations to harvest valid QR codes. It also impairs end-users from self reporting problems with QR codes they encounter.

Other observations

When attempting a password reset

PUT /users/reset-password

1
2
3
{
  "userName""+61 444 444 444"
}

Response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
  "success"true,
  "message""Your SafeWA password has been reset. You should receive an SMS with instructions shortly.",
  "smsSent"true,
  "smsResponse": {
    "body""Your SafeWA password reset link is:  https://safewa.health.wa.gov.au/reset-password/404cdc6dcbf107d149d8753dc961d138b05413b9",
    "numSegments""1",
    "direction""outbound-api",
    "from""+61480018865",
    "to""+61444444444",
    "dateUpdated""2021-02-15T11:20:36.000Z",
    "price"null,
    "errorMessage"null,
    "uri""/2010-04-01/Accounts/ACcaea02d0a5831a252dfa71c72651f933/Messages/SM62cfd784b7e64cdbb2ad94f044ae981d.json",
    "accountSid""ACcaea02d0a5831a252dfa71c72651f933",
    "numMedia""0",
    "status""queued",
    "messagingServiceSid"null,
    "sid""SM62cfd784b7e64cdbb2ad94f044ae981d",
    "dateSent"null,
    "dateCreated""2021-02-15T11:20:36.000Z",
    "errorCode"null,
    "priceUnit""USD",
    "apiVersion""2010-04-01",
    "subresourceUris": {
      "media""/2010-04-01/Accounts/ACcaea02d0a5831a252dfa71c72651f933/Messages/SM62cfd784b7e64cdbb2ad94f044ae981d/Media.json"
    }
  }
}

The SafeWA reply contains the full contents of a Twilio SMS API response. The mobile app seems to have no requirement for this data, logging it server side instead would normally be a far better approach.

Resolution

App updates

When launching the app on April 03 I, along with many fellow West Australians, was greeted with the following error page:

This was a pretty good indication that updates had recently been deployed, so I took a look to see if anything had changed.

POST /venues/enter

1
2
3
{
  "qrCode""eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2ZW51ZUlkIjoiNWZjNWM3YzJkNmY1OTNhN2U3NjQ5YTU4Iiwic2NhbkxvY2F0aW9uSWQiOiI1ZmM1YzdjMmQ2ZjU5MzVhZWM2NDlhNWEiLCJpYXQiOjE2MDY3OTcyNTAsImV4cCI6MjIzNzUxNzI1MH0.HXxK2ty_DdMOn9yoL9Au1jDGVPTp3ITqiXnv8bR7LCo"
}

I was pleased to see that the venue check-in action now only submits the QR code’s JWT, without any additional user or location parameters. This now allows the QR code to be validated server side (eliminating the ability to create forged QR codes) as well as ensuring that the user’s identity is captured from the bearer token (thus eliminating the missing authorisation).

To support legacy clients the existing /venues/{venueId}/enter endpoint was also updated to ignore the (redundant) userId parameter from the requestBody, and instead grab the userId from the bearer token.

Final Thoughts

It was reassuring to see appropriate authorisation over requests that retrieved check-in history. On the other hand, it was possible for a sophisticated adversary to pollute the check-in data set with forged check-in events. An attack at scale would likely run into difficulty due to the substantial volume of discovery requests required. Fortunately, it appears recent updates have been made to successfully resolve the the identified vulnerabilities.

Given the prevalence of contact tracing apps around the nation, and the urgent circumstances in which they were developed, I expect these applications to be an area of ongoing interest to security researchers.


Comments: