Hello again and welcome back. This is part two in our four-part series on firmware and embedded devices. Today, I will be discussing home automation and the Internet of Things (IoT). More specifically, I'll be talking about Blossom. Blossom is a cloud-based smart lawn watering system that will 'automatically' water your lawn. Normally, our goal is to break into the target device so I may inspect running processes and resident binaries to ensure they are not designed to work in ways that are counter to our interests. Today, I won't be doing that. Instead, I am going to observe the functionality of the device and how it interacts with the manufacturers cloud-based API. Then, I'll force network traffic redirection from the device to a server I control. Finally, I will recreate a bare minimum copy of the manufacturer's API available internally so that the device will no longer require internet access for a somewhat normal operation.

What does this mean? I am going to write an application to water my lawn, when I want my lawn watered. Why? Because I like the functionality of smart-enabled devices, but I do not like adding network potential pivot points anywhere on my networks. My hope is that this part in our series serves as a soft introduction into the thought process I typically use when removing an unwanted third-party from my networks or even attempting to attack the underlying software of a target device.

So, how does the Blossom work? The first thing Blossom asks you to do is move your wiring over to the new system, but I won't cover that. Once you power it up, Blossom will start an Access Point that you can connect to. The access point will be named as such: Blossom-XXXX where 'X' is a four digit number. Once you install the corresponding phone app, it wants you to create an account and input some information about the geographic location where the system resides. In retrospect, since I don't care about managing my lawn settings while say ... traveling or while being down the street at a friends, creating an account and providing said information may be irrelevant. There is also an HTTP portal and API which does not require any of that information.

The wireless password for every Blossom unit is '12flowers'. Once connected to the Blossom access point, you should visit the default web portal (http://192.168.10.1). The latest Blossom firmware will disable the access point after setup, but it will also disable the web portal and API I use. My advice is to not update the firmware. However, a bug in the older firmware exists where the access point does not get torn down after setup. This was a concern to me as a separate wireless adapter on the Blossom also exists, which is connected to an internet routable network. A security issue in the Blossom web application or WSGI API could result in another easy pivot point. Fortunately, after emailing Blossom's support group they agreed to provide me with a firmware build that would disable the access point while retaining the web server and API. I wish other vendors would be as responsive to my requests. Bravo Blossom!

Here you can configure a few things within the Blossom. The four main sections within the UI are named: Provisioning, System Info, Advanced, and Blossom. Provisioning allows you to connect Blossom to your wireless access point. System Info displays basic network adapter and operating system information. Advanced allows the wireless access point settings to be changed, firmware to be upgraded, the device can be rebooted, and reset to the factory issued state. The Blossom section allows the user to change the 'Server Group' between Live, Staging, and Test. I haven't tested to see if changing this value alters system settings such as running services.

POST /sys/network HTTP/1.1 Host: [redacted] User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:42.0) Gecko/20100101 Firefox/42.0 Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate DNT: 1 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 X-Requested-With: XMLHttpRequest Referer: http://[redacted]/ Content-Length: 175 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache {"ssid":"korelogicwashere","security":4,"key":"sillyfuntimes","ip":0,"ipaddr":"192.168.2.2","ipmask":"255.255.255.0","ipgw":"192.168.2.1","ipdns1":"0.0.0.0","ipdns2":"0.0.0.0"}

They've tried to hide some non-critical things, but it doesn't seem like they put a lot of effort into it. For example, you can open and close valves artibrarily and also change the LED colors! Their method of hiding the functionality was to comment out the associated javascript which displays that part of the UI. They didn't actually remove the underlying functionality. So if you just view source:

Example requests for those looked like:

For the LED:

POST /bloom/led_custom HTTP/1.1 Host: [redacted] User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:42.0) Gecko/20100101 Firefox/42.0 Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate DNT: 1 Content-Type: application/json; charset=utf-8 X-Requested-With: XMLHttpRequest Referer: http://[redacted]/ Content-Length: 76 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache {"type":1,"r1":90,"g1":10,"b1":0,"r2":20,"g2":20,"b2":30,"t1":500,"t2":1000}

For valve control:

POST /bloom/valve HTTP/1.1 Host: [redacted] User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:42.0) Gecko/20100101 Firefox/42.0 Accept: application/json, text/javascript, */*; q=0.01 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate DNT: 1 Content-Type: application/json; charset=utf-8 X-Requested-With: XMLHttpRequest Referer: http://[redacted]/ Content-Length: 24 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache {"valve":1,"inverter":1}

At this point, my dest device is sitting on a rooted Linksys WRT54GL wireless network. Inspecting the traffic is trivial from this position within the network. Even more favorably, there is no encryption between the device and the cloud-based API. As far as the type of information within the network traffic, it's mostly non-sensitive information pertaining to watering schedules, current weather information, etc. However, it does also contain the approximate GPS coordinates and physical address for the device. In my opinion, this traffic probably should be encrypted. I used fake information but have redacted the data shown here so as to not implicate unaffiliated third parties. You know ...just in case.

HTTP/1.1 200 OK Allow: GET, POST, PUT, PATCH, HEAD, OPTIONS Content-Type: application/json Date: Thu, 19 Nov 2015 22:58:38 GMT Vary: Accept transfer-encoding: chunked Connection: keep-alive {"latitude": [redacted], "longitude": [redacted], "zones": [{"id": 44318, "name": "Zone 1", "plant_type": 1, "emitter_type": 5, "gets_rainfall": true, "user_watering_modulation": 1.0, "valve": 1, "active": true, "illustration": null, "thumbnail_1": null, "thumbnail_2": null, "thumbnail_3": null, "thumbnail_4": null, "schedule_manual_secs": 180, "auto_scheduling": true, "precipitation_rate": 16.51, "kc": 0.75}, {"id": 44319, "name": "Zone 2", "plant_type": 1, "emitter_type": 5, "gets_rainfall": true, "user_watering_modulation": 1.0, "valve": 2, "active": true, "illustration": null, "thumbnail_1": null, "thumbnail_2": null, "thumbnail_3": null, "thumbnail_4": null, "schedule_manual_secs": 180, "auto_scheduling": true, "precipitation_rate": 16.51, "kc": 0.75}, {"id": 44320, "name": "Zone 3", "plant_type": 1, "emitter_type": 5, "gets_rainfall": true, "user_watering_modulation": 1.0, "valve": 3, "active": true, "illustration": null, "thumbnail_1": null, "thumbnail_2": null, "thumbnail_3": null, "thumbnail_4": null, "schedule_manual_secs": 180, "auto_scheduling": true, "precipitation_rate": 16.51, "kc": 0.75}, {"id": 44321, "name": "Zone 4", "plant_type": 1, "emitter_type": 5, "gets_rainfall": true, "user_watering_modulation": 1.0, "valve": 4, "active": false, "illustration": null, "thumbnail_1": null, "thumbnail_2": null, "thumbnail_3": null, "thumbnail_4": null, "schedule_manual_secs": 180, "auto_scheduling": true, "precipitation_rate": 16.51, "kc": 0.75}, {"id": 44322, "name": "Zone 5", "plant_type": 1, "emitter_type": 5, "gets_rainfall": true, "user_watering_modulation": 1.0, "valve": 5, "active": false, "illustration": null, "thumbnail_1": null, "thumbnail_2": null, "thumbnail_3": null, "thumbnail_4": null, "schedule_manual_secs": 180, "auto_scheduling": true, "precipitation_rate": 16.51, "kc": 0.75}, {"id": 44323, "name": "Zone 6", "plant_type": 1, "emitter_type": 5, "gets_rainfall": true, "user_watering_modulation": 1.0, "valve": 6, "active": false, "illustration": null, "thumbnail_1": null, "thumbnail_2": null, "thumbnail_3": null, "thumbnail_4": null, "schedule_manual_secs": 180, "auto_scheduling": true, "precipitation_rate": 16.51, "kc": 0.75}, {"id": 44324, "name": "Zone 7", "plant_type": 1, "emitter_type": 5, "gets_rainfall": true, "user_watering_modulation": 1.0, "valve": 7, "active": false, "illustration": null, "thumbnail_1": null, "thumbnail_2": null, "thumbnail_3": null, "thumbnail_4": null, "schedule_manual_secs": 180, "auto_scheduling": true, "precipitation_rate": 16.51, "kc": 0.75}, {"id": 44325, "name": "Zone 8", "plant_type": 1, "emitter_type": 5, "gets_rainfall": true, "user_watering_modulation": 1.0, "valve": 8, "active": true, "illustration": null, "thumbnail_1": null, "thumbnail_2": null, "thumbnail_3": null, "thumbnail_4": null, "schedule_manual_secs": 180, "auto_scheduling": true, "precipitation_rate": 16.51, "kc": 0.75}, {"id": 44326, "name": "Zone 9", "plant_type": 1, "emitter_type": 5, "gets_rainfall": true, "user_watering_modulation": 1.0, "valve": 9, "active": true, "illustration": null, "thumbnail_1": null, "thumbnail_2": null, "thumbnail_3": null, "thumbnail_4": null, "schedule_manual_secs": 180, "auto_scheduling": true, "precipitation_rate": 16.51, "kc": 0.75}, {"id": 44327, "name": "Zone 10", "plant_type": 1, "emitter_type": 5, "gets_rainfall": true, "user_watering_modulation": 1.0, "valve": 10, "active": true, "illustration": null, "thumbnail_1": null, "thumbnail_2": null, "thumbnail_3": null, "thumbnail_4": null, "schedule_manual_secs": 180, "auto_scheduling": true, "precipitation_rate": 16.51, "kc": 0.75}, {"id": 44328, "name": "Zone 11", "plant_type": 1, "emitter_type": 5, "gets_rainfall": true, "user_watering_modulation": 1.0, "valve": 11, "active": true, "illustration": null, "thumbnail_1": null, "thumbnail_2": null, "thumbnail_3": null, "thumbnail_4": null, "schedule_manual_secs": 180, "auto_scheduling": true, "precipitation_rate": 16.51, "kc": 0.75}, {"id": 44329, "name": "Zone 12", "plant_type": 1, "emitter_type": 5, "gets_rainfall": true, "user_watering_modulation": 1.0, "valve": 12, "active": true, "illustration": null, "thumbnail_1": null, "thumbnail_2": null, "thumbnail_3": null, "thumbnail_4": null, "schedule_manual_secs": 180, "auto_scheduling": true, "precipitation_rate": 16.51, "kc": 0.75}], "channel": "channel_[redacted]", "sch_start": "+60", "address": {"city": "[redacted]", "country": "[redacted]", "street2": null, "zipcode": "[redacted]", "state": "[redacted]", "street": "[redacted]"}, "timezone": "[redacted]", "current_time": "2015-11-19T14:58:37.925-08:00", "avg_eto": 1.74}

Anyhow, I am at a cross-road. There are two ways I can go about accomplishing our goal. I got close to making both work, but in the end and for the purpose of this blog, I will only discuss one. The first way, and way I won't go into detail on how to recreate, has code to almost completely rebuild their cloud-based API, including the phone app. The phone app has a button that lets you run the sprinklers arbitrarily. This is why it was my intial target for accomplishing our goal. In the end, that route was taking much more effort than what it would for the route I'll talk about later on. A bit of information on how their cloud architecture works:

I thought it would work kind of like this:

[Phone App] -> [Cloud API] [Blossom]

But really it works something like this: