A total remote-control and remote-discovery vulnerability in IoT smart thermostats

2025/03/14

tl;dr

Until this past week, an attacker could have easily and surreptitiously discovered, monitored, and controlled all of the Mysa smart thermostats in the world.

Mysa thermostat owners use the Android or iPhone apps to control their thermostats. Even if the owner is at home and connected to the same WiFi network as the thermostats, the communication between the app and the thermostat always passes through a single set of US-based servers in the AWS cloud.

It turns out that the servers did not enforce any restrictions on who could inspect and control any given Mysa device.

About Mysa

Mysa is a company that makes Internet-connected smart thermostats for different kinds of home heating and cooling systems. They were founded in St. John’s, Newfoundland with a focus on energy conservation.

Their thermostats have a nice clean physical design, and their mobile app is helpful and straightforward, with energy consumption measurement and charting being a particularly attractive feature.

I’d estimate that Mysa has sold a few hundred thousand Internet-connected thermostats since 2021.1

About me

About a year ago, my wife and I moved to a nice townhouse here in Vancouver 🇨🇦, built in the ’70s. Now we have a 7-month-old son. The house is well-built, but the windows are old and the heat is from resistive baseboard and in-floor heaters. (This is common in Vancouver!) I added some weatherstripping to the windows, and I’ve also learned to do minor electrical repairs like changing light fixtures and replacing switches and outlet receptacles.

The electric bill quickly got expensive as the temperatures dropped in the fall. A friend who’s interested in housing and environmentalism, an all-around smart and curious person, showed me some nice smart thermostats that he’d gotten at a really good price thanks to a cross-promotion and rebates from our electric utility company.

So I ordered one Mysa BB V2 Lite, thinking we’d try it out as a way to adjust the heat in our baby’s room without waking him. I was unlucky, and got one with an uncommon defect (a stuck-open relay), and spent a few hours frustratedly trying to figure out why it didn’t work, but I learned a whole lot about how North American 208V ⏦ AC circuits work in the process, so it wasn’t a waste of time. When I contacted Mysa support, they were very helpful and quickly replaced it. The working Mysa was great, and I found two more Mysa V1 thermostats on Facebook Marketplace and got those too.

I decided to figure out how the apps and the devices communicate, and whether I could make them do anything else that was cool or interesting. I could give you a few reasons why,2 but figuring out how obscure or undocumented protocols work and reimplementing them in open source software is kind of my thing.

Coding things up

I found a Github project from 2020, which had figured out that the Mysa apps use AWS Cognito to authenticate users, then send and receive information about the thermostats via an HTTPS-and-JSON API… very standard stuff. They also figured out that there is a second communication channel used for “real-time” updates (e.g. heat on/off, temperature down/up) based on the MQTT publish/subscribe protocol, but they hadn’t figured out how to authenticate to it and it appeared that the project was abandoned.

The auth part still worked, but I quickly discovered that the API endpoints from 2020 no longer worked. So I fired up dlenski/create_ap3 and mitmproxy in transparent mode, and connected my phone to the MITM’ed access point.

I quickly figured out that the HTTPS-and-JSON API from 2020 had been replaced with a more structured one, but soon ran into the same issue that the 2020 project had encountered: I couldn’t figure out how to authenticate the MQTT-over-WebSockets protocol in order to access the “real-time” messages to and from the thermostats. It appeared that Mysa was using the query-string variant of AWS’s SigV4 “URL-presigning” algorithm4 for authentication. Delving5 into the mitmproxy captures, I could see valid SigV4 signatures that the Mysa app was generating using my Mysa credentials, but I couldn’t generate new ones that Mysa’s server would accept.

I realized that the only part of the to-be-signed plaintext that varies from one SigV4 invocation to the next (at least in the URLs that Mysa is signing for MQTT-over-WebSsockets) is timestamp like 20250102T030405Z. If I could “freeze time,” the signature wouldn’t change either. That meant I could monkey-patch the timestamping function used in an AWS client library to fix a moment in time matching one of the known-valid signatures from the Mysa app, and experiment with different tweaks to the SigV4 algorithm until I was able to generate a matching result.

Next I noticed that the order of the URL parameters in Mysa’s signed URLs didn’t quite match the order shown in one of the examples in the AWS docs:

?X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=${AWS_ACCESS_KEY_ID}%2F${YYYYMMDD}%2Fus-east-1%2Fiotdevicegateway%2Faws4_request
&X-Amz-Date=${YYYYMMDD}T${HHmmSS}Z
&X-Amz-SignedHeaders=host
&X-Amz-Signature=${SIGNATURE}                    <-- based on the examples, this should be the last parameter
&X-Amz-Security-Token=${AWS_SESSION_TOKEN}       <-- and this should be second-to-last

I realized that Mysa was doing the SigV4 signing in a nonstandard way, where the “security token” piece of the AWS credential gets plucked out before the normal SigV4 signing process and glommed back in afterwards.

Et voilà. I could now connect to Mysa’s MQTT server, and subscribe and publish to the topics which I saw Mysa’s apps using: /v1/dev/DEVICE_ID/out and /v1/dev/DEVICE_ID/in. I pretty quickly figured out the format and meaning of all of the interesting messages sent between the app and the devices.

Making something useful (to me!) with this

The Mysa V2 Lite thermostat lacks some of the features of the more expensive Mysa V2. Some of these are obviously due to hardware differences; for example, the V2 has a visible proximity/light sensor on it, while the Lite doesn’t, and that explains why the Lite device doesn’t have “wake on approach” and “auto brightness.”

Other features seemed like they must be software-limited, though. Specifically these two, which the Mysa apps allow me to access for my older Mysa V1 devices but not for my Mysa V2 Lite devices:

I wondered if it would be possible to convince the app that my Mysa V2 Lite devices were actually fancier devices, and unlock these features.

And… it was. Quite easy, in fact. I wrote a tool called liten-up:

This tool makes your Mysa Lite thermostat (model BB-V2-0-L) look like a Mysa Baseboard V1 thermostat (model BB-V1-1) to the official Mysa apps. This enables zone control, the usage graph, and the humidity sensor in the app.

The Mysa Lite doesn’t have a current sensor, and it doesn’t report any estimated energy usage to the servers. TODO: Figure out how to report this to the server in a form it will accept, by estimating from the relay on/off signals.

When it’s active, it convinces the Mysa server and app that your Mysa Lite thermostats are actually Mysa V1 devices, and then adds one small translation to the communications between them:

screenshot

This trick enables the thermostat grouping/zones, in-app charting of temperature, and — quite surprisingly — the unadvertised built-in humidity sensor.6 Kind of cool, right? 🥳

The only problem was that I still wasn’t able to get a graph of energy usage out of the Mysa Lite devices. I surmised that this was partly due to the Mysa Lite’s simpler hardware7, but I was still very curious about exactly how the fancier Mysa devices communicated their minute-by-minute power consumption to the server. I hadn’t seen it anywhere in the traffic I had analyzed…

The 😱😨‼️🚨 moment

I wondered if there might be some additional MQTT topics that were used by the devices to communicate their precise readings to an energy-charting server, and which I couldn’t see while MITM’ing the app because the app didn’t need to subscribe to them. Perhaps /v1/dev/DEVICE_ID/sensors or /readings or the like.

I puttered around, and then learned that MQTT offers wildcard topics: both a “single-level” wildcard (e.g. /v1/dev/DEVICE_ID/+) and a “match-everything” wildcard.

Out of sheer laziness, I connected to the Mysa MQTT server and subscribed to the match-everything wildcard topic, #. I was hoping I’d see messages from a few more MQTT topics, giving me more information about my Mysa devices.

Instead,8 I started receiving a torrent of messages from every single Internet-connected production Mysa device in the whole world. Due to the lazy way my script was written, it would immediately query device identifying information from the web API alongside each MQTT message, so I could see that I was getting messages about “Kids’ bedroom” in the America/Dallas timezone, “Salle à manger” in America/Montreal, etc.

Potential impact

I blinked and stared, hit Ctrl-C, ran it again… and knew that this was a serious vulnerability.

An attacker could gain enormous amounts of information about all the Mysa thermostats in the world, including their recorded air temperatures and setpoints, temperature units (°F/°C), timezone, ongoing log of control actions, grouping of thermostats by homes and users, and human-assigned names9. One could use this information to make many inferences about geographic location, size of home, typical daily schedule of occupants, and periods of prolonged absence.

This would enable individually targeted attacks:

It would also enable larger-scale attacks:

An attacker could suddenly turn on (or off!) thousands of Mysa thermostats all at once in a geographically concentrated region, let’s call it “Metro Vancouver”, potentially impacting the reliability of power generation and distribution.10

Disclosure and response

I realized that my GithHub repo would give a huge head start to anyone looking for a vulnerability in Mysa, so I made it private until just now.

I spent some time thinking things over.

Then I wrote a ~120 line program in Python to demonstrate the vulnerabilities I had discovered, and a ~700 word email, and slept on it. At about 8:45 am, I sent the email to Mysa’s security contact (easy to find 👍), explaining the vulnerabilities and attaching my demo. I told Mysa that I would publicize my findings in one week if I didn’t receive a response.

I got a response just about one hour later, indicating that folks at Mysa had read my report thoroughly, understood its possible impacts, had begun investigations to reproduce it, and would give me a fuller response the next day.

About 28 hours after that, Mysa explained to me that they had reproduced the vulnerabilities and had taken specific steps to address them. I was able to confirm that my demo no longer worked, as well as verifying a couple subtler effects adjacent to the vulnerability. (Checking for brown M&Ms, maybe?)

Some thoughts

I have long been skeptical about the privacy and security of IoT devices. These Mysa devices are the first IoT devices I’ve ever owned,2 and they turned out to have major vulnerabilities that I discovered completely by accident, and even though the company appears to be extremely competent at addressing problems in my recent experience.

I’m not sure how I should update my Bayesian priors regarding the incidence of serious vulnerabilities in IoT devices. 🤔🙈

On the other hand, I think this experience actually makes me more likely to recommend Mysa devices specifically. I was very impressed by how quickly, conscientiously, and graciously Mysa responded to this disclosure. It was by far the best and most reassuring experiences I’ve had in disclosing a technological vulnerability.11 This was a bad bug, but all bugs are obvious in retrospect, and it matters a great deal how developers respond to them, and what they learn from them.


[Discussion on HN], [on LinkedIn]


  1. There’s a nice writeup about the company by the National Research Council of Canada). The article mentions that Mysa has had revenue of C$10 million/year “since entering the market,” which would be about 3 years as of this article. With retail prices in the C$100-$200 range for many of their thermostats, and the fact that this was written 1.5 years ago, I am confident that they’ve sold hundreds of thousands of them. ↩︎

  2. If a device has to connect to the Internet in order to work, and I don’t have control over the software running on it, I don’t really own it or even control it. I’m an incorrigible pain-in-the-ass absolutist about this.

    Also, I had never owned anything that you’d likely consider an Internet-of-Things device before. I’ve read plenty of stories over the years about Internet-connected consumer devices getting hacked, or bricked when their manufacturers no longer want to support them. There are even infurating cases where device makers use their remote control over devices to cut off features that users paid for and relied on↩︎ ↩︎

  3. create_ap is a bash script that uses hostapd, dnsmasq, and iptables to turn your desktop Linux computer into a full-featured and very customizable WiFi access point and router, which I’ve been maintaining it since 2020. It has become an indispensable part of my toolkit. If you are trying to analyze network traffic with any WiFi-connected device, try it out. ↩︎

  4. “URL presigning” really should be called “query presigning”. The idea is that your web app might require executing an HTTP query like GET https://my.aws.server/secret/thing?foo=bar with request header X-Which-Secret-Thing: the-shiny-one. You need to be able to hand out a “one-time ticket” to a possibly-untrusted client to allow it to execute this exact query, while protecting against reuse of the ticket, or modification of the query. SigV4 is AWS’s way of creating these tickets, and exists both in a form where the ticket gets plunked into an Authorization header as well as one where it gets glommed onto the URL’s query string. ↩︎

  5. I didn’t use or consult any generative AI in any of the writing, researching, or coding described here. I think “dwelve” is a great word. If you’re a prominent venture capitalist who does grep dwelve and discounts all matches as AI slop, then… congratulations! You’ve poisoned your brain. ↩︎

  6. This is an odd one! The Mysa V1 and V2 devices have humidity sensors and this is an advertised feature. The Mysa V2 Lite device doesn’t advertise a humidity measuring feature, and the app doesn’t show it, but it definitely has a humidity sensor onboard and sends humidity readings to the servers. Initially, I thought that the appearance of humidity readings on the V2 Lite devices might be a vestigial reading from a disconnected GPIO port, or that the humidity sensor might be physically present but uncalibrated, but it appears to work just fine. I don’t know why Mysa doesn’t enable this feature. 🤷🏻‍♂️ ↩︎

  7. The Mysa V1 and V2 have current sensors, enabling them to accurately measure the power consumption of your baseboard heater. The Mysa Lite doesn’t have this sensor. ↩︎

  8. Actually “in addition”… not “instead.” In discovering the vulnerability, I did also discover an MQTT topic called /v1/dev/DEVICE_ID/batch, and learned how to get the precise sensor readings from the thermostats, so my original supposition proved correct. ↩︎

  9. These names could range from relatively generic terms like “Office” to very revealing ones like “Vault behind the hidden bookshelf in the basement den” ↩︎

  10. One reason that BC Hydro has promoted and cross-subsidized Internet-connected smart thermostats is in order to expand their voluntarily, opt-in demand response program, in which the utility would be able to adjust its customers thermostats during times of exceptional demand like cold snaps and heat waves. This would help maintain the reliability of power generation and distribution for all users; the vulnerability here would basically allow an attacker to do the opposite↩︎

  11. I have disclosed vulnerabilities in near-household-name technologies before, even while working for a well-known company myself, and I’ve been met with hostility, confusion, non-acknowledgment, or non-response. I’ve also triggered months-long infighting and buck-passing when internally reporting vulnerabilities at previous employers. ↩︎