austrianredcross / stopp-corona-android

Android Source Code
https://play.google.com/store/apps/details?id=at.roteskreuz.stopcorona
GNU Affero General Public License v3.0
269 stars 55 forks source link

Remove P2Pkit #69

Closed TuxCoder closed 4 years ago

TuxCoder commented 4 years ago
P2Pkit is a SDK that allows you to find nearby persons that also use this sdk over 
different P2P-Channels(BLE/GATT, WIFI-Direct), it does not have a audo P2P-Channel.
More information found on https://p2pkit.io .

The internal workflow is used to exchange a UUID (kanka_id) over the P2Pkit-Channels. This is later on used to exchange data (in this case public keys) over the p2pkit cloud via WebSocket (wss://p2pkit-api-v5-prod.uepaa.ch)

Problem summary

The p2pkit api server knows ALL public keys, kanka_id/p2pkit uuid and ephemeral id (token) and their relationship and can build a full contact network.

Even if the public keys and the ephemeral ids change the relationship is still visible for the server because the p2pkit UUIDs never change. For details see 'Local UUIDs'

In addition Alice and Bob have both the public keys, kanka_id/p2pkit uuid and ephemeral id of each other. This is not necessary.

The ephemeral id is only 32bit long and is enough to ask the p2pkit api for a public keys, kanka_id/p2pkit uuid of a third person. Brute forcing this small space and getting all public keys seams feasable. This information could be used to send fake infection-messages to the red cross server (rca-coronaapp-rcp-fd.azurefd.net).

Impact

The app always sends the public key to the nearby devices in combination with a static UUID from p2pkit-sdk to a ueppa server (p2pkit-api-v5-prod.uepaa.ch) (hosted in Amazon EC2 cloud in Frankfurt/Germany).

This leads to a privacy breach, when someone uses the data that is send over the p2pkit api server. Also a new key rolling every day would not fix this problem, because the UUID stays the same for an app until it gets deinstalled and not reinstalled from a backup.

It is possible to track the person pseudonymised. This gives advertise tracker a perfect identifier for each person. This can be used to measure user behavior. Also the advertise industrie show that they are able to invest already more in similar technics like face recognition and bluetooth mac address tracker.

Proposed Solution

Remove p2pkit and forget it for these kind of apps.

Analysis of the Datenschutzfolgeabschaetzung

Ref: https://www.roteskreuz.at/fileadmin/user_upload/Bericht_Datenschutzfolgeabschaetzung_StoppCorona_App.pdf

On page 10/13 with the data flow chart there is an issue. pink 4a and pink 4b also contains the kanka_id (P2P ID) and the ephemeral ids (rollierende ID) from the other enduser, This leads to be able to identify everyone by this kanka_id that is static over the lifetime of the app installation.

Analysis of other contact tracing app concepts

Analysis PEPP-PT https://github.com/DP-3T/documents/blob/master/Security%20analysis/PEPP-PT_%20Data%20Protection%20Architechture%20-%20Security%20and%20privacy%20analysis.pdf

Contact Tracing Joint Statement https://www.esat.kuleuven.be/cosic/sites/contact-tracing-joint-statement/

Data Privacy Policy from P2Pkit

This is a quote from the data policy from p2pkit.

Source: http://p2pkit.io/pdf/160718_PrivacyPolicy.pdf

P2PDataexchange:

Your Applications using our Service may send data such as 
Application levelIDs, Application leveldata, or EndUserdata. This data is sent both
over the online channel (if available) through our Cloud Service and the P2Pchannel.
The data exchanges are not encrypted and we recommend Application developers to not
send sensitive data using our Service.

Last release 2017-08-24

http://p2pkit.io/maven2/ch/uepaa/p2p/p2pkit-android/maven-metadata.xml

http://p2pkit.io/maven2/ch/uepaa/p2p/p2pkit-android-lib/maven-metadata.xmln

<metadata>
<groupId>ch.uepaa.p2p</groupId>
<artifactId>p2pkit-android</artifactId>
<versioning>
<latest>2.0.6</latest>
<release>2.1.3</release>
<versions>
<version>2.0.4</version>
<version>2.0.6</version>
<version>2.1.2</version>
<version>2.1.3</version>
</versions>
<lastUpdated>20170824120556</lastUpdated>
</versioning>
</metadata>

Also the timestamps in the zip matches with that.

Maven repo has not https support https://github.com/austrianredcross/stopp-corona-android/issues/54

Local UUIDs

P2Pkit uses internal UUIDs called kanka_id. These are saved in the app_data folder eg. /data/data/at.roteskreuz.stopcorona/shared_prefs/ at first system start.

The UUIDs are used as senderProxyId and recipientProxyId during automatic handshake by p2pkit.

internal_p2pkit_preferences.xml Alice

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="kanka_id">bab450e9-472e-4cbc-a3bc-ec9908bac107</string>
</map>

internal_p2pkit_preferences.xml Bob

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="kanka_id">b3d3d4a1-7b73-457e-81b3-5bbe0b574629</string>
</map>

creation of the static P2Pkit-UUID

The P2PKit UUID is generated ones and stored as kanka_id in the shared preferences file internal_p2pkit_preferences.xml of the app.

o.a.b.j:

    static synchronized void a(Context context) {
        synchronized (b.class) {
            SharedPreferences sharedPreferences = context.getSharedPreferences("internal_p2pkit_preferences", 0);
            String string = sharedPreferences.getString("kanka_id", null);
            if (string == null) {
                string = UUID.randomUUID().toString();
                sharedPreferences.edit().putString("kanka_id", string).apply();
            }
            h = UUID.fromString(string);
        }
    }

websocket secret from java util random

Source: https://developer.android.com/reference/java/util/Random

Instances of java.util.Random are not cryptographically secure. Consider instead using SecureRandom to get a cryptographically secure pseudo-random number generator for use by security-sensitive applications.

https://android.googlesource.com/platform/libcore.git/+/android-4.4.4_r1.0.1/luni/src/main/java/java/util/Random.java

    public Random() {
        // Note: Using identityHashCode() to be hermetic wrt subclasses.
        setSeed(System.currentTimeMillis() + System.identityHashCode(this));
    }

Code in the p2p-sdk n.a.a.v:

public class v extends Thread {
    public final ByteBuffer A;
    public final Socket B;
    public OutputStream C;
    public Handler D;
    public final Random x = new Random(); // 🤬
    ....
  stringBuilder3 = new StringBuilder("Sec-WebSocket-Key: ");
  byte[] bArr2 = new byte[16];
  vVar.x.nextBytes(bArr2); // 🤬
  sb.append(Base64.encodeToString(bArr2, 2));
  sb.append("\r\n");
  byteBuffer.put(sb.toString().getBytes());

P2Pkit WS data dump

We did the analysis of the App with the version 1.1.4. The captured data is the actual data that is transfered over the network in a TLS websocket connection.

More detail dumps: https://gist.github.com/TuxCoder/875fd5de7bfff4272f0a0651a9b3eb56

Sample DiscoveryMessage Response from Bob to Alice

This following message is generated while a contact is exchanged between Alice and Bob.

Raw package before send to websocket (w/o websocket header)

00000000: 0000 000a 0000 0190 0000 00f4 0072 26df  .............r&.
00000010: 0000 0024 6261 6234 3530 6539 2d34 3732  ...$bab450e9-472
00000020: 652d 3463 6263 2d61 3362 632d 6563 3939  e-4cbc-a3bc-ec99
00000030: 3038 6261 6331 3037 0000 0024 6233 6433  08bac107...$b3d3
00000040: 6434 6131 2d37 6237 332d 3435 3765 2d38  d4a1-7b73-457e-8
00000050: 3162 332d 3562 6265 3062 3537 3436 3239  1b3-5bbe0b574629
00000060: 0000 000b 0000 0004 2f1a 24e3 0000 008c  ......../.$.....
00000070: 3081 8902 8181 00cc 4eaa 3b43 7763 f1f4  0.......N.;Cwc..
00000080: 7000 6303 4085 9f5f ca35 06a8 3eff ea76  p.c.@.._.5..>..v
00000090: 527f 7809 acd1 0419 1d61 5dc8 e1aa a2d6  R.x......a].....
000000a0: 5361 7c26 514d 7bed 1e52 0b8d 38fe f437  Sa|&QM{..R..8..7
000000b0: 569c de2e 3cf1 53bc c7cd 6fec 292f 7ea0  V...<.S...o.)/~.
000000c0: 5996 2869 45ef ef83 e890 b6e0 b17e f56b  Y.(iE........~.k
000000d0: 46e0 3856 b19c 0cfe a4b7 6650 28fa 1df9  F.8V......fP(...
000000e0: 9514 2c19 0f19 5126 c010 be83 a186 2bd9  ..,...Q&......+.
000000f0: 109f 043b 5974 3702 0301 0001 0000 0000  ...;Yt7.........

UUID / kanka_id / senderProxyId of of Bob

bab450e9-472e-4cbc-a3bc-ec9908bac107

UUID / kanka_id / senderProxyId of of Alice

b3d3d4a1-7b73-457e-81b3-5bbe0b574629

Ephemeral ID of Bob

2f1a 24e3

The ephemeral ID of Alice is known to the server by the ProxyConnectRequest when the app connects to p2pkit api.

Public key in this message fetched from local app database ('automatic_discovery')

30818902818100cc4eaa3b437763f1f4
7000630340859f5fca3506a83effea76
527f7809acd104191d615dc8e1aaa2d6
53617c26514d7bed1e520b8d38fef437
569cde2e3cf153bcc7cd6fec292f7ea0
5996286945efef83e890b6e0b17ef56b
46e03856b19c0cfea4b7665028fa1df9
95142c190f195126c010be83a1862bd9
109f043b5974370203010001

BLE/GATT

Service UUID

m.b.k.s:

    public static UUID a(UUID uuid) {
        return UUID.fromString("0000FFFF-0000-1000-8000-00805F9B34FB".replace("FFFF", uuid.toString().substring(4, 8)));
    }

BluetoothLeAdvertise

https://developer.android.com/reference/android/bluetooth/le/BluetoothLeAdvertiser

o.a.b.m.b.a.a:

AdvertiseSettings build = new AdvertiseSettings.Builder().setAdvertiseMode(2).setTxPowerLevel(3).setConnectable(true).build();
AdvertiseData build2 = new AdvertiseData.Builder().setIncludeDeviceName(false).setIncludeTxPowerLevel(false).addServiceUuid(new ParcelUuid(uuid)).addServiceData(new ParcelUuid(a2), cVar.a()).build();
AdvertiseData build3 = new AdvertiseData.Builder().setIncludeTxPowerLevel(true).addServiceUuid(new ParcelUuid(uuid)).addServiceData(new ParcelUuid(a2), cVar.a()).build();
AdvertiseCallback aVar = new a();
StringBuilder stringBuilder = new StringBuilder("Start advertising service: ");
stringBuilder.append(uuid.toString());
stringBuilder.append(" with packet bytes: ");
stringBuilder.append(Arrays.toString(cVar.a()));

Build in Analytics

url: p2pkit-analytics-v5-prod.uepaa.ch

The apps calls this server on start of the automatic discovery. In the response the wss address of the p2pkit api server p2pkit-api-v5-prod.uepaa.ch is announced.

TLS settings from p2pkit-api-v5-prod.uepaa.ch

testssl.sh is used for testing, full report: https://cpad.milliways.info/code/#/2/code/view/xJHP+QVt8zA1bjJkDUAbl18HVrBV1RkkwexEMB67aZY/present/

Result is not good not terrible.

Found problems:

Tested Server: 63.33.213.0:443 (p2pkit-api-v5-prod.uepaa.ch)

 TLS 1      offered (deprecated)
 TLS 1.1    offered (deprecated)

 Has server cipher order?     no (NOT ok)

 Obsolete: SEED + 128+256 Bit CBC cipher       offered

 OCSP stapling                not offered
mitsuhiko commented 4 years ago

Closing as duplicate of https://github.com/austrianredcross/meta/issues/3

See also the comment I left here: https://github.com/austrianredcross/stopp-corona-android/issues/66#issuecomment-631102833