Jump to content
Nytro

Reverse Engineer a Verisure Wireless Alarm part 1 – Radio

Recommended Posts

Posted (edited)

Reverse Engineer a Verisure Wireless Alarm part 1 – Radio Communications

by foip on November 16th, 2014

gnuradio_grc_fft21.png

1. Introduction

Verisure is a supplier of wireless home alarms and connected services for the home. A Verisure setup can be composed of multiple devices, sensors and/or detectors such as Motion detectors with camera, Magnetic contacts for doors or Windows, Smoke detectors, Keypads, Sirens, etc. Each component of the setup communicates using wireless technology with the central gateway called “Vbox”, it-self monitored by Verisure agents through the Internet and/or 3G connection.

As a Verisure customer, I was curious to get a clear view of the design and security measures implemented by the manufacturer. I therefore decided to buy a testing Kit on eBay (120 Euros) to open it and starting an exciting journey inside the boxes.

This post is the first part of my Verisure story and aims to observe radio communications between the multiple devices of the alarm. In other words, we will translate the radio communication into binary messages. Please note that Verisure is the new name of Securitas-Direct. You may potentially find both names in my scripts and screenshots.

Verisure1.jpg

2. Discovering frequency and modulation

We know that 433 MHz and 868 MHz are popular bands for such equipments. Starting our favorite spectrum analyzer (GQRX in this case) confirmed our thoughts by showing some strong pulses while we were pushing random keys on the keypad alarm, located next to the HackRF.

gqrx-discovery.png

In order to get a clear view of the signal, samples were recorded using hackrf_transfer tool and then opened into baudline:

[TABLE]

[TR]

[TD=class: gutter]1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24[/TD]

[TD=class: code]$ hackrf_transfer -s 2000000 -f 869000000 -a 0 -r /tmp/hackrf_verisure_s2000000_f869000000_01.iq

call hackrf_sample_rate_set(2000000 Hz/2.000 MHz)

call hackrf_baseband_filter_bandwidth_set(1750000 Hz/1.750 MHz)

call hackrf_set_freq(869000000 Hz/869.000 MHz)

call hackrf_set_amp_enable(0)

Stop with Ctrl-C

3.9 MiB / 1.000 sec = 3.9 MiB/second

3.9 MiB / 1.000 sec = 3.9 MiB/second

3.9 MiB / 1.000 sec = 3.9 MiB/second

4.2 MiB / 1.000 sec = 4.2 MiB/second

3.9 MiB / 1.000 sec = 3.9 MiB/second

3.9 MiB / 1.000 sec = 3.9 MiB/second

3.9 MiB / 1.000 sec = 3.9 MiB/second

4.2 MiB / 1.000 sec = 4.2 MiB/second

^CCaught signal 2

2.1 MiB / 0.540 sec = 3.9 MiB/second

User cancel, exiting...

Total time: 8.54128 s

hackrf_stop_rx() done

hackrf_close() done

hackrf_exit() done

fclose(fd) done

exit[/TD]

[/TR]

[/TABLE]

Nice! Zooming into the signal shows 2 spikes which means that we are probably in front of a 2-FSK modulated signal.

baudline.png

3. Chipsets and datasheets

Before going further, it could be interesting to learn a bit more about the micro-controller used by the devices. As you will see, this information is really helpful since it gives us some clues about the potential modulation, ciphering, data encoding, etc. Opening a magnetic contact revealed a CC1110-F16 chip.

magnet_pcb.png

Briefly, the datasheet informs us about the following capabilities of the chip:

  • Modulation: 2-FSK, GFSK, MSK, ASK, and OOK
  • 128-bit AES supported in hardware coprocessor (so if data looks encrypted, we probably already know which cipher suite is in use)
  • 8051 MCU architecture (needed later for IDA Pro)
  • Optional automatic whitening and de-whitening of data.

Additionally, we know a magical firmware called RFCat which can definitively help us to learn and play with CC1110 chips. RFCat will be largely used in the next parts of our Verisure story. For now, we will focus on GNURadio framework and the HackRF One SDR platform.

4. GNURadio at works

41. A First FFT

Let’s build a simple GNURadio flowgraph using the HackRF as a source, plus an FFT Sink. To avoid DC spike in the middle of our signal, we tune the HackRF to 520KHz below the interesting frequency, and then shift back the signal using the Frequency Xlating FIR Filter block. A few GUI sliders are used to control the gain and to provide additional fine-tuning of the frequency.

gnuradio_grc_fft1.png

Great. Our supposed 2-FSK modulated signal is back. The center frequency is about 869.036 MHz.

gnuradio_grc_fft21.png.

4.2. Signal filtering and demodulation

It is time to start demodulating the signal but first, we need to remove any unwanted noise or adjacent communications.

4.2.1. Filtering

Using baudline, we have observed about 38.3 KHz between the MARK and SPACE frequencies, so a deviation of about 19 KHz.

lowpass_filter.png

In our GNURadio flowgraph, we then apply a Lowpass filter against our signal using a cutoff value of 21 KHz (so a bit more than 19 KHz) and a transition width of 15 KHz:

[TABLE]

[TR]

[TD=class: gutter]1[/TD]

[TD=class: code]firdes.low_pass(1,samp_rate, 21000, 15000)[/TD]

[/TR]

[/TABLE]

4.2.2. Demodulation

As we are facing a potential FSK modulated signal, a new Quadrature Demod block is added to the flowgraph, which will then send the demodulated signal into a WAV file for further analyze. The new flowgraph becomes:

gnuradio_grc_demod1.png

Haaaa, by opening our WAV file using Audacity (or any other WAV file editor), the demodulated signal seems to reveal its secrets: a preamble (0101010101…) and a potential synchronization pattern.

gnuradio_grc_demod_audiacity.png

Let’s go a bit further by slicing the signal into propers 0 and 1. The Binary Slicer block aims to convert all samples above 0 to 1, and sample below 0 to 0. The block is inserted between the demodulator block and the WAV sink.

gnuradio_grc_demod_slicer1.png

Back to Audacity, this beautiful binary sequence appears…

gnuradio_grc_demod_slicer_audiacity.png

4.3. Getting sync-word (Access Code)

Getting the synchronization word is now only a matter of observing the pattern just after the preamble. Audacity helps to do this by adding a Label layer on top of the WAV signal. The difficulty here is to find the right police character which match (more or less) the flow of our signal. We observe a double SyncWord equal to 0xD391.

audiacity_syncword2.png

4.4. Getting symbol rate and samples per symbol

So far so good. The next step is to discover the symbol rate and the samples per symbol needed to convert this signal into a binary sequence (done by the “Clock Recovery” block).

4.4.1. Symbol rate

What is “Symbol Rate” ? By symbol, we mean 0 or 1. Thus the question to answer is “How many 0 or 1 do we observe per second?”. I know that better techniques exist to compute this value (I just need to learn them) but right now, I will simply count the number of symbols from the Audacity view:

symbole_rate1.png

We count about 69 symbols during 0.00180 seconds, which give a symbol rate of 38333

[TABLE]

[TR]

[TD=class: gutter]1

2

3

4

5

6

7

8

9

10

11[/TD]

[TD=class: code]$ echo -n "010101010101010101010101011010011100100011101001110010001111100001111" | wc -c

69

$ bc

bc 1.06.95

Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.

This is free software with ABSOLUTELY NO WARRANTY.

For details type `warranty'.

69 / 0.00180

38333

quit[/TD]

[/TR]

[/TABLE]

As I said, this method is probably not the most accurate. Actually, I found a more precise value by performing the same exercise on a lager portion of the signal (but making screenshots less clear). The final symbol rate is 38450.

4.4.2. Samples per symbol

The number of samples per symbol is one of the most important value needed by the Clock Recovery block. Remember that your SDR device is configured to receive or transmit signal at a specified sample rate. The number of samples per symbol is just the number of samples needed to send one symbol (0 or 1), and is simply computed like this:

[TABLE]

[TR]

[TD=class: gutter]1[/TD]

[TD=class: code]samples_per_symbol = sample_rate / symbol_rate[/TD]

[/TR]

[/TABLE]

We now have everything we need to setup the Clock Recovery block:

samp_per_sym.png

clock_recovery.png

4.5. Decoding packets

The remaining steps to get digital messages are to recognize the frames using the syncword (done by the Correlate Access Code block) and to decode them (done by the Packet Decoder block). There are a couple of GNURadio blocks dedicated to decoding/encoding packets but since we are dealing with CC1110 devices, we had to create specific blocks able to respect the CC1110 packets format. Further information about these blocks can be found on the following blog post: GNU Radio – CC1111 packets encoder/decoder blocks.

The final GNURadio flowgraph is:

gnuradio_final_flowgraph.png

As you can see, the Packet Decoder (CC1111) block receives a python queue as argument (see Target Message Queue). This is where our decoded messages will be sent out.

This flowgraph will actually not be executed as it. Another python script will managed its execution and will pull messages from the python queue, dissect them (as Wireshark would do against a pcap file) and print them out on the screen.

Here below is the main script.

#!/usr/bin/env python
#=============================================================
# Securitas-Direct (Verisure) RF sniffer
# By Jerome Nokin (http://funoverip.net / @FUNoverip)
#=============================================================
#
# Usage: securitas_rx.py [-k KEY]
#
# optional arguments:
# -k,--key <KEY> Optional AES-128 Key (hexadecimal)
#
#=============================================================

import ctypes
import sys
import datetime
import argparse
from grc.verisure_demod import verisure_demod
from threading import Thread
from Crypto.Cipher import AES
from binascii import hexlify, unhexlify
from time import sleep

# Colors
def pink(t): return '\033[95m' + t + '\033[0m'
def blue(t): return '\033[94m' + t + '\033[0m'
def yellow(t): return '\033[93m' + t + '\033[0m'
def green(t): return '\033[92m' + t + '\033[0m'
def red(t): return '\033[91m' + t + '\033[0m'

# Thread dedicated to GNU Radio flowgraph
class flowgraph_thread(Thread):
def __init__(self, flowgraph):
Thread.__init__(self)
self.setDaemon(1)
self._flowgraph = flowgraph

def run(self):
self._flowgraph.Run()
#print "FFT Closed/Killed"

# AES decryption
BS = 16
pad = lambda s : s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]
def aes_decrypt(ciphertext, iv, key, padding=True):

cipher = AES.new(key, AES.MODE_CBC, iv)
plaintext = cipher.decrypt(ciphertext)
if padding:
return unpad(plaintext)
else:
return plaintext

# Generate timestamp
def get_time():
current_time = datetime.datetime.now().time()
return current_time.isoformat()

# Print out frames to stdout
def dump_frame(frame, aes_iv = None, aes_key = None):

# Dissecting frame
pkt_len = hexlify(frame[0:1])
unkn1 = hexlify(frame[1:2])
seqnr = hexlify(frame[2:3])
src_id = "".join(hexlify(n) for n in frame[3:7])
dst_id = "".join(hexlify(n) for n in frame[7:11])
data = ""

# Payload is a block of 16b and AES key provided ? Try to decrypt it
if (ord(unhexlify(pkt_len))-2-8) % 16 == 0 and aes_iv!=None and aes_key!=None:
if unkn1 == '\x04':
# block is 16b without additional padding
data = " ".join(hexlify(n) for n in aes_decrypt(frame[11:], aes_iv, aes_key, False))
else:
# block is 16b with padding
data = " ".join(hexlify(n) for n in aes_decrypt(frame[11:], aes_iv, aes_key))
if len(data) ==0:
data = "<empty> Wrong EAS key ?"
else:
data = " ".join(hexlify(n) for n in frame[11:])

# Print out the frame
print "[%s] %s %s %s %s %s %s" % (get_time(), yellow(pkt_len), blue(unkn1), seqnr, green(src_id), red(dst_id), pink(data))

# Main entry point
if __name__ == '__main__':

aes_iv = unhexlify("00000000000000000000000000000000")
aes_key = None

if sys.platform.startswith('linux'):
try:
x11 = ctypes.cdll.LoadLibrary('libX11.so')
x11.XInitThreads()
except:
print "Warning: failed to XInitThreads()"

# Read args
parser = argparse.ArgumentParser()
parser.add_argument("-k", "--key", help="Optional AES-128 Key (hex)", type=str)
args = parser.parse_args()

# Initializing GNU Radio flowgraph
flowgraph = verisure_demod()

if args.key:
print "[%s] AES key provided. Decryption enabled" % get_time()
aes_key = args.key
aes_key = ''.join(aes_key.split())
aes_key = unhexlify(aes_key)
print "[%s] AES-128 IV : %s" % (get_time(), hexlify(aes_iv))
print "[%s] AES-128 key: %s" % (get_time(), hexlify(aes_key))

# current frequency
freq = 0

# Some additional output
print "[%s] Starting flowgraph" % get_time()

# Start flowgraph insie a new thread
flowgraph_t = flowgraph_thread(flowgraph)
flowgraph_t.start()

# Until flowgraph thread is running (and we hope 'producing')
while flowgraph_t.isAlive():
# Did we change frequency ?
if freq != flowgraph.get_frequency():
print "[%s] Frequency tuned to: %0.2f KHz" % (get_time(), flowgraph.get_frequency()/1000)
freq = flowgraph.get_frequency()

# Emptying message queue
while True:
if flowgraph.myqueue.count() <= 0:
break;
frame = flowgraph.myqueue.delete_head_nowait().to_string()
dump_frame(frame, aes_iv, aes_key)

# I can't exit the script because of a blocking call to "myqueue.delete_head()". So for now..
sleep(0.1)

print "[%s] Exiting" % (get_time())

# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

Here is the final output:

securitas_rx_gui.png

A few comments about the output. Briefly:

  • In yellow: the length of the packet
  • In blue: still unclear but it appears to be related to payload encryption (encrypted or not, has padding or not, …)
  • In grey (and gray): a kind of sequence number
  • In green: the source ID of the packet
  • In red: the destination ID of the packet
  • In pink: the payload

5. Final note

The reader has probably observed from the output of the script that an AES-128 key is provided and that payloads are decrypted. The way we recovered the key will be discussed later on. Don’t try this key at home since keys are randomly generated by the VBox.

GNURadio flowgraph and python script can be downloaded from https://github.com/funoverip/verisure-alarm.

Prerequisite:

  • I’ve used HackRF One as SDR platform but any other SDR device should make the trick.
  • You will need GNURadio 3.7 or above
  • Do not forget to also install the GNURadio gr-cc1111 blocks !

Last but not least, a big thank you to Michael Ossmann for his awesome SDR class. Strongly recommended!

Hope you enjoyed this post…

Regards.

Sursa: Reverse Engineer a Verisure Wireless Alarm part 1 – Radio Communications | Fun Over IP

Edited by Nytro
Posted

In my humble opinion, copy/pasting (stealing?) full articles from bloggers is not very fair-play. The correct way to share information while crediting authors is to introduce the post and then to provide the link to the full article, which in this case is:

Reverse Engineer a Verisure Wireless Alarm part 1 – Radio Communications | Fun Over IP

Writing quality (or not) articles take a lot of time, and the work behind this one too. Hope you understand my point.

Regards.

funoverip (The author)

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...