智慧城市真的安全吗?看看这款APP的分析报告

简介: 本文讲的是智慧城市真的安全吗?看看这款APP的分析报告,今年早些时候,我收到了我们市政府的一个NextDoor讯息,声称他们为民众提供了(购买)一个智慧城市应用程序。在此之前并没有任何关于这个应用程序的信息,而我也没有在网上找到其相关的内容,所以对于它我非常有兴趣尽兴更多地了解。
本文讲的是 智慧城市真的安全吗?看看这款APP的分析报告今年早些时候,我收到了我们市政府的一个NextDoor讯息,声称他们为民众提供了(购买)一个智慧城市应用程序。在此之前并没有任何关于这个应用程序的信息,而我也没有在网上找到其相关的内容,所以对于它我非常有兴趣尽兴更多地了解。

智慧城市真的安全吗?看看这款APP的分析报告

根据应用程序的描述,Bright City是“为城市和执法机构面向民众提供服务的专用移动应用程序”。除了NextDoor公告中提到的“财产锁箱”功能外,该应用还支持接收和提交报告可疑活动,当民众离开家之后进行财产监控保护,保护公民的家,并可报告市政维护问题。Bright City的网站上还介绍了一些额外的功能,包括公民可以去了解市政费用的去向,许可证等内容。

智慧城市真的安全吗?看看这款APP的分析报告

而我们可以看到应用程序中涉及的信息的性质似乎非常敏感,所以我很有兴趣进行仔细观察。我下载安装了应用程序,创建了一个帐户并登录。这是初始面板:

智慧城市真的安全吗?看看这款APP的分析报告

接下来我开始浏览应用中的某些选项,以生成一些API流量,在这一过程中没有发现到一些非常严重的问题 。下面是应用程序在获取当前用户的配置文件信息时所做的请求示例:

GET https://api.brightcityapps.com/api/user/getuser/***REMOVED*** HTTP/1.1
Host: api.brightcityapps.com
Connection: keep-alive
Accept: application/json, text/javascript, */*; q=0.01
User-Agent: Mozilla/5.0 (Linux; Android 7.1.2; Pixel XL Build/NHG47L; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: en-US
X-Requested-With: com.mobilesciencetech.brightcity

这里和我最近写的另一个本地市政应用程序类似,该请求不需要任何身份验证。毫无疑问这是令人震惊的,因为这会使一切变得更糟。以下是对上述请求的API响应:

{
"BusinessWatches": [],
"Carrier": null,
"City": null,
"CityGroups": [],
"CityGroupPermissions": [],
"DepartmentCallDetails": [],
"EventSignUps": [],
"HomeWatches": [],
"Lockboxes": [],
"Lockboxes1": [],
"LockboxInsurances": [],
"Lookouts": [],
"LookoutInfoes": [],
"Maintenances": [],
"OpenRecords": [],
"PatrolRequests": [],
"Permissions": [],
"Photo": null,
"Supports": [],
"SurveyResponses": [],
"UserNotificationSettings": [],
"UserLogins": [],
"UsersDevices": [],
"UserTransactions": [],
"UtilityBills": [],
"id": ***REMOVED***,
"cityid": 17,
"first": "Randy",
"mi": null,
"last": "Westergren",
"phone": null,
"cell": "***REMOVED***",
"carrierid": null,
"email": "***REMOVED***",
"dob": null,
"username": "rwestergren",
"password": "***REMOVED***",
"thumb": null,
"created": "2017-05-21T16:57:00",
"active": 1,
"admin": 0,
"brainTreeCustomerId": null
}

请注意,虽然我从上面的输出中删除了它,但用户的密码是以纯文本形式返回的。显然,这一点也非常的糟糕。

接下来,我决定反编译应用程序,看看我是否可以找到更详尽的API端点列表(以及是否需要任何身份验证)。这是过程中主要的活动:

package com.mobilesciencetech.brightcity;
 
import android.content.Intent;
import android.os.Bundle;
import org.apache.cordova.CordovaActivity;
 
public class BrightCity extends CordovaActivity
{
 
    public BrightCity()
    {
    }
 
    public void onCreate(Bundle bundle)
    {
        super.onCreate(bundle);
        bundle = getIntent().getExtras();
        if(bundle != null && bundle.getBoolean("cdvStartInBackground", false))
            moveTaskToBack(true);
        loadUrl(launchUrl);
    }
}

由于该应用程序使用Android Cordova,因此几乎所有代码都不在Java中。相反,当我转移到 assets / www  目录时,我发现了所有使应用程序运行的HTML / JS。

智慧城市真的安全吗?看看这款APP的分析报告

我打开了 editlockbox.js 文件,并查看了一下包含一些API请求的JavaScript:

$.getJSON(url + '/api/LockboxInsurance/GetLockboxInsuranceCompanies', function(companiesJsonPayload) {
    $('#companyid').append("<option value='0'>Select Insurance Company</option>");
    $(companiesJsonPayload).each(function(i, item) {
        $('#companyid').append('<option value="' + item.id + '">' + item.name + '</option>');
    });
    $('#companyid').append("<option value='Add'>Add New Company</option>");
});
 
var userid = window.localStorage.getItem("userid");
$("#itemid").val(id);
$("#userid").val(window.localStorage.getItem("userid"));
 
$.getJSON(url + '/api/lockbox/getlockbox/' + id + '', function(result) {
    if (result == null) {
        $('#result').append('<li>Item does not exist</li>');
    } else {
        $("#userid").val(window.localStorage.getItem("userid"));
        $("#name").val(result.name);
        $.getJSON(url + '/api/lockboxcategory/getlockboxcategories', function(categoriesJsonPayload) {
            $(categoriesJsonPayload).each(function(i, item) {
                if (item.id == result.categoryid) {
                    $('#categoryid').append('<option value="' + item.id + '" selected>' + item.name + '</option>');
                } else {
                    $('#categoryid').append('<option value="' + item.id + '">' + item.name + '</option>');
                }
            });
        });
        $.getJSON(url + '/api/LockboxInsurance/GetUserPolicies/' + userid + '', function(companiesJsonPayload) {
            $('#insurance').append("<option value='0'>Select an insurance policy (choose one)</option>");
            $(companiesJsonPayload).each(function(i, item) {
                if (item.PolicyID == result.policyid) {
                    $('#insurance').append('<option value="' + item.PolicyID + '" selected>' + item.CompanyName + "-" + item.PolicyNumber + '</option>');
                } else {
                    $('#insurance').append('<option value="' + item.PolicyID + '">' + item.CompanyName + "-" + item.PolicyNumber + '</option>');
                }
                //$('#insurance').append('<option value="' + item.PolicyID + '">' + item.CompanyName + "-" + item.PolicyNumber + '</option>');
            });
            $('#insurance').append("<option value='Add'>Add new policy</option>");
        });
        $("#description").val(result.description);
        $("#serial").val(result.serial);
        $("#make").val(result.make);
        $("#model").val(result.model);
        $("#caliber").val(result.caliber);
        $("#additionalinfo").val(result.additionalinfo);
        $("#room").val(result.roomlocation);
        $.getJSON(url + '/api/lockbox/GetUserLocationRooms/' + userid + '', function(companiesJsonPayload) {
            $('#room').append("<option value='0'>Select location of item (choose one)</option>");
            $(companiesJsonPayload).each(function(i, item) {
                if (item.Name == result.roomlocation) {
                    $('#room').append('<option selected value="' + item.Name + '">' + item.Name + '</option>');
                } else {
                    $('#room').append('<option value="' + item.Name + '">' + item.Name + '</option>');
                }
            });
            $('#room').append("<option value='Add'>Add new location</option>");
        });
        //$("#insurance").val(result.policyid);
        $.getJSON(url + '/api/photo/getphoto/' + id, function(photoresult) {
            if (photoresult.name != "") {
                var image = document.getElementById('thumb');
                var imageData = iurl + '/upload/lockbox/' + id + '/thumb/' + photoresult.name;
                image.src = imageData;
                image.style.display = 'block';
                //$('#showthumb').html('<img src="' + iurl + '/upload/lockbox/' + id + '/thumb/' + photoresult.name + '" style="width:56px; height:56px; border-radius: 28px; -webkit-border-radius: 28px; -moz-border-radius: 28px;" />');
            }
        });
        $.getJSON(url + '/api/photo/getdoc/' + id, function(docresult) {
            if (docresult != null) {
                alert(docresult.name);
                var doc = document.getElementById('doc');
                var docData = iurl + '/upload/lockbox/' + id + '/doc/' + docresult.name;
                doc.src = docData;
                doc.style.display = 'block';
                //$('#showdoc').html('<a href="' + iurl + '/upload/lockbox/' + id + '/doc/' + docresult.name + '" style="width:56px; height:56px; border-radius: 28px; -webkit-border-radius: 28px; -moz-border-radius: 28px;">' + docresult.name + '</a>');
            }
        });
        //$.getJSON(url + '/api/lockboxinsurance/GetInsuranceDetails/' + id, function (insresult) {
        //    if (insresult != null) {
        //        $("#policynumber").val(insresult.PolicyNumber);
        //        $("#companyid").val(insresult.CompanyID);
        //    }
        //});
    }
});

您可以看到,所有请求都以类似的方式进行,没有任何身份验证或会话状态机制。接下来,我写了一个快速脚本来搜索所有的JavaScript文件,以生成所有端点的列表:

/api/agency/
/api/agency/getagency/
/api/brightcityapp/
/api/brightcityapp/geteventdetails/
/api/brightcityapp/geteventsforagency/
/api/brightcityapp/geteventsforagencybydaterange/
/api/brightcityapp/geteventsforagencybyloadcount/
/api/brightcityapp/geteventsforagencynew/
/api/brightcityapp/geteventsignupdetails/
/api/brightcitypayments/getcitypaymentsbydaterangenew/
/api/brightcitypayments/getcitypaymentsbyloadcountnew/
/api/brightcitypayments/getcitypaymentsnew/
/api/brightcitypayments/getcityutilpaymentsnew/
/api/brightcitypayments/geteventpaymentdetails/
/api/brightcitypayments/geteventpaymentsbydaterangenew/
/api/brightcitypayments/geteventpaymentsbyloadcountnew/
/api/brightcitypayments/geteventpaymentsnew/
/api/brightcitypayments/geteventutilpaymentsnew/
/api/brightcitypayments/getpaymentdetails/
/api/business/getbusinessesbyagency/
/api/businesswatch/
/api/businesswatch/cancelbusinesswatch/
/api/businesswatch/getbusinesswatch/
/api/businesswatch/getbusinesswatchesforagencybydaterangenew/
/api/businesswatch/getbusinesswatchesforagencybyloadcountnew/
/api/businesswatch/getbusinesswatchesforagencynew/
/api/businesswatchstatus/
/api/businesswatchstatus/getbusinesswatchupdates/
/api/city/getcitiesbyagency/
/api/country/
/api/eyecolor/
/api/gender/
/api/glass/
/api/haircolor/
/api/height/
/api/homewatch/
/api/homewatch/cancel/
/api/homewatch/cancelhomewatch/
/api/homewatch/gethomewatch/
/api/homewatch/gethomewatchesforagencybydaterangenew/
/api/homewatch/gethomewatchesforagencybyloadcountnew/
/api/homewatch/gethomewatchesforagencynew/
/api/homewatchstatus/
/api/homewatchstatus/gethomewatchupdates/
/api/house/gethousesbyagency/
/api/lockbox/
/api/lockbox/deletelockbox/
/api/lockbox/getlockbox/
/api/lockbox/getlockboxesforagencybydaterangenew/
/api/lockbox/getlockboxesforagencybyloadcountnew/
/api/lockbox/getlockboxesforagencynew/
/api/lockboxcategory/
/api/lockboxcategory/getlockboxcategory/
/api/lookout/
/api/lookout/getlookout/
/api/lookout/getlookoutsforagencybydaterangenew/
/api/lookout/getlookoutsforagencybyloadcountnew/
/api/lookout/getlookoutsforagencynew/
/api/lookoutinfo/
/api/lookoutinfo/getlookoutinfoforlookout/
/api/maintenance/
/api/maintenance/cancel/
/api/maintenance/getmaintenance/
/api/maintenance/getmaintenanceforagencybydaterangenew/
/api/maintenance/getmaintenanceforagencybyloadcountnew/
/api/maintenance/getmaintenanceforagencynew/
/api/maintenance/getpublicmaintenanceforagencybydaterangenew/
/api/maintenance/getpublicmaintenanceforagencybyloadcountnew/
/api/maintenance/getpublicmaintenanceforagencynew/
/api/maintenancestatus/
/api/maintenancestatus/getmaintenancestatusforrequest/
/api/message/
/api/message/getmessage/
/api/message/getmessagesforofficerbydaterangenew/
/api/message/getmessagesforofficerbyloadcountnew/
/api/message/getmessagesforofficernew/
/api/newsfeed/getagencynewsfeedslist/
/api/officer/
/api/officer/getofficer/
/api/openrecord/acceptopenrecordrequest/
/api/openrecord/getopenrecord/
/api/openrecord/getopenrecordsforagency/
/api/openrecord/getopenrecordsforagencybydaterange/
/api/openrecord/getopenrecordsforagencybyloadcount/
/api/patrolrequest/acceptpatrolrequest/
/api/patrolrequest/getpatrolrequest/
/api/patrolrequest/getpatrolrequestsforagencybydaterangenew/
/api/patrolrequest/getpatrolrequestsforagencybyloadcountnew/
/api/patrolrequest/getpatrolrequestsforagencynew/
/api/photo/getdoc/
/api/photo/getmaintenancephoto/
/api/photo/getphoto/
/api/race/
/api/scamalert/
/api/scamalert/deletescamalert/
/api/scamalert/getscamalert/
/api/scamalert/getscamalertsforagencybydaterangenew/
/api/scamalert/getscamalertsforagencybyloadcountnew/
/api/scamalert/getscamalertsforagencynew/
/api/skintone/
/api/state/
/api/state/getstate/
/api/support/
/api/trafficalert/
/api/trafficalert/deletetrafficalert/
/api/trafficalert/gettrafficalert/
/api/trafficalert/gettrafficalertsforagencybydaterangenew/
/api/trafficalert/gettrafficalertsforagencybyloadcountnew/
/api/trafficalert/gettrafficalertsforagencynew/
/api/user/
/api/user/getuser/
/api/weatheralert/
/api/weatheralert/deleteweatheralert/
/api/weatheralert/getweatheralert/
/api/weatheralert/getweatheralertsforagencybydaterangenew/
/api/weatheralert/getweatheralertsforagencybyloadcountnew/
/api/weatheralert/getweatheralertsforagencynew/
/api/weight/

此外,我在我的帐户下设置了一个“锁箱”,并上传了一些图像来测试其功能。而对于我的目录列表被允许查看这一点我没有丝毫的意外,这意味着应用程序中所有上传的文档和图像都可以被公开访问。

风险

毫无疑问,使用此应用程序的公众面临的风险非常多并且十分严重。应用程序本身存储的敏感信息,攻击者可以轻易获得,进而可以以其他用户(或警察机构)的身份向系统内提交欺诈性的活动和事件。

没有基本的认证要求,任何应用程序信息或操作/事件的完整性都不能保证是合法的。必须要清楚的一点是,这个应用程序中包含有用户密码(和其他个人信息),可疑人员的常驻报告,公民电子目录,甚至本系统中存储和使用的付款信息,而这些都是不安全的。 

同样需要注意的是,其对于用户的影响可能远远超过Bright City系统。用户在多个系统中使用相同的密码是常见的,因此以明文形式显示用户密码可能导致其他帐户(例如电子邮件,银行,社交媒体)的一一告破。 

报告

目前我已经就如何以最有效的方式解决这些问题进行了多次的推敲理论。在我的其他博客文章中,我总是尽力与供应商合作,报告和修补已识别的漏洞 – 但这次有点不同。我积极的向市政府报告了上述问题,我们有一个简短的通话来对此进行讨论。尽管供应商最终将整个应用程序脱机以实现身份验证,但我报告的其他许多问题仍未解决(包括目录列表问题)。

编者:在各类智慧城市、智慧社区等概念大举进入我们的生活中时,我们仍然需要时时刻刻的去关注安全问题,智慧化智能化的设备、程序固然能够使我们的生活更为便捷,但安全出了问题也可能让我们的生活陷入一片黑暗。




原文发布时间为:2017年7月31日
本文作者:鲁班七号
本文来自云栖社区合作伙伴嘶吼,了解相关信息可以关注嘶吼网站。
目录
相关文章
|
2月前
|
人工智能 安全 算法
AI与大数据:智慧城市安全的护航者与变革引擎
AI与大数据:智慧城市安全的护航者与变革引擎
35 1
|
8月前
|
算法 数据安全/隐私保护 索引
App逆向百例|09|某App hkey分析还原
App逆向百例|09|某App hkey分析还原
203 0
|
1月前
|
数据采集 小程序 网络安全
云擎技术---分析工信部APP备案的“传闻”
APP备案并非新事物,自2005年起已有非经营性互联网信息服务备案制度。备案针对的是网站主办者,而非用户,不涉及个人用户网络访问。网络接入服务提供者包括ISP和IDC,不限于三大运营商。通知要求不为未备案网站提供接入,但不影响国外软件使用。个人开发者不能涉及经营性内容,备案审核时长1-20个工作日。境内服务器和国内应用商店需备案,境外则无需。手机厂商不会开启白名单制,仅实行黑名单制。APP备案与民营经济发展壮大意见不冲突,工信部有权颁布相关规定。该政策不存在逐步试探底线情况,所有解读均有法律依据。
32 3
云擎技术---分析工信部APP备案的“传闻”
|
3月前
|
网络协议 算法 Android开发
安卓逆向 -- 实战某峰窝APP(动态分析)
安卓逆向 -- 实战某峰窝APP(动态分析)
42 4
|
3月前
|
算法
某圈app算法分析
某圈app算法分析
20 0
|
3月前
|
算法 安全 数据安全/隐私保护
某影视APP算法逆向分析
某影视APP算法逆向分析
20 0
|
3月前
|
算法 Java
某江app算法分析
某江app算法分析
17 0
|
3月前
|
算法 数据挖掘 数据安全/隐私保护
某合伙人app算法分析
某合伙人app算法分析
15 0
|
8月前
|
算法 Java 数据安全/隐私保护
App逆向百例|12|某电商App Sign分析
App逆向百例|12|某电商App Sign分析
248 0
|
8月前
|
数据安全/隐私保护
App逆向百例|10|某App x-zse-96分析
App逆向百例|10|某App x-zse-96分析
239 0