Week Review 9/29/2023

Continued Work on Battery Holder

While I was waiting for the radios to come in I made the cylindrical battery holder in CAD. Later in the week Ben Wirz cleaned up the model.

The limit for voltage on non-ROV devices in the MATE ROV competition in 12 volts, so we are putting 8 AA batteries in it (8*1.5=12).

battery holder model

Testing Radios

The replacement RFM69HCW radios came in from Adafruit. We bought the 433MHz Spring Antenna with them.

The range was much better with the new radios. This makes me think that the old radios were broken by whoever used them before me.

Image of wired breadboard

Writing Arduino Code

I started by taking a closer look at the example code provided by the Radiohead library. When looking at the part of the code that receives data I saw something suspicious.

  uint8_t buf[RH_RF69_MAX_MESSAGE_LEN];
  Serial.println(RH_RF69_MAX_MESSAGE_LEN);
  uint8_t len = sizeof(buf);

  if (rf69.waitAvailableTimeout(500)) {
    // Should be a reply message for us now
    if (rf69.recv(buf, &len)) {
      Serial.println(len);
      Serial.print("Got a reply: ");
      Serial.println((char*)buf);

It is making a buffer with a fixed size and passing a pointer of it into a function that recieves data. Seeing that it also passes a length variable is was a good sign but I thought it would be a good idea to make sure that the rf69.recv() function protected from buffer overflows.

After looking at the function I saw that it did not. Although it checks if the len variable was correct, it does not stop the memcpy() function from running if the buffer it is getting from SPI is larger than the buffer allocated in memory for it. This makes it possible for an attacker to write arbitrary data into the buffer. This is a condition called a buffer overflow.

bool RH_RF69::recv (uint8_t* buf, uint8_t* len)
{
	if (!available())
		return false;

	if (buf && len)
	{
		ATOMIC_BLOCK_START;
		if (*len > _bufLen)
			*len = _bufLen;
		memcpy (buf, _buf, *len);
		ATOMIC_BLOCK_END;
	}
	_rxBufValid = false; // Got the most recent message
//    printBuffer("recv:", buf, *len);
	return true;
}

What is a Buffer Overflow

A buffer overflow is a condition where the size of user entered data is not checked before being written to a buffer. When data that is larger than a buffer is being written to it, after the buffer is full the data will be written into whatever memory comes next.

This can be exploited by putting malicious commands into memory. A thorough explanation of how to do this is outside the scope of this blog, but LiveOverflow made a very good youtube video that explains buffer overflow attacks with an example.

Fixing the Vulnerability

Fixing this vulnerability is fairly simple. We just need to code to check if _buf is bigger than buf before writing to it. I edited the rf69.recv() function to make it more secure. The updated version of the function is below.

bool RH_RF69::recv (uint8_t* buf, uint8_t* len)
{
	if (!available())
		return false;

	if (buf && len)
	{
		ATOMIC_BLOCK_START;
		if (*len > _bufLen)
		{
			*len = _bufLen;
			if (buf >= _buf){
				memcpy (buf, _buf, *len);
			}
		}
		ATOMIC_BLOCK_END;
	}
	_rxBufValid = false; // Got the most recent message
//    printBuffer("recv:", buf, *len);
	return true;
}

I submitted a pull request, but the maintainer has not accepted it yet. The project seems like it might be abandoned so I don’t know if that change will ever made it downstream. If you are using the RF69HCW it might be a good idea to use my fork of the project.

To be clear I did not fix all of the vulnerabilities in this library; I only patched the rf69.recv() function specifically. Looking at other radios included in this library, the same vulnerability is present. If anyone wants to try their hand at finding binary exploitation vulnerabilities in the wild, I would recommend looking at this library, and more generally Arduino libraries.

“The S in IoT stands for security” -Tin Kadlec

Setting Up A Connection Sequence

We need a predetermined sequence for connecting the float to the topside. In packet radio you have RX and TX. TX is the initial transmitter and TX will send responses. I plan on having the float be TX because it will make the topside silent until there is a connection.

I am going to have the float send out pings until it connects to the station above the water. Once it connects it will start uploading the depth data. When it is done uploading the depth data it will do another profile.