Author Topic: Getting random gibberish data opens my garage door  (Read 207 times)

luisr320

  • Full Member
  • ***
  • Posts: 240
  • Country: pt
Getting random gibberish data opens my garage door
« on: October 16, 2020, 05:52:50 AM »
Hi. I have a Moteino connected to my garage door which has been working flawlessly for several years now. Last night I suddenly got a notification that the garage door was opening. I closed but it kept on opening again after a while. I got a Moteino with a promiscuous mode set to TRUE to sniff any traffic on the network and picked up some gibberish once in a while, which is the cause of the problem.

I'm posting a screenshot of what I'm talking about picked up on Putty. In the image, the node ID is 179 but I don't have a node with that ID. All of the data is comming from the boiler, ID 3.

Any ideas of what I'm dealing with?

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6357
  • Country: us
    • LowPowerLab
Re: Getting random gibberish data opens my garage door
« Reply #1 on: October 16, 2020, 09:19:25 AM »
What sketch do you use on your Garage opener?

luisr320

  • Full Member
  • ***
  • Posts: 240
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #2 on: October 16, 2020, 09:45:34 AM »
Hello Felix. Thank you for your reply.
Here is the code:

Code: [Select]
// **********************************************************************************************************
// Garage Door Node
// Ver. 9.0 - 2020-09-14
// **********************************************************************************************************

#include <RFM69.h>
#include <RFM69_OTA.h>
#include <RFM69_ATC.h>
#include <SPIFlash.h>

#define THISNODEID 4//unique for each node on same network
#define GATEWAYID 1//Gateway number on this network
#define BLYNKGATEWAY 10 //Blynk Gateway
#define NETWORKID 100//the same on all nodes that talk to each other
#define FREQUENCY RF69_433MHZ
#define ENCRYPTKEY "XXXXXXXXXXXXXXXX"
#define ATC_RSSI -80
#define ACK_TIME 3000 //max # of ms to wait for an ack
#define LED 9 // Moteinos have LEDs on D9
#define RELAYPIN1 6 // Output pin connected to the relay
#define RELAYPIN2 7 // Output pin connected to the relay
#define POSITIONPOT A1 //Input pin connected to the potentiometer
#define POSITIONPOT_EN 5//Digital output to enable the potentiometer
#define RELAY_PULSE_MS 500 //just enough that the opener will pick it up
#define SERIAL_BAUD  115200
#define PROMISCUOUSMODE false //set to 'true' to sniff all packets on the same network


// Outbound Data Struct
typedef struct
{
char out_nodeType[6]; // Node Type
uint32_t out_data1; // Data 1 << Device Output - 1 = DOOR POSITION SENSOR
uint32_t out_data2; // Data 2 << Door position in %
float out_data3; // Data 3 << NIL

} Payload;
Payload outData;

//Inbound Data Struct
typedef struct
{
uint8_t in_data0; // Node Type
uint32_t in_data1;  // Data 1 >> Device Input - 1 = OPEN/STOP/CLOSE RELAY
uint32_t in_data2; // Data 2 >> INPUT A - 1 = OPEN/STOP/CLOSE
float in_data3; // Data 3 >> INPUT B - NIL

} in_Payload;
in_Payload inData;

//Initialize some variables
unsigned long timeNow = 0;
word blinkPeriod = 15000; //transmit a ping to gateway so often (in ms)
int16_t doorPosition = 0;
int16_t olddoorPosition = 0;

// Hardware Init
SPIFlash flash(8, 0xEF30); //EF30 for windbond 4mbit flash
RFM69_ATC radio;

void setup()
{
//Setup the pins
pinMode(LED, OUTPUT);
pinMode(RELAYPIN1, OUTPUT);
pinMode(RELAYPIN2, OUTPUT);
pinMode(POSITIONPOT, INPUT);
pinMode(POSITIONPOT_EN, OUTPUT);

//Turn on the position trim pot
digitalWrite(POSITIONPOT_EN, HIGH);

//Start the Serial port
Serial.begin(SERIAL_BAUD);

//Initialize the radio
radio.initialize(FREQUENCY, THISNODEID, NETWORKID);
radio.setHighPower();
radio.encrypt(ENCRYPTKEY);//Turn encryption ON
radio.promiscuous(PROMISCUOUSMODE);
radio.enableAutoPower(ATC_RSSI);
}

void loop()
{
if (radio.receiveDone())//If some packet was received by the radio, wait for all its contents to come trough
{
CheckForWirelessHEX(radio, flash, true);// Check if it is a OTA upload

//If an ACK (acknowledge) was requested by the data packet transmitter radio, send the ACK back to it
if (radio.ACKRequested())
{
radio.sendACK();
}

//Extract data from the payload
inData = *(in_Payload*)radio.DATA; //assume radio.DATA actually contains our struct and not something else
   
//Now check what was the instruction sent by the Gateway to this node
if (inData.in_data1 == 1) // If it was a "1", a command was sent from the Gateway to the Output 1 - Garage Door Relay
{
if (inData.in_data2 == 1) // If it was a "1", a command was sent from the Gateway to toggle the relay
{
controlDoor();
}
inData.in_data1 = 0; //Reset the in_data2 Variable content
}
}
sendDoorPos(); // Get the door position from the potenciometer and sent it to the Gateway
}

void controlDoor()
{
digitalWrite(RELAYPIN1, HIGH);//Using two pins to doulbe the current
digitalWrite(RELAYPIN2, HIGH);
delay(RELAY_PULSE_MS);
digitalWrite(RELAYPIN1, LOW);//Using two pins to doulbe the current
digitalWrite(RELAYPIN2, LOW);
Blink(25);//Blink the node led to show a the command was successfully received
}

void sendDoorPos()
{
doorPosition = analogRead(POSITIONPOT); //Read Potenciometer to determine door position
doorPosition = map(doorPosition, 5, 920, 0, 100);
doorPosition = constrain(doorPosition, 0, 100);

//Check if door position has changed and if it did, send new position to the Gateway.
if (millis() / 1000 - timeNow / 1000 > 1)
{
timeNow = millis();
if (doorPosition != olddoorPosition)
{
olddoorPosition = doorPosition;
//fill in the struct with new door position
strcpy(outData.out_nodeType, "GGATE");
outData.out_data1 = 1; // Device output - Potenciometer
outData.out_data2 = doorPosition;
outData.out_data3 = analogRead(POSITIONPOT);
sendData(GATEWAYID);
sendData(BLYNKGATEWAY);
}
}
}

void sendData(byte destinationNode)
{
radio.sendWithRetry(destinationNode, (const void*)(&outData), sizeof(outData), 3, ACK_TIME); //Send new position to the Moteino Gateway
}

void Blink(byte DELAY_MS)//Local led blinking function
{
digitalWrite(LED, HIGH);
delay(DELAY_MS);
digitalWrite(LED, LOW);
}

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6357
  • Country: us
    • LowPowerLab
Re: Getting random gibberish data opens my garage door
« Reply #3 on: October 16, 2020, 10:34:50 AM »
This is very different than my GarageMote example. You're using a struct, so Im guessing that's where things go wrong - a stray packet from some other source gets through, gets decrypted as binary data, which then gets picked up into your struct without any checks of whether the data is valid.

You need some more checks in your code, do not just blindly execute stuff that is decoded from an unknown packet into a struct.

Code: [Select]
if (inData.in_data1 == 1)

The above code is NOT a good check  :-\

luisr320

  • Full Member
  • ***
  • Posts: 240
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #4 on: October 16, 2020, 11:16:20 AM »
At a point it opened both my small gate and the garage door at the same time. But not the outer gate.
Here is the code of the small gate>

Code: [Select]
// **********************************************************************************************************
// Outer Gate Node
// Ver. 4.0
// 2020-09-16
// **********************************************************************************************************

#include <RFM69.h>
#include <RFM69_ATC.h>
#include <RFM69_OTA.h>
#include <SPIFlash.h>

#define NODEID 5 //unique for each node on same network
#define GATEWAYID 1 //Gateway number on this network
#define NETWORKID 100 //the same on all nodes that talk to each other
#define FREQUENCY RF69_433MHZ
#define ENCRYPTKEY "XXXXXXXXXXXXXXXX" //exactly the same 16 characters/bytes on all nodes!
#define ATC_RSSI -80
#define ACK_TIME 3000 // max # of ms to wait for an ack
#define LED 9 // Moteinos have LEDs on D9
#define RELAY1 5 // Output pin connected to the relay
#define RELAY2 7 // Output pin connected to the relay
#define SERIAL_BAUD 115200
#define PROMISCUOUSMODE false //set to 'true' to sniff all packets on the same network

//Initialize some variables
unsigned long timeNow = 0;

// Outbound Data Struct
typedef struct
{
char out_nodeType[6]; // Node Type
uint32_t out_data1; // Data 1 << Device Output - 1 = DOOR POSITION SENSOR
uint32_t out_data2; // Data 2 << Door position in %
float out_data3; // Data 3 << NIL

} Payload;
Payload outData;

//Inbound Data Struct
typedef struct
{
uint8_t in_data0; // Node Id
uint32_t in_data1;  // Data 1 >> Device Input - RELAY 1 and 2
uint32_t in_data2; // Data 2 >> INPUT A - 1 = OPEN; 0 = CLOSE
float in_data3; // Data 3 >> INPUT B - NIL

} in_Payload;
in_Payload inData;

// Hardware Init
SPIFlash flash(8, 0xEF30); //EF40 for 16mbit windbond chip
RFM69_ATC radio; // Call the radio

void setup()
{
//Setup the pins
pinMode(LED, OUTPUT);
pinMode(RELAY1, OUTPUT);
pinMode(RELAY2, OUTPUT);

//Start the Serial port
Serial.begin(SERIAL_BAUD);

//Initialize the radio
radio.initialize(FREQUENCY, NODEID, NETWORKID);
radio.setHighPower();
radio.encrypt(ENCRYPTKEY);//Turn encryption ON
radio.promiscuous(PROMISCUOUSMODE);
radio.enableAutoPower(ATC_RSSI);
}

void loop()
{
if (radio.receiveDone())//If some packet was received by the radio, wait for all its contents to come trough
{
CheckForWirelessHEX(radio, flash, true); // set to true for debug. This will check if there is a new wireless program to load

//Extract data from the incoming payload
inData = *(in_Payload*)radio.DATA;

//If an ACK (acknowledge) was requested by the data packet transmitter radio, send the ACK back to it
if (radio.ACKRequested())
{
radio.sendACK();
}

if (inData.in_data1 == 1) //If data is for Relays
{
if (inData.in_data2 == 1)// If a command was sent from the Gateway to toggle RELAY1, a "1" was received
{
digitalWrite(RELAY1, HIGH);
delay(500);
digitalWrite(RELAY1, LOW);
}
else if (inData.in_data2 == 2) // If a command was sent from the Gateway to toggle RELAY2, a "2" was received
{
digitalWrite(RELAY2, HIGH);
delay(500);
digitalWrite(RELAY2, LOW);
}
}
inData.in_data1 = 0; //Reset the in_data Variable content
inData.in_data2 = 0; //Reset the in_data Variable content
Blink(25);//Blink the transmitter led to show the data was successfully received
}
}

void Blink(byte DELAY_MS)//Local led blinking function
{
digitalWrite(LED, HIGH);
delay(DELAY_MS);
digitalWrite(LED, LOW);
}
« Last Edit: October 16, 2020, 12:55:07 PM by luisr320 »

luisr320

  • Full Member
  • ***
  • Posts: 240
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #5 on: October 16, 2020, 11:19:20 AM »
Ahhhh!!! I see. But if I use PROMICUOUS MODE is FALSE, shouldn't the node only receive traffic sent exclusively for it?
« Last Edit: October 16, 2020, 12:54:48 PM by luisr320 »

luisr320

  • Full Member
  • ***
  • Posts: 240
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #6 on: October 16, 2020, 12:13:15 PM »
So I guess I should have something like:

Code: [Select]
if (radio.SENDERID = GATEWAYID)
{
if (inData.in_data1 == 1) // If it was a "1", a command was sent from the Gateway to the Output 1 - Garage Door Relay
{
if (inData.in_data2 == 1) // If it was a "1", a command was sent from the Gateway to toggle the relay
{
controlDoor();
}
inData.in_data1 = 0; //Reset the in_data2 Variable content
}
}

Still I can't understand how the garbled data can be passed if spyMode is set to false
« Last Edit: October 18, 2020, 05:09:03 PM by luisr320 »

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6357
  • Country: us
    • LowPowerLab
Re: Getting random gibberish data opens my garage door
« Reply #7 on: October 16, 2020, 12:48:42 PM »
Read my response again for the explanation. PROMISCUOUS doesn't help you in any way to filter bad data.
Instead of checking 1 bit to trigger a OPEN or CLOSE, you need to check something like:

Code: [Select]
if (struct.magicToken == "Averyspecialstringherethattellsmethisisreallyforthegarageandnotsomethingelselikefakedata")
{ all good, open my garage! }

That is pseudocode and grossly exaggerated, but I think you get the idea of what's needed before you release the heaviest moving object in your home without your presence  ;)

luisr320

  • Full Member
  • ***
  • Posts: 240
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #8 on: October 16, 2020, 12:52:33 PM »
AHHHHHHHHHH! OK!!!!! I get it. Thank you.  ;)

luisr320

  • Full Member
  • ***
  • Posts: 240
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #9 on: October 18, 2020, 04:43:16 PM »
Problem solved. Thank you Felix.
I introduced a "security token" on the message transmitted to all the gates that must match the token of each node attached to it before checking what is the command sent to it.

I was putting a lot of faith on the Moteino capability to only receive data sent exclusively to it with the radio.reveiveDone() function. As far as security was concerned, I was assuming that the message content was encrypted, and the radio only passed what it decrypted to the board's processor if the destination node number matched its own node ID number.

I guess that is what happens but eventually some garbled data may satisfy the node ID and pass an incorrect command to be processed, as it happened with my garage door and entrance gate.
By inserting a specific combination of characters as a security token it decreases the probability of a random match.

Regarding the encryption, is all the content of the transmitted message encrypted?

luisr320

  • Full Member
  • ***
  • Posts: 240
  • Country: pt
Re: Getting random gibberish data opens my garage door
« Reply #10 on: October 18, 2020, 05:31:26 PM »
I have been reading your approach to the rolling code problem on your hackaday entry for the Moteino Framework and found it very interesting (https://hackaday.io/project/2197/logs?sort=newest&page=3):
Quote
I found this article that explains very elegantly how this all works and how the author was able to attack an auto lock using this method, via an inexpensive RTL-SDR device. Moreover, such devices as garage openers and perhaps key fob home door locks work this way. Scary.
To fix this we need to implement something that will require the receiver to be involved in each exchange. In SSL the receiver gives the sender a public key that can only be used to encrypt the message. Then the receiver uses a private key to decrypt that message. So in our case the messages are already AES128 encrypted so the messages contents are not readable to the attacker. We could then ask the receiver for a transactional token, a one time unique set of pseudorandom bytes generated by the receiver that will be used to "salt" the sender message. This technique will ensure the sender will send a different looking message every time *and also* that the receiver knows exactly what the unique expected token is. The attacker cannot spoof the token since they don't know the secret shared AES128 encryption key. One essential assumption is that the sender message will contain the security token in addition to the actual message. The encryption will ensure that 2 messages with the same content but with different security tokens will look completely different as far as modulated RF output.

Since both receiver and sender are involved in each exchange, the receiver should expect the sender to respond very quickly with the encrypted and salted message. A short message takes a few ms to send at 55.5kbps for instance which is the default at the time of writing for the RFM69 library. Now even if an attacker records and replays messages, they are obsolete by the time they are received.
The article referred to is this one: http://spencerwhyte.blogspot.com/2014/03/delay-attack-jam-intercept-and-replay.html.

Your idea, as I understand it, is for the transmitter to send the receiver a request for a random security token and then send the final command with that token in it to the receiver which it will use for a one time only. So even if someone manages to pick up and record the transaction, it will be too late to use it, since the security token should already expire.

I have a question, though: when should the random security token expire? Only after it is used, or after some time expired?

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6357
  • Country: us
    • LowPowerLab
Re: Getting random gibberish data opens my garage door
« Reply #11 on: October 19, 2020, 11:17:55 AM »
Encryption is NOT the same as authentication (or IOW one does not include the other). They are very distinct. So for most security you must have both. You only had encryption.

Rolling code would ensure that the same authentication, even if encrypted, could not be repeated by an attacker. A really sophisticated attacker would be required for this. I would use an expired token, IMO that is sufficient.