Osmophile

Osmophile is a virtual lab notebook that documents ongoing projects and experiments.

Reverse Engineering the PayRange BluKey Protocol - Part 1

2023-06-04, C. V. Pines

This article is part of a series where I attempt to reverse engineer the protocol used by PayRange BluKey laundry devices. In this first part, I perform some preliminary investigations using Bluetooth LE advertisements and characteristics in order to remotely read machine identity and state.

For Educational And Informational Purposes Only. All information presented in this article, including linked data, source code, and materials, is provided for educational and informational purposes only. The information provided should not be used for any unlawful purpose or employed in order to circumvent payment systems. Use at your own risk: the author assumes no liability for any claim or damages arising from or in connection with the provided information. All analysis performed in the course of this article involves publicly-accessible information and the interpretation of unencrypted data sent through public radio channels. No security systems or techniques were circumvented.

Introduction

I recently moved into an apartment building that uses a mobile app rather than coins to operate the laundry machines. I was curious about how the system works, so I decided to attempt to reverse engineer the protocol and build my own laundry monitoring / control system.

Posted signage indicated that I should install an app called PayRange. After doing so, I was instructed to create an account. Interestingly, I was not asked to specify a property name or street address, nor provide any kind of sign-up code. This implied that the app locates and controls laundry machines using a local wireless connection rather than solely via the Internet.

After a brief scanning period, the app displayed six numbered items corresponding to the six laundry machines. Each item displayed the machine’s state as well, indicating whether it was idle or in use. On selecting an item, an interface appeared with several payment amounts; confirming payment caused the corresponding machine to increase its payment counter in 25¢ increments until the final value was reached and the cleaning cycle could be started.

Given that the app can read the status of several machines at once and does not require any sort of pairing procedure, I strongly suspected it communicates via Bluetooth LE. I theorized it would accomplish this using publicly-advertised addresses and over an unencrypted channel.

Peripheral Scanning

Performing a quick Bluetooth scan immediately revealed the presence of several nearby BLE peripherals with the “PayRange” name, confirming the first part of my theory.

I wrote a simple Python script (available in Appendix A) to scan for BLE peripherals using the bluepy library, and captured the results of running the following command:

payrange scan -p PayRange > machines.json

(Note that the complete MAC addresses have been redacted for privacy. WW was skipped to avoid conflation with a washing machine.)

Address Name Type
7C:79:E8:TT:TT:TT PayRange LE Only, Legacy Advertising
7C:79:E8:UU:UU:UU PayRange LE Only, Legacy Advertising
7C:79:E8:VV:VV:VV PayRange LE Only, Legacy Advertising
7C:79:E8:XX:XX:XX PayRange LE Only, Legacy Advertising
7C:79:E8:YY:YY:YY PayRange LE Only, Legacy Advertising
7C:79:E8:ZZ:ZZ:ZZ PayRange LE Only, Legacy Advertising
Table 1. MAC addresses and advertising information for discovered peripherals.

I located six peripherals in total, corresponding to the three washing machines and three dryers. Unfortunately, all six BLE peripherals share the same name. This meant that I was unable to associate an address with a specific machine. Furthermore, I was unable to discern a pattern in the addresses that might correspond to each machine type.

Bluetooth LE Characteristics

I then expanded my Python script to enumerate the Bluetooth LE characteristics available on each peripheral, and captured the results of running the following command for each address:

payrange findchars [ADDR] > characteristics.json

By comparing the results for each address, I determined that all peripherals exposed the same set of BLE characteristics.

I hoped that the characteristics would encode the machine’s identity or state (e.g. idle, in use, complete), providing a way to associate the MAC addresses to individual machines. With this in mind, I added a command to capture the values of all readable characteristics from all peripherals:

payrange readchars machines.json characteristics.json > snapshot.json

Perplexingly, all readable characteristic values were the same across all six peripherals, except the “System ID” (which encodes the MAC address in a legacy format described by the BLE GATT specification).

Characteristic Properties Read Value
Appearance Read Generic/Unknown
Device Name Read “BluKey”
Firmware Revision String Read “5”
Manufacturer Name String Read “PayRange”
Peripheral Preferred
Connection Parameters
Read Interval: 10ms-20ms, Latency: 0,
Timeout: 4000ms
Service Changed Read, Indicate 0x0500FFFF
Software Revision String Read “Jul 26 2017”
System ID Read 0xXXXXXXFEFFE8797C
00001011-d102-11e1-9b23-00025b00a5a5 Read 0x07
00001013-d102-11e1-9b23-00025b00a5a5 Read, Write 0x01
00001014-d102-11e1-9b23-00025b00a5a5 Read, Notify “​”
00001018-d102-11e1-9b23-00025b00a5a5 Write n/a
0a1934f5-24b8-4f13-9842-37bb167c6aff Read, Write, Notify,
Write-No-Response
0x00
18cda784-4bd3-4370-85bb-bfed91ec86af Read, Notify 0x0000000000000000000000000000000000000000
99564a02-dc01-4d3c-b04e-3bb1ef0571b2 Read 0x01000218001A001C001D001F0021002200
a87988b9-694c-479c-900e-95dfa6c00a24 Read, Write, Notify,
Write-No-Response
0x01
bf03260c-7205-4c25-af43-93b1c299d159 Write, Notify,
Write-No-Response
n/a
fdd6b4d3-046d-4330-bdec-1fd0c90cb43b Read, Notify 0x01
Table 2. Discovered BLE characteristics and their corresponding values.

I took multiple snapshots for each characteristic on all peripherals across various states:

  • All machines idle
  • Washer paid for but not started
  • Washer started
  • Washer completed
  • Dryer paid for but not started
  • Dryer started
  • Dryer completed

Unfortunately, all readable characteristics reported the same values across each state. This implies that the machine state is not reported by any of these characteristics.

It is possible that machine identifiers / states are only reported after a write operation; notably, characteristics 00001013-d102-11e1-9b23-00025b00a5a5, a1934f5-24b8-4f13-9842-37bb167c6aff, a87988b9-694c-479c-900e-95dfa6c00a24, and bf03260c-7205-4c25-af43-93b1c299d159 all expose both read and write properties. Unfortunately, without a way to sniff BLE packets sent by the PayRange app, I had no way to test this hypothesis.

PayRange App

I decided to take a break and check the PayRange website. I located some operator manuals, one of which revealed that the app has a hidden function: after double-tapping the “Special Offers” header with two fingers, a “Tech Info” box appeared, containing a Device ID and firmware version:

(As before, the complete IDs have been redacted for privacy.)

Machine Device ID Firmware Version
W1 10GGGGGG 32
W2 10HHHHHH 32
W3 10IIIIII 32
D4 10JJJJJJ 32
D5 10KKKKKK 32
D6 10LLLLLL 32
Table 3. Device IDs and firmware versions for each machine, as reported by the PayRange app.

I hoped these IDs would correspond in some way to the MAC addresses, perhaps matching directly to the three differing bytes; unfortunately, I was unable to find any pattern connecting the two values.

RSSI Distance Estimation

At this point, I realized I was taking too complex of an approach while trying to associate each machine with a MAC address. I added a command to record advertisement RSSI for each address, placed my computer on top of each machine, and placed the average RSSI into a matrix:

payrange rssi machines.json > rssi.json

RSSI at Position W1 W2 W3 D4 D5 D6
7C:79:E8:TT:TT:TT -58.7 -69.2 -68.6 -75.0 -75.0 -74.0
7C:79:E8:UU:UU:UU -64.2 -61.4 -57.3 -78.5 -80.0 -73.7
7C:79:E8:VV:VV:VV -68.3 -65.2 -67.4 -62.0 -68.7 -65.0
7C:79:E8:XX:XX:XX -65.0 -57.9 -69.2 -75.5 -68.0 -78.2
7C:79:E8:YY:YY:YY -66.7 -69.6 -69.2 -58.0 -57.0 -67.4
7C:79:E8:ZZ:ZZ:ZZ -72.3 -74.0 -69.9 -67.0 -62.2 -60.8
Table 4. Mean observed RSSIs for each device when placed on top of each machine.

For each machine location, the table entry corresponding to the MAC address with the highest RSSI is bolded. This provided a “best guess” association. However, the small sample size and imprecision of RSSIs prevent me from drawing any definite conclusions.

A more robust version of this test could involve calculation of confidence intervals around each possible permutation, determined using propagation of uncertainty. I may revisit this method in a future article.

Advertisements Revisited

I later realized that the BLE advertisement packets sent by each peripheral contain more information than my script was capturing: some packets (but not all) include manufacturer specific data. I added a new command to the Python script and captured some values:

payrange mfgdata -p Payrange > mfg.json

7C:79:E8:TT:TT:TT 7C:79:E8:ZZ:ZZ:ZZ
0x8500FFGGGG9G00010800197A1F1801 0x8500FFLLLL9L00010800664451F601
0x8500FFGGGG9G00010800CFE9582801 0x8500FFLLLL9L00010800AEA5927501
0x8500FFGGGG9G000108007BCDFA9D01 0x8500FFLLLL9L000108004D3E416801
0x8500FFGGGG9G00010800ECCB8E1A01 0x8500FFLLLL9L000108003E0131AA01
0x8500FFGGGG9G00010800D860F7D501 0x8500FFLLLL9L00010800F18C694D01
7C:79:E8:UU:UU:UU 7C:79:E8:YY:YY:YY
0x8500FFIIII9I00010800BEC2D59001 0x8500FFKKKK9K00010800A116D21F01
0x8500FFIIII9I00010800DD613D8201 0x8500FFKKKK9K00010800A29C0C3301
7C:79:E8:VV:VV:VV 7C:79:E8:XX:XX:XX
0x8500FFJJJJ9J00010800D31968C901 0x8500FFHHHH9H00010800B24DDF4C01
0x8500FFJJJJ9J0001080020C991E401 0x8500FFHHHH9H0001080091D3A60C01
0x8500FFJJJJ9J0001080080CD7A8401 0x8500FFHHHH9H000108002451808701
0x8500FFHHHH9H000108007ABB9CBD01
Table 5. Manufacturer specific data included in some advertising packets from each device.

Some patterns are immediately visible:

  • Bytes 0–1 specify the Bluetooth radio manufacturer, BlueRadios, Inc.
  • Byte 2 is 0xFF in all recorded packets.
  • Bytes 3–5 correspond to the Device ID displayed in the hidden “Tech Info” box in the PayRange app.
  • Bytes 6–9 are 0x00010800 in all recorded packets.
  • Bytes 10–13 differ across machines and over time without any immediately obvious pattern.
  • Byte 14 is 0x01 in all recorded packets.

In a future part, I will investigate the effect of changing machine states on manufacturer specific data and attempt to decode state information.

Device Identification

Combining the results in Tables 3 and 5 allowed me to create a map between each machine, Device ID, and MAC address. Furthermore, it confirms that the methodology employed in Table 4 produced correct results.

It remains unclear how the PayRange app associates each Device ID with the displayed machine numbers. I suspect the association is made via an API call to an external server containing a database of Device IDs, machine numbers, and payment recipients; identifying this proposed API is outside the scope of this article.

Machine Device ID MAC Address
W1 10GGGGGG 7C:79:E8:TT:TT:TT
W2 10HHHHHH 7C:79:E8:XX:XX:XX
W3 10IIIIII 7C:79:E8:UU:UU:UU
D4 10JJJJJJ 7C:79:E8:VV:VV:VV
D5 10KKKKKK 7C:79:E8:YY:YY:YY
D6 10LLLLLL 7C:79:E8:ZZ:ZZ:ZZ
Table 6. Device IDs and MAC addresses associated with each machine.

Summary

In this first part, I determined that PayRange BluKey devices broadcast Bluetooth LE advertising packets that contain the Device ID (in addition to other undecoded data). I used this information to associate each machine with a Device ID and MAC address.

I also identified the set of BLE characteristics exposed by each peripheral, which can be used to determine the software and firmware revisions (in addition to other undecoded data).

In the next part, I will use differential techniques to further study the advertising packet’s manufacturer specific data. I will also use a dedicated Bluetooth LE sniffer device to capture communications between the PayRange app and connected machines.

Appendix A: Source Code