Nytro Posted November 16, 2014 Report Posted November 16, 2014 (edited) Reverse Engineer a Verisure Wireless Alarm part 1 – Radio Communicationsby 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. 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. 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]123456789101112131415161718192021222324[/TD][TD=class: code]$ hackrf_transfer -s 2000000 -f 869000000 -a 0 -r /tmp/hackrf_verisure_s2000000_f869000000_01.iqcall 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-C3.9 MiB / 1.000 sec = 3.9 MiB/second3.9 MiB / 1.000 sec = 3.9 MiB/second3.9 MiB / 1.000 sec = 3.9 MiB/second4.2 MiB / 1.000 sec = 4.2 MiB/second3.9 MiB / 1.000 sec = 3.9 MiB/second3.9 MiB / 1.000 sec = 3.9 MiB/second3.9 MiB / 1.000 sec = 3.9 MiB/second4.2 MiB / 1.000 sec = 4.2 MiB/second^CCaught signal 22.1 MiB / 0.540 sec = 3.9 MiB/secondUser cancel, exiting...Total time: 8.54128 shackrf_stop_rx() donehackrf_close() donehackrf_exit() donefclose(fd) doneexit[/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. 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. 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. Great. Our supposed 2-FSK modulated signal is back. The center frequency is about 869.036 MHz. . 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. 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: 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. 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. Back to Audacity, this beautiful binary sequence appears… 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. 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: We count about 69 symbols during 0.00180 seconds, which give a symbol rate of 38333 [TABLE][TR][TD=class: gutter]1234567891011[/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: 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: 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 ctypesimport sysimport datetimeimport argparsefrom grc.verisure_demod import verisure_demodfrom threading import Threadfrom Crypto.Cipher import AESfrom binascii import hexlify, unhexlifyfrom time import sleep# Colorsdef 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 flowgraphclass 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 decryptionBS = 16pad = 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 timestampdef get_time(): current_time = datetime.datetime.now().time() return current_time.isoformat()# Print out frames to stdoutdef 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 pointif __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: 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 November 16, 2014 by Nytro Quote
funoverip Posted November 17, 2014 Report Posted November 17, 2014 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 IPWriting 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) Quote