Skip to content

Reverse Engineer a Verisure Wireless Alarm part 1 – Radio Communications

by foip on November 16th, 2014

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.

Verisure

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

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

$ 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

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

baudline

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

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_fft

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

gnuradio_grc_fft2.

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

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:

firdes.low_pass(1,samp_rate, 21000, 15000)

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_demod

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

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_slicer

Back to Audacity, this beautiful binary sequence appears…

gnuradio_grc_demod_slicer_audiacity

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_syncword

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_rate

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

$ 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

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:

samples_per_symbol = sample_rate / symbol_rate

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

samp_per_sym

clock_recovery

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

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 (https://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	= hexlify(frame[3:7])
    dst_id	= hexlify(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

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 at the following address. 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.

1 Star2 Stars3 Stars4 Stars5 Stars (55 votes, average: 4.82 out of 5)
Loading...

© 2014 – 2022, foip. All rights reserved.

One Comment
  1. Very interesting… I’ve a question not about the protocol, but more about the communication… A vendor tell me that is a “ping back” on two separate canal to avoid wireless network jamming and limit possible sabotage…

    did you seen something like that (ping back or second (backup) canal ?)

    regards,
    Thierry

Comments are closed.

© 2010-2024 Fun Over IP All Rights Reserved -- Copyright notice by Blog Copyright