Recently, during an internal IoT research project, we did a pentest of the Android and iOS Yunmai smart scale apps.
Below are the 5 vulnerabilities that we discovered, and we chained 3 of these (#2,#3 and #4) to achieve mass account takeover. All vulnerabilities have been responsibly disclosed to Yunmai.
- Bypass limit of 16 family members per primary account
- UserID Enumeration
- Ineffective authorization checks
- Information Leak
- Account takeover through ‘password reset’ functionality
First, a short description about the Yunmai mobile apps.
The Android one has 500.000+ installs. However, the iOS AppStore does not show the number of downloads as the Google Playstore.
The mobile application allows you to see your weight, a graph with your progress over time, along with 12 other indices such as BMI, body fat percentage, visceral fat, etc. In addition, you can add and delete family members to your account, and each user can add information such as sex, name, age, height, relationship, profile photo.
TLDR: We achieved Mass Account Takeover by chaining 3 vulnerabilities. First we brute-forced userIds, in order to leak puId (primary uid) account values. Then we used the leaked puId values in a Privilege Escalation attack to add ‘family member’ accounts to other registered accounts; in response, we obtained an Information Leak containing the corresponding ‘accessToken’ and ‘refreshToken’ of the newly created ‘family member’ accounts. Thus, we can now use these tokens to impersonate the ‘family member’ accounts, switch between the accounts of the family members and query all their data.
Account types and account structure
There are two types of accounts: parent/primary accounts and family member/child accounts. The primary accounts are all the accounts created through the registration process. On the other hand, the child accounts are all the accounts created by the primary account once logged in. The purpose of the ‘child’ accounts is to have the data of each family member separate if more than 1 person uses the same smart scale.
When a user registers in the app, each user account will contain information such as:
– a userId key having a unique 9 digit value for each user. (ex: ‘userId’: xxxxxx252, as seen below)
– a puId (or primary userId) key having a value of 0. If the puId is 0 it means the userId is also a puId in this case.
Once logged in in the app with the above registered account you can create ‘child’ accounts for those friends or relatives that will use the scale with you. This will help to keep your measurements data separate. Thus, when you create the ‘child’ accounts, each ‘child’ account, just like the primary account, gets a puId value and a unique 9 digit userId (ex: ‘userId’: xxxxxx253, as seen below). But, the puId value will be equal to the userId of the user who created the ‘child’ account (ex: ‘puId’: xxxxxx252)
Now let’s take a look at the vulnerabilities one by one.
1. Bypass the limit of 16 family members per primary account
A primary account can have up to 16 ‘family member’ accounts registered to use the same smart scale. However, this limit is enforced only client-side and not server-side.
If you try to create the 17th family member account from the app, you will receive the below message.
However, you can use a previous request to create such an account. Just update the ‘realName’ value of the new account and send it to the server once you reached the 16 accounts limit. Thus, you bypass the client-side validation and the server will happily fulfill the request, as seen below.
2. UserID Enumeration
When you are adding users under your account, the maximum number of members you can add is 16.
At one point, when you reached the limit of 16 registered family members, a request will be issued on Android only. As seen below, this request will contain your 16 userIds values.
Let’s keep from the above request only 1 userId instead of all the 16 userId values. We will brute-force the last 5 digits of the userIds (from the “ids” parameter). Moreover, we will extract from the responses the usernames, as seen below.
This is a great way to extract from the enumerated users:
- their names
- profile photos
- their sex
- puIds values
The above request is the 1st one to be used in our chain to achieve Mass Account Takeover.
userIds – represents a unique 9 digit value corresponding to each account.
puIds – is the userId of the primary ‘family member’ account. All the userIDs of the family members of one account sit under one puId (primary userId).
3. Ineffective Authorization Checks
Description: The Android and iOS API were discovered to not implement any authorization checks while adding or deleting ‘family member’ accounts to/from other accounts.
Impact: You can add and delete family members to/from other people’s accounts.
- Deleting family members from other people’s accounts
- Adding family members to other people’s accounts
Instance1: Deleting family members from other people’s accounts
The account supports adding multiple family members in order to switch between them and keep their data, such as weight, BMI, body fat %, etc, separate.
Now that you have the userIds that you want from the previous finding ‘Username enumeration’, you can target one userId belonging to a different account and delete it. All you have to do is update the ‘delUserId‘ parameter with the userId that you want to delete, in the below request. As a result, that userId will be deleted because of the lack of ‘Authorization Checks’ of the API on the ‘delUserId’ parameter. These ‘Authorization checks’ should verify if this to-be-deleted userId is one of my ‘family member’ accounts using my Yunmai smart scale, or not.
Below is the request on the iOS app with the modified delUserId and its successful response:
Instance2: Adding family members to other people’s account
All we need to perform this attacks is a victim’s puId value, that we can take from the above ‘Username enumeration’ vulnerability. Once we have that, we can add family members to that account, as seen below. We only need to update the puId value with the victim’s puId.
The below request is the 2nd and last one to be used in the chain to achieve Mass Account Takeover.
Adding family members in the iOS application under victim’s puId:
From the above response, we must take notice of 2 important parameters useful for the next finding: ‘accessToken’ and ‘refreshToken’.
4. Information Leak leading to Mass Account Takeover
The mass account takeover chain
As seen in the above screenshot (‘Figure 34’), when we create a ‘family member’ account to other people’s accounts, the server leaks the ‘accessToken’, and the ‘refreshToken’ of the newly created account. As a result, we can impersonate the newly created ‘family member’ account. Then, we can move upwards to impersonate and hijack the primary account. Doing this process in an automated way for every primary account enumerated through chaining this Information leak with the previous 2 findings (UserId Enumeration and Ineffective Authorization Checks), we can achieve Mass Account Takeover.
Now that we have a leaked ‘accessToken’ and ‘refreshToken’ from Fig. 34, we can impersonate the newly created account. Below is an example where we exchanged the leaked ‘refreshToken’ for a new ‘accessToken’. Regarding the ‘userId’ request parameter, there is no need to update it to our ‘MaliciousAccount’ one.
Now we can use the newly ‘accessToken’ from the above response to do authenticated requests, as seen below. Here we query the newly created ‘MaliciousAccount’ account. This account was set up using the default values such as: ‘basisWeight’: 0 ; ‘birthday’: 19970524;
From the above request, we move ‘laterally’ inside the family account to query the primary account and all its family members. Therefore, we need the above ‘puId’:602619692 value. Below we can see there are 16 userIds (family members) in total under this puId account.
We take note of the above ‘accessToken’ belonging to the primary account and now we query the puId user for extra details.
Finally, get all the recordings with information about this victim user. In the below response are a ‘total’ of 2 recordings meaning the victim weighed 2 times.
5 Account takeover through ‘forgot password’ functionality
The iOS functionality to change your password is not working at all. However, the Android application implements a weak ‘forgot password’ functionality.
The application does NOT invalidate the previously generated ‘forgot password’ tokens, once a user requests a new ‘forgot password’ token. As a result, an attacker can request multiple tokens to be sent to the victim’s email. This will increase attacker’s chances of guessing that code and changing the victim’s password. So the impact of this finding is that someone can take over over any Yunmai’s user account.
Endpoint: https://intapi.iyunmai.com/api/android/verify/code.d – for generating ‘forgot password’ tokens that are emailed to the victim
Steps to Reproduce:
- Go to the login page in the Android app and select ‘Forgot your password?’.
- Write the email of the targeted victim and click ‘Send verification code’. Afterwards, the victim will get an email with a unique 6 digit code that allows to reset the password.
- Intercept the above request and send it multiple time, in order to increase your chances of guessing the code. We have retrieved the following list of codes: 172067, 167686, 167921, 164708, 171353, 171316, 163569, 169577,163965, 168449, etc, as seen below.
- Analyzing the codes, we realized we can do a brute force them, as seen below. This is to reset the password of the victim to the one we want. Therefore we chose a brute-force range from 166000 to 169999. The first and only successful code was 166309.
- And the response to the above request:
Responsible Disclosure Timeline:
Sept-Oct 2021: We responsibly disclosed all vulnerabilities to the support team. So the Support team thanked us for our help and forwarded our emails to the dev team. However, we received no response back from the dev. team.
We did multiple retests.
Regarding the finding #5, a silent fix has been made after we reported it. Now all the previously generated ‘forgot password’ tokens are invalidated. Consequently, we did a retest for this finding, however, the codes are in the same range 165k-175k. Thus, you can easily bypass this partial protection and achieve account takeover through forgot password functionality’ by brute-forcing the reset password token.
The finding #2 is still open. Thus, the request on Android to the endpoint /device-users.d used for ‘UserId enumeration’ is still triggered when/after it reaches the 16 users limit.
The finding #3 is still open. So you can still add/delete family members to/from other accounts.
5th of May 2022: we emailed again to the support and dev. teams. We got no response back.
30th of May 2022, 7months after the first emails to Yunmai to report the findings: Blog post published.
Privilege Escalation Attack in Concrete CMS
Account Takeover through Password Reset Poisoning vulnerability in Drupal CMS
Account Takeover through Password Reset Poisoning vulnerability and a Stored XSS for Full Compromise in Joomla