I use Home Assistant to automate my home. This mostly involves turning lights and music on and off but I really like the idea of the dashboard being a sort of hub for my abode.
I am also getting fat. I mean, lockdown fat.
I have a set of Renpho scales to keep track of this and connect over WiFi, but I never remember to weigh myself. The app works well enough, but really I want the data to feed into my Home Assistant, so I can see my current weight and send myself reminders to weigh in - I will probably do something similar with the Pure Gym app to check when I last visited and remind me to pay a visit, but that's a different story.
Being a tubby Thomas, I intend to do the following:
This was a right chew on.
Basically I have very little knowledge of programming for Android, bar some React Native experiments, and my Wireshark isn't great. I had a brief look into sniffing via Wireshark, as the quickest in and out operation, but this fell flat pretty quickly as a) I have a 2018 Macbook pro (the one with the pathetic, broken keyboard) with the wifi chipset of 2 midgies, so that was out the window and b) there's a 99.9% chance the data will be sent over ssl, so I wouldn't be able to read any traffic between the app and the server. I had assumed at this point the data would be sent over a fairly simple web rest api, which thankfully it sort of is.
My two options were:
I'm not clued up on Java enough to really get into decompiling, and a quick look at some of the methods were a little bit more than spinning back to 2001 and using a cracked version of WDasm so I went for packet sniffing.
After multiple trial and errors, rooting an old Pixel3a, installing security certificates and general failing and fannying about, I have had success with the following method.
The amount of nerds out there creating tools for free is amazing, I salute each and every one of you.
I went with using an Android Virtual Device.
I'm not explaining how I set all these up, but the method was along the lines of:
Obviously I installed the Renpho app on my AVD and checked data was logging.
Huzzah, we have decrypted SSL traffic from the app proxied through mitmproxy. I am very aware one of the endpoints is called girth_goals
, I'm not aware wtf it actually returns data wise. Maybe ask your mam. I have cut out any potentially identifiable data on this screenshot.
Next up, I logged out the app, logged in again and, again, huzzah! I have a post request containing the following information:
So my next task is to replicate this call in postman. I will mention now the above screenshot has resolved ips, and when using in postman the requests seemed to break, possibly due to how virtual hosts are set up or equivalent, but looking at my PCAPDroid logs I have the endpoint renpho.qnclouds.com. Sadly this doesn't take you to a browser login, but it was worth a shot.
In postman I've created a request with the bare minimum, to sort of feel it out.https://renpho.qnclouds.com/api/v3/users/sign_in.json?app_id=Renpho
- it seems to like the app_id here, my theory is qnclouds is a generic api service and Renpho identifies who to check logins against or otherwise identify the app. I did manage to remove every other query var and have it return success however.Body params are sent as x-www-form-urlencoded and everything is grand. It returns something similar to the following:
{
"status_code": "20000",
"status_message": "ok",
"terminal_user_session_key": <hidden this>,
"device_binds_ary": [
{
"id": <hidden this>,
"mac": <hidden this>,
"scale_name": "QN-Scale",
"demo": "",
"hw_ble_version": 0,
"device_type": 0,
"hw_software_version": 0,
"created_at": "2020-12-26 07:51:54",
"uuid": "",
"b_user_id": <hidden this>,
"internal_model": "02D3",
"wifi_name": <hidden this>,
"product_category": 0
}
],
"id": <hidden this>,
"email": <hidden this>,
"account_name": "Neil",
"gender": 1,
"height": <hidden this>,
"height_unit": 1,
"waistline": 0,
"hip": 0,
"person_type": 0,
"category_type": 0,
"weight_unit": 4,
"current_goal_weight": 0.0,
"weight_goal_unit": 1,
"weight_goal": 82.6,
"locale": "en",
"birthday": <hidden this>,
"weight_goal_date": "",
"avatar_url": "",
"weight": <hidden this>,
"facebook_account": "",
"twitter_account": "",
"line_account": "",
"sport_goal": 0,
"sleep_goal": 0,
"bodyfat_goal": <hidden this>,
"initial_weight": <hidden this>,
"initial_bodyfat": <hidden this>,
"area_code": "GB",
"method": 2,
"user_code": "",
"agree_flag": 1,
"reach_goal_weight_flag": 0,
"reach_goal_bodyfat_flag": 0,
"set_goal_at": <hidden this>,
"sell_flag": 1,
"allow_notification_flag": 1,
"phone": "",
"region_code": "",
"dump_flag": 0,
"weighing_mode": 0,
"password_present_flag": 1,
"stature": 67.0,
"custom": "",
"index_extension": -1,
"person_body_shape": 0,
"person_goal": 0
}
I've hidden a few things there as a matter of privacy, but you get the gist.
From the returned information, the most important I believe and a key to figuring this out is the terminal_user_session_key. This looks to be sent with every following request in order to authenticate the user, and in testing I have confirmed this. This key also seems to expire, I guess it is literally a session token.
My next puzzle, as I seem to have an encoded version of my key which provides session access, is to either filter through my requests to find what I want and build a very simple library around it, or, and this is my next puzzle, figure out how the client is encrypting the password before sending for verification.
From what I can tell after preliminary investigation:
I have checked it straight up against base64 encoding, and all default supported android hashes listed here with no luck as of yet.
I have a feeling in this part it may be easiest to dump and decompile the Renpho Apk in order to check what calls it is making to hash the password, as it may be using a combination of email + password strings, or a changing salt. This isn't my strong suit, so I find it odd that you can hash something with a different outcome each time and have it verify, but I'm pretty sure I'll learn something which is encryption 101 in my next delve into this.