Contest: reverse engineer immunization card verification API -- hackathon July 10th)

325 views
Skip to first unread message

Mark Jenkins

unread,
Jul 4, 2021, 1:38:36 AM7/4/21
to Announce
(Note, I originally wrote this on June 24th. Put off posting it as I
wanted to think it over and edit a bit more and then lost interest for
a bit... Tonight this is back on my radar and I'm announcing a virtual
hackathon for noon Saturday July 10th!)

----------
(Original June 24th deferred post)

Good news everybody,

The Manitoba Government has unintentionally created a white hat
reverse engineering contest that's of broad public interest.

The immunization card verification app was published for Android and
iThings Wednesday June 23rd.

Amoung other things (*) I don't like that this is proprietary software
with an undocumented API. Our Manitoba restaurateurs, who are able to
seat vaccine verified mixed households indoors as of Saturday June
26th should be able to meet their regulatory requirement to validate
these customers with free, libre open source software.

Nor should they be forced into the broadly proprietary world of
Android and iThings.

Broadly speaking, I believe the ability to communicate with one's
government should be through open protocols, just as we have open
laws.
(positive example, the CRA has an open XML format for employers submitting T4s)

So the contest is:
A) Reverse engineer the API used by the verification app.
B) Provide proof of concept alternative client implementation.

-----

I do not believe public health will be undermined by reverse
engineering this. From the outside, the protocol and app appears to be
properly designed and good protocols don't rely on their secrecy for
security. If a design problem was noticed, then this is a chance for
it to be reported and fixed.

I'm not concerned about the prospect of restaurateurs doing their own
app to perform a mix of guanine and fake verifications at their
discretion. This falls into the same category as not performing
verifications at all or just pretending to. They don't need the API to
commit that kind of fraud. For that reason, agencies performing
compliance checks are going to need to re-verify mixed household
indoor tables anyway. Opening up the protocol doesn't change the need
to actively enforce the public health rules in that manner with boots
on the ground.

An open protocol does lower the cost for a restauranterer to engage in
unethical logging of name + status. Though I doubt there's much
incentive and a lot more about patrons can be logged by way of
security cameras and contract tracing forms.

Bad actors will reverse engineer the API anyway. The value to good
actors of an open protocol outweighs this concern.

------------
What I know so far.

The app requires login to the portal found at the redirect for
https://immunizationcard.manitoba.ca . Registration is open, the
intention being that it is also the place where you can request your
own card.

After completing open registration you can log into the app. There
isn't a requirement that you go far enough with the portal to have
received your own card to use the verifier app.

The verifier app does QR scans. The immunization card QRs consist of
the portal base URL
https://immunizationcard.manitoba.ca/

and ends with ?ID=
and a unique user identifier.

source:
https://www.reddit.com/r/Winnipeg/comments/o0llyt/i_decoded_the_qr_code_on_the_proof_of_vaccination/

https://imgur.com/Gur6cDZ

The URL aspect is just there to document. Some people wondered if the
app would be dumb enough to just run an embedded browser on any URL.
It does not, I scanned "https://gov.mb.ca" and got the nopes response.


So presumably the protocol and API is two step.
1) Login to the portal, results in some kind of cookie or token used in step 2

2) Requesting verification at a particular end point of vaccine status
with the identifier part from the vaccine card codes. Returned data is
the persons name and either "fully vaccinated" or "no data found"

One reverse engineering approach is to disassemble/decompile the app binary.

Another approach is to customize TLS certificate authority (CA)
storage on a device with the app, construct a private network where
you can mess with some combination of DNS and IPs and direct to an
alternative end-point with a TLS certificate signed by your own
self-generated certificate authority.

Doing so requires knowing the domain name of the end point that the
device is trying to reach so your own self-signed TLS certificate
matches that name. Discovering that name may just be a matter of being
in control of the DNS server queried or intercepting an unencrypted
DNS request. Tougher if an encrypted request to a specific DNS server
is made.

Worse case the end point domain name may need to be extracted by
dissassemble/recompile, and from there a network interception approach
to reverse engineering can be resumed.

Mark

[p.s. re among other things *, ask in private]

---------

UPDATE July 4th,

The Android app has been taken down!

https://www.reddit.com/r/Winnipeg/comments/od2t7h/what_happened_to_the_manitoba_immunization/

https://imgur.com/a/niUmAtB

This is a perfect example that an open API and libre implementation
are called for. Our government gave away our digital sovereignty
exclusively to the mobile app-store duopoly of Google and Apple and
has now reaped what it sowed.

Some poor government contractor is probably in the middle of trying
(on a weekend) to beg for the privilege of our government to publish
software to our own residents!

At the very least, an Android package should have been independently
published to provide an alternative pathway to Android devices that
ship without the Play Store.

(The most commonly seen ones are Amazon's Fire series. Are believe
there are also floss firmwares that don't have Play Store)

This could very well be causing some restaurateurs who are already
hurting to feel the need to buy additional iThings in the interim!

Therefore, I call for a hackathon on Saturday July 10th at noon.
I will be present on
https://meet.jit.si/ScientificBeesRetireSometime
#hackathon on Slack
#skullspace on Libera

Presumably by then Google will have shown mercy on our Province and
the Android app will be available again. That will not diminish the
need for the hackathon.

Our restaurateurs should not be forced into an abusive relationship
with Google or Apple to seat customers under this regulatory
requirement.

Open protocols, open APIs, open government!

Free, libre, open source software!

HACK THE PLANET!

Mark Jenkins

unread,
Jul 10, 2021, 7:12:27 PM7/10/21
to Announce
And we pretty much have this API reverse-engineered now. Very big
thanks to both Alex W for his work in advance and Alex T for his
comments on slack regarding authorization and what might be possible
with given auth tokens.

The authentication of the website and app is based around this standard
https://en.wikipedia.org/wiki/OpenID#OpenID_Connect_(OIDC)

There's a manifest that self-documents some of this auth behavior at
https://govmbcitizen.b2clogin.com/govmbcitizen.onmicrosoft.com/b2c_1_signupsignin/v2.0/.well-known/openid-configuration

At the end of the authentication process, you get a bearer access token.

One element of OAuth/OpenID Connect is the client_id . Evidence
suggests this is different when using the website vs using the
verification app, though they appear to share the same code base.

Different client_id makes sense because the verification app and
website for requesting your own immunization card serve different
purposes.

I logged into the website https://immunizationcard.manitoba.ca/ from
my browser with developer tools enabled. I was able to grab my access
token. Accompanying metadata said it would be valid for 24 hours.

I already have a digital immunization card on file that I successfully
applied for.

I was also able to see that the web-app calls GET /api/citizen to show
me all of the digital immunization cards already on file. It is cards
plural because someone might be managing this for multiple members of
their own household for example.

My browser was providing both the bearer token and cookies to the
server. Alex T suggested the server may only care about the bearer
token. I gave it a try by running

$ wget -O - --header 'Accept: application/json, text/plain, */*'
--header 'Authorization: Bearer MYVERYLONGBEARERTOKENFROMMYBROWSER'
https://immunizationcard.manitoba.ca/api/citizen

and boom, JSON.
[{"firstName":"MARK","lastName":"Jenkins","certificateId":"https://immunizationcard.manitoba.ca/?ID=MY-TYPE4-RANDO-UUID}]

The outer part of the response is a JSON list because of the
possibility of having multiple cards.

Where is the QR code you may ask? The browser side JS is responsible
for computing it using the value of the certificateId field.

Some folks may take an interest in /api/client as a means other than
screenshot or print to download their digital cards into alternative,
offline wallets.

------------

Now it was evident from Alex W's highlighting of relevant sections of
the JS packed into the Android APK that the API endpoint relevant to
card verification would be GET /verification/userUUID where userUUID
is what follows after ID= on valid cards.

I discovered this was actually relative to a baseURL of
https://immunizationcard.manitoba.ca/api
https://gist.github.com/markjenkins/72e42fc91c0a98bad7558a1c9896060a#file-client_id_aka_appid_and_base_url-js

Alex T suggested the client_id used by the mobile app to request
access tokens may not be treated any differently by the server than
the client_id used by the web-app. After-all, the general public is
intended to be able to use both and is inevitably going to have access
to both client_id anyway.

And sure enough, he was right!

$ wget -O - --header 'Accept: application/json, text/plain, */*'
--header 'Authorization: Bearer MYVERYLONGBEARERTOKENFROMMYBROWSER'
https://immunizationcard.manitoba.ca/api/verification/randomWellFormedByInvalidUUID

returned
{"firstName":null,"lastName":null,"isValid":false}
e.g., looks like this in the mobile app
https://imgur.com/a/3JKSK2r

And
$ wget -O - --header 'Accept: application/json, text/plain, */*'
--header 'Authorization: Bearer MYVERYLONGBEARERTOKENFROMMYBROWSER'
https://immunizationcard.manitoba.ca/api/verification/myValidUUID

returned
{"firstName":"MARK","lastName":"Jenkins","isValid":true}
e.g. looks like this in the mobile app
https://imgur.com/a/PinDqhh

------------

So, the verification API itself ended up being as simple as you'd
expect. I definitely had my money on the response payload being JSON.

Don't ask me (yet!) to fill in any further details on the
authorization API and how to get yourself a valid token with only
email, password and your own raw code. I skipped getting a full
understanding of that part of things today by just letting my browser
run the JS and just extracting the resulting bearer token.

With the bearer token lasting 24 hours, my next step (another day)
might even be constructing some kind of verification "machine" as a
proof of concept demo that just takes a 24 hour bearer token as part
of it's local configuration. I'll circle back to understanding
documenting the auth steps more fully another, later time. I know
there's a few different fields involved in oAuth token request stuff
like scope.

Until then, you learn more about the Auth yourself by looking up
what's known about the OpenID_Connect standard (there are many
implementations to draw upon), what's documented in the OpenID Connect
Manifest, what you can ascertain from dev tools in your browser and
what you can learn from the source that Alex W deminified.

Though, with the browser's oAuth flow providing a suitable token for
now, and browser dev tools being pretty good, I don't think there's
any further point in looking at the mobile app JS for the moment.

Though if you were to study it, all 133492 lines or 5MB, it may help
to identify which sections are from imported javascript libraries and
which are unique to this app. I came across the infamous
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED from Facebook's
ReactJS for example.

Probably libraries are relied on for some of the OAuth/OpenID_Connect
stuff. The only relevant unique parts are which application specific
fields like scope are used.

Happy hacking and happy immunizations!


Mark Jenkins
Reply all
Reply to author
Forward
0 new messages