SmellsLikeML

Hacking the iTag Bluetooth Tracker

1/27/18

Bluetooth, Botnets, Networks, Alexa Skills, Home Automation, IoT

In the previous experiments, I used a Samsung device. Unfortunately, this required me to initiate a scan for nearby devices through android's OS. This seems to be a standard security feature of modern mobile operating systems.

To tag an important item with bluetooth, we found the DILITEC tracker appealing. This product features bluetooth low energy and a button that can be set to trigger cameras to take photos and drop pins on maps, so naturally the mobile app will require access to all these applications in your phone.

...No thank you!!

This means that without the phone, you will not be able to recover the keys. And so we are motivated by the fact that alexa devices are expected to remain stationary and commands can be issued around the home by voice.

To these ends, we turn to investigating bluetooth communications with the iTag device. This adafruit tutorial on hacking your smart light was extremely helpful and forms the basis for how we discovered, connected, and began to reverse engineer features of the iTag.

Specifically, we used hcitool to scan for the device named iTag:


sudo hcitool lescan

With the target address, we can use gatttool interactively running:


sudo gatttool -I

Next, we connect with the target device.


connect AA:BB:CC:DD:EE:FF

We expect the reply Connection successful. Then we can enter 'primary' into the prompt to discover the primary services. For other options, we could simply enter help.

The information on display using the gatttool prompt should be cross referenced against gatt services specifications. We can run 'char-desc' on a characteristic's handle to get more information and reference this output against the gatt characteristics specification. For instance, we find UUIDs beginning in 1800, 180f, 1802, and ffe0 corresponding to 'generic access', 'battery service', 'immediate alert', and the 'custom service' respectively. Inquiring on the 'immediate alert' service, we find a UUID beginning with '00002a06' which corresponds to '0x2A06' in the characteristics chart the 'alert level' is controlled. In code, we have:


primary

char-desc 0x000b 0x000b

You're probably curious about the 'custom service'... If you are connected to the device and you give the button a push, you will receive a notification of the form:


Notification handle = 0x000e value: 01

Here, the notification references the handle 0x000e associated with custom services.

At this point in the adafruit example, I simply tried out the char-write-cmd command on the alert service handle using the same value they used to manipulate the light color only to find it triggered a repeated beeping alarm. Very interesting!!


char-write-cmd 0x000b 58010301ff00ff0000

Pop out the battery to stop this. Now we have a way to kick off an alarm on the tagger using Alexa. To pin this down, I would need to study the packets being sent between devices. Here I refer to another part in the adafruit series to use wireshark to get more information about the communications.

After reviewing a few exchanges, you find the structure of the packets. Generally, they seem to take a couple bits followed by '111000000001'. Experimenting interactively, you'll quickly stumble upon '0100111000000001' to turn on the alarm. Naturally, replacing the 1 with a 0, '0000111000000001' shuts off the alarm. Putting this into python, we have a function like:


#!/usr/bin/env python
import sys
import pexpect

BD_ADDR = sys.argv[1]

def sound_alarm():
    child = pexpect.spawn('gatttool -I')
    child.sendline('connect {}'.format(BD_ADDR))

    child.expect('Connection successful', timeout=30)
    child.sendline('char-write-cmd 0x000b 0100111000000001')

This can be used to trigger the alarm and Alexa can help us to do this with a convenient voice UI.

Making our skill smarter

Rather than running the alexa code through a lambda function, we opt to create a flask server running flask-ask. This allows us to run code on our own computers where we can make use of machine learning libraries like XGBoost.

Getting the data is easy, we have a simple script to query the device for RSSI signal strength which we can run on a crontab every 2 minutes. These readings are quite noisy and tend to time out before getting the signal leaving many missing readings. However, with readings from 4 machines every 2 minutes, samples quickly accumulate.

Every few minutes, I would relocate the beacon and insert a marker into the log file like:


echo "KITCHEN" >> logfile.txt

This helped to label the samples and after accumulating readings from many different nooks and crannies, I parse the logfile and form a pandas dataframe with roughly 1000 readings taken over a day. Here, we can coarsen the labels somewhat. This will make learning easier and since we can trigger an alarm, we only care about being within earshot. I divide the space into 4 quadrants labeled: BEDROOM, BATHROOM, KITCHEN, and LIVING ROOM.

With each sample comprised of RSSI readings from 4 machines, we expect simple models to work well. Since we expect many missing values, we try XGBoost which handles missing values easily and trains in seconds.


clf = XGBClassifier(max_depth=4, learning_rate=0.05, 
                    n_estimators=300, 
                    objective='multi:softmax', 
                    n_jobs=10, num_class=4)

We pickle the model so that the flask server can read off the last N lines to construct test samples. Here, we use the idea that a lost item has likely been sitting for a few minutes and collect samples for the last 10 minutes, then we generate predictions for the last 5 readings and return the mode prediction to limit the impact of model error.

Check out the hackster project and the github repo for details.