惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Darknet – Hacking Tools, Hacker News & Cyber Security
V
Vulnerabilities – Threatpost
Cloudbric
Cloudbric
G
GRAHAM CLULEY
S
Securelist
Schneier on Security
Schneier on Security
Help Net Security
Help Net Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Project Zero
Project Zero
Spread Privacy
Spread Privacy
P
Privacy International News Feed
C
Cyber Attacks, Cyber Crime and Cyber Security
Cisco Talos Blog
Cisco Talos Blog
T
Tailwind CSS Blog
博客园_首页
有赞技术团队
有赞技术团队
Simon Willison's Weblog
Simon Willison's Weblog
Stack Overflow Blog
Stack Overflow Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Latest news
Latest news
T
Tor Project blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Attack and Defense Labs
Attack and Defense Labs
www.infosecurity-magazine.com
www.infosecurity-magazine.com
O
OpenAI News
J
Java Code Geeks
T
Tenable Blog
K
Kaspersky official blog
AWS News Blog
AWS News Blog
S
Security @ Cisco Blogs
The GitHub Blog
The GitHub Blog
T
Threatpost
月光博客
月光博客
H
Heimdal Security Blog
Security Latest
Security Latest
The Hacker News
The Hacker News
Y
Y Combinator Blog
A
Arctic Wolf
Apple Machine Learning Research
Apple Machine Learning Research
C
Cisco Blogs
美团技术团队
Microsoft Security Blog
Microsoft Security Blog
Hugging Face - Blog
Hugging Face - Blog
T
The Blog of Author Tim Ferriss
C
CERT Recently Published Vulnerability Notes
D
Docker
Google Online Security Blog
Google Online Security Blog
D
DataBreaches.Net
V
Visual Studio Blog
H
Help Net Security

Emil Burzo

GPS spoofing teleported me to Peru, mid-flight Running Claude Code dangerously (safely) Tracking down an old OkHttp regression What does StackOverflow's personalized prediction data think of you? Cum se plătesc impozitele PFA prin BT24 Work-around for when keyserver.ubuntu.com is down Creating a tailable cursor on MongoDB 3 with the Java driver API Quick fix for CVE-2015-1328 Analiza parcului auto din România Setting up gitolite on Ubuntu 12.04 LTS
Reverse engineering the Nest home/away API
2020-12-17 · via Emil Burzo

Background

A while ago I purchased a Nest camera because I liked the idea of only having to provide power and a WiFi connection to get a nice security system.

The fact that you could control it with Works with Nest (their open API) was a major factor in that decision.

Then Google bought Nest, and disabled access for users who didn’t sign up in time (including me).

There were some promises that a new open API will be created “soon”.

Eventually, they released the Smart Device Management API, and although you can do some things (get camera stream), you can’t set the “Home/Away” status.

Bummer.

What is Home/Away anyway?

You can read more about it here, but TL;DR the Nest camera movement/sound alarms only trigger if it’s set to “Away”.

For reasons I don’t want to go into, the automatic Home/Away feature doesn’t work for me so I need a way to control it from code so it can be automated, as all good things.

Optimistic approach

Like all pros, my first try was to just open the Nest webapp with DevTools open, click the Home/Away toggle, copy as curl, run it from the CLI and… nothing.

Didn’t work.

Upon closer inspection I noticed the payload was binary, never a good sign :)

curl --data-binary $'\nB\n\u001aSTRUCTURE_XXXXXXXXXXXXXXXX\u0012$XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\u0012\u0095\u0001\n\u000estructure_mode\u0012u\nTtype.nestlabs.com/nest.trait.occupancy.StructureModeTrait.StructureModeChangeRequest\u0012\u001d\u0008\u0002\u0010\u0001\u001a\u0017\n\u0015USER_XXXXXXXXXXXXXXXX\u001a\u000c\u0008ü¸Ìþ\u0005\u0010\u0080É\u0087¸\u0001' 'https://grpc-web.production.nest.com/nestlabs.gateway.v1.ResourceApi/SendCommand'  

(some details redacted)

gRPC and protobuf

The host endpoint at the end of the curl command was a very good hint that they are using gRPC, which I didn’t have any experience with so far.

On the gRPC website was our next clue:

Define your service using Protocol Buffers, a powerful binary serialization toolset and language

That matches the binary payload that we previously saw, now it’s getting exciting!

Binary fun

Since the simple payload replay didn’t do anything, I needed to figure out what’s actually in there.

The first (surprising) problem was actually just getting the binary payload into a text file, I guess the binary was getting messed up somewhere between the browser and the file I was pasting to.

The “Save all as HAR with content” feature was very helpful here, especially since there’s a base64 encoded field beneath the binary one.

Extracting that only took a bit of bash magic:

cat set-away-home.nest.com.har                           \
    | gron                                               \
    | grep 'json.log.entries[1].response.content.text'   \
    | cut -d '"' -f 2                                    \
    | base64 -d                                          \
    | base64 -d

(If you don’t know about gron, you should definitely check it out, it makes JSON grep-able)

(No idea why it was base64-encoded twice)

Decoding protobuf

Going through the official protobuf documentation it seemed like I needed the .proto files to do anything useful.

Which I couldn’t figure out how to extract from the Nest website, or if that’s even possible.

Luckily, I stumbled upon protoc --decode_raw, which gave the following output:

1 {
  1 {
    1: "STRUCTURE_XXXXXXXXXXXXXXXX"
    2: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
  }
  2 {
    1 {
      1: "STRUCTURE_XXXXXXXXXXXXXXXX"
      2: "structure_mode"
      3: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
    }
    2: 4
    4 {
      1 {
        1: "type.nestlabs.com/nest.trait.occupancy.StructureModeTrait.StructureModeChangeResponse"
        2 {
          1: 1
        }
      }
    }
    6 {
      1 {
        1: "STRUCTURE_XXXXXXXXXXXXXXXX"
        2: "structure_mode"
        3: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
      }
      2 {
        1: "type.nestlabs.com/nest.trait.occupancy.StructureModeTrait.StructureModeChangeRequest"
        2 {
          1: 2
          2: 1
          3 {
            1: "USER_XXXXXXXXXXXXXXXX"
          }
        }
      }
      3 {
        1: XXXXXXXXXX
        2: XXXXXXXXX
      }
    }
  }
}
15: ""
2: ""
15: "\000\000"

Quite a heavy payload to set a flag.

Then I tried to manually create the .proto files going by the output from above.

Not fun.

By luck (aka searching GitHub for those types) I found out that somebody a lot smarter than me had actually managed to extract the proto files from the Nest website, thank you stranger!

Building my own request

Time to make use of those .proto files.

Start up the IDE and follow the protobuf tutorial on how to build a payload.

It was a lot harder than I expected.

I’m sure most of the hardness came from me never using protobuf before, but it also took me a bit to realize that they serialize the command we care about (StructureModeChangeRequest) into a generic type (ResourceCommandRequest) which is then also serialized.

Eventually I was able to send the protobuf payload to the Nest API and oh boy, seeing that icon switch from “Home” to “Away” was like an early christmas :)

Nest home to away animation

Hmm, but why isn’t the HTTP connection closing? It just hangs there.

Comparing the headers from the original request I could see that there’s an X-Accept-Response-Streaming: true header, adding it caused the connection to directly close as expected.

Strange, especially since false keeps the connection open. Mystery for another day.

I packaged everything nicely into a GitHub repo and moved on to the last part.

Custom home/away logic

The standard approach here would be to install some location tracker on all the “home” phones and periodically send it to a server which then decides when to toggle the Nest status.

Although I actually built an android location sharing app, I didn’t like this approach due to having to make a tradeoff between battery life and responsiveness.

I also looked into Google Location Sharing, but it involved creating another Google Account, permanently sharing my location with it and using that as the source. Too fragile.

The solution I went with in the end was:

  • assign static IPs to all the relevant phones
  • try to connect to them from the internal network

If the response is connection refused, they are on the network/“home”.

Anything else, they aren’t “home”.

Well, maybe also connection accepted if you’re weird and have a server on your phone.

I’m still surprised how well this works, because I read a lot of “don’t do this” online with reasons like:

  • phones will automatically switch off their WiFi overnight
  • they won’t respond when they are in deep sleep
  • etc

Maybe I got lucky, but it works, and it works very well:

2020-12-17 09:36:07 INFO     found hosts: ['192.168.0.30', '192.168.0.31']
2020-12-17 09:36:07 INFO     192.168.0.30 is home
2020-12-17 09:36:09 INFO     set status to: home (200)
2020-12-17 09:41:09 INFO     192.168.0.30 is home
2020-12-17 09:46:13 INFO     192.168.0.31 is home
2020-12-17 09:51:17 INFO     192.168.0.31 is home
[..]
2020-12-17 11:17:29 INFO     set status to: away (200)
2020-12-17 12:35:11 INFO     192.168.0.31 is home
2020-12-17 12:35:12 INFO     set status to: home (200)
2020-12-17 12:40:16 INFO     192.168.0.31 is home

The switch from Away -> Home is quicker than doing it by hand :)

I prepared another GitHub repo, fired everything up, and… it works!

Conclusion

It really sucks that I had to spend a day for something that either should just work, or at least I should have access to change on my own.

If the camera quality wasn’t so high I would have just ditched it and gone self-hosted with a DVR/NVR.

It would have taken longer to build all the features, but then I have control over every part.

The Nest app is still annoying me to migrate to a Google account, so let’s see for how long this solution actually works.