Reverse engineering an API isn’t exactly unique these days but it’s always pretty interesting to see what steps are needed to gain a basic understanding of a private API. This post isn’t meant to be a technical how-to but more of a process in deciphering an unknown API. I’ve decided to not expose specific endpoints or headers values of the August API as part of this post but will follow up in the future with documentation.
tl;dr: See August Lock API Documentation here
I ended up setting my sights on August Lock as part of turning my various Smart Home devices (Nest, Dropcam, August Lock, Keen Home Vents, SmartThings, my car and a few others) into a custom management system for my house (lovingly called Homer). While August Locks operate over Bluetooth, I also have an August Connect that’s able to manage my lock from anywhere via their private API and mobile apps. I didn’t want to deal with Bluetooth as it wasn’t flexible enough for my needs so I focused on accessing the REST API instead.
Anytime I reverse engineer a private API, I start by proxying HTTP traffic from a target app by using the wonderful Charles Proxy. To learn how to setup Charles, Google will point you to a lot of great articles to get you started.
One issue I ran into immediately was that I couldn’t proxy any of the traffic from the August app! I had installed Charle’s root certificate that was working with other SSL endpoints but not August. It was pretty clear that the August app was using SSL Pinning but it wasn’t clear to me was exactly how to bypass it. I use my iPhone for my day-to-day personal and work use and didn’t want to jailbreak so I settled on using a Galaxy S6 instead.
After rooting the S6, I needed to install a few pieces of software that would enable me to disable SSL Pinning. I installed Xposed and the SSL_Unpinning module for Xposed, once those were setup I was able to disable pinning for the August app and started seeing SSL traffic in Charles.
August API Responses
I started monitoring traffic from a fresh install of August so I know I’m not missing any API requests fired on the first launch. Once I logged in, I started loading all the various screens and interacting with the app as much as possible to give me a broad overview of various aspects of the API calls. I then logged out and back in again to see if anything changed from the initial login. While the first time sent me a login validation code, the second time did not. Validation appeared to be tied to an “installId” UUID being sent on the login request which was indicated in a slightly modified response on the second login.
Once I had enough activity I began reviewing the Charles logs and seeing pretty standard API security features including an API access key, a custom User-Agent, and an access-token. The access-token was a JSON Web Token and I began decoding the various parts.
An example token, modified for security purposes.
The first part of the token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 base64 decoded to:
The second part between the two “.” decoded to:
The third part was the signature.
I played around with modifying the signature signing algorithm to “none” but it failed to be authenticated.
In Charles, I saw the access-token changing every request with only slight differences in the Payload section but with a completely new signature. The only modifications being made were the “expiresAt” date, indicating the token would expire once signed. I didn’t really want to have to store a token and deal with expiration so I decided to dig deeper into the Android app to see what I’d find.
I downloaded the August Lock APK and decompiled it giving me a very nicely formatted source code for the Android app. It was a large-ish code base (roughly 43,6072 LOC, most of that code wasn’t from August but I still had to dig through it all) and not everything was able to be decompiled into readable variables or class names. I decided to start by searching the source code for something I knew was hardcoded such as the token header. I found the header being set in the main API file and worked my way back from there finding a class that neatly contain every endpoint for the August API (many of which I had not seen in my logs) and some interesting functions available in a Utilities class.
After examining the API classes, I concluded the app wasn’t modifying the access-token and wasn’t capable of generating a signed token itself. I was going to have to store the access-tokens after all. However, I now had a comprehensive list of all endpoints including some interesting Nest related ones and the ability to maintain authentication indefinitely. At this point, I considered the August API completely accessible and I’ve got the basics working perfectly in Homer.
In a Utilities class, I found a function using ROT-13 character substitution and thought that it could be involved with access-tokens. Instead, it was decoding hardcoded strings to access a debug activity from the login screen and a “God” mode enabled by changing the User’s first name in the User Settings activity.
There were also some hard-coded endpoints for staging and QA servers, both of which were open to the public. As a general rule, those types of servers should not be accessible to end-users.