After investigating Zenly, the Checkmarx Security Research Team discovered a User Data Exposure vulnerability and an Account Takeover vulnerability that could have potentially put Zenly users at risk. The details of our discoveries are below. A Zenly representative told us, “We care deeply for our users, so we are grateful to the Checkmarx team who helped us fix issues before they could have any negative impact on the Zenly user experience.”
Vulnerability #1: Friend Request Flow Exposes User Data (rpc.znly.co)
(CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N) (5.3 Medium)
CWE-359: Exposure of Private Personal Information to an Unauthorized Actor
When submitting a friend request to a user, Zenly will allow access to their phone number regardless of whether the friend request is accepted or not. To obtain this information, a malicious actor only needs to know their username. While obtaining a username could be a difficult task by itself, it is made easier by the fact Zenly also exposes an exhaustive list of friends of a user. This means that, for obtaining the phone number of a user, a malicious actor does not need to know their username at the start, but is able to follow a chain of friends until one of them has the victim in their friends list.
Why This Is an Issue
Exposure of user data can be used by attackers with malicious purposes. Obtaining this data can put at risk not only the users of the application, but also Zenly’s brand image.
Consider a scenario where a malicious actor wants to attack a company by targeting its CEO. An attacker can make use of this vulnerability and employ the following attack vector:
- Search the web for an employee of the company and try to obtain their social media handle (for example, on Twitter).
- Employees who work on communications or marketing fields are typically more exposed and represent easier targets.
- Check if their handle is valid on Zenly.
- Access their list of friends through Zenly, obtain the handle of the CEO.
- Retrieve the phone number of the CEO through their username.
- Carry out a spear phishing attack, using the phone number of the CEO.
An attacker can also repeat these steps to obtain the phone number of other employees and thus prepare a more credible attack.
Note that, according to documentation provided by Zenly, present at this link, it should not be possible to retrieve the phone number of a user unless we are already friends with them.
The following screenshot was obtained from this documentation (March 10, 2021).
Proof of Concept
To reproduce this issue, an environment that enables intercepting and decoding network requests is required. Once this environment is set up, we are able to gain visibility over network activity.
The vulnerability makes use of the “Add by Username” flow, which starts by searching a known username.
The interceptor that was previously set up can be used to view the requests that occurred during this search. Note that the “Add as Friend” button was never pressed, meaning a friend request was never sent.
By observing the response of the request that was executed on the /UserPublicFriends endpoint, a list of friends can be seen, although it is not displayed on the UI of the application. This list contains every friend of the user, one of them is Bogus_CEO (bogus CEO of Zenly, for demonstration purposes). Note that the response also contains their username, which could in turn be used to repeat this process and obtain their friends list instead.
Once we obtain the username of the target user, we can obtain their phone number through a flow that is almost identical. On the “Add by Username” view, we search for their username and complete the flow by tapping the “Add as Friend” button.
This friend invitation will trigger a request to the /FriendRequestCreate endpoint, whose response contains specific information regarding both our user (items 3, 5 and 6) and the target user (items 4, 7 and 8).
Note that the response contains both our phone number and the phone number of the target user, even though our friend request was never accepted by the target user.
The mitigation suggestion for this vulnerability can be split into two different steps.
The most severe impact comes from gaining access to Personally Identifiable Information (PII) of a user without authorization from their part. This could be mitigated by removing the target phone number field from the response that is returned when a friend request is created.
However, exploiting this vulnerability would be significantly more difficult if a malicious actor had to guess the username of their victims. With this in mind, the second step of this mitigation suggestion would be to effectively limit or shape the data that is returned by the /UserPublicFriends endpoint when a username search is made, instead of returning an exhaustive list containing the friends’ usernames.
If this data is being used as part of a requirement (for example, to show friends in common or to show a top of close friends), then consider only returning data of users which are relevant for these cases and, if possible, only their identifiers, instead of usernames.
Vulnerability #2: Account Takeover via SMS Authentication Flow (rpc.znly.co)
(CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N) (4.7 Medium)
CWE-287: Improper Authentication
During the authentication flow, an SMS is sent to the user in order to validate the session and proceed to the user account. The way Zenly API handles this flow is by:
- Calling the /SessionCreate endpoint with the mobile phone number of the user.
- A session for the user is created and a session token is returned, but no operations with this session are possible until the verification is complete.
- An SMS message is sent to the user, containing a verification code.
- Calling the /SessionVerify endpoint with both the session token and the verification code received by SMS.
- Once this request is successfully completed, the session token becomes valid, and the user is now logged in.
After the first call to /SessionCreate, subsequent calls will return the same session token, until a call to /SessionVerify is made with a valid verification code.
Why This Is an Issue
An attacker can take over a user account by abusing the /SessionCreate endpoint, which will consistently return the same session token (although not yet valid) for the same user. Once the legitimate user validates the SMS code for that session token, the session will become valid for both the legitimate user and the attacker.
The main point of this issue is that the attacker needs to obtain a session token before the legitimate user calls the /SessionVerify endpoint. This can be done either before or after the legitimate user calls the /SessionCreate endpoint.
Allowing both the legitimate user and an attacker to have the same session token will give an advantage to the attacker. The verification code sent through SMS will remain valid for the same amount of time that the session token is valid, and it will not be regenerated within that time period, meaning that if the legitimate user inputs this code in the application (triggering a call to /SessionVerify), the session token that both the legitimate user and the attacker hold will become valid. This means that the attacker now has a valid session for the account of the legitimate user, even though the attacker never knew the verification code.
On the other hand, even if the attacker wasn’t able to obtain the session token (through a call to /SessionCreate) before the legitimate user, this attack is still possible while the legitimate user doesn’t input the correct verification code in the application, although this scenario would be less likely since the time window for carrying out this attack can be rather short.
Once the attacker has a valid session for the account of the legitimate user, they can access their location, notifications, conversations, and friends’ information just like the legitimate user could. While its impact is severe, carrying out this attack can be non-trivial since an attacker would need to:
- Know the mobile phone number of the victim.
- This can be achieved using the “Friend Request Flow Exposes User Data” issue.
- Have knowledge of when the victim will login, sign up, register a new device, or go through the authentication flow for any other reason.
Proof of Concept
To reproduce this issue, an environment that enables intercepting and decoding network requests is required. For this purpose, we used the same approach followed for the “Friend Request Flow Exposes User Data” issue. Once this environment is set up, we are able to gain visibility over network activity.
By following a typical login flow, we can gain knowledge of the network requests that are involved. The flow starts by requesting the mobile phone number from the user. Once the user inputs their phone number, they will be prompted for a verification code that is sent through SMS.
At this moment, before entering the verification code, a request to /SessionCreate is launched. Note that this request (on the left) contains the mobile phone number of the user, and the response (on the right) to this request contains a session token, as shown below.
At this moment, if an attacker also sends a request to /SessionCreate with the mobile phone number of the legitimate user, they will obtain the same session token. The response to this request, initiated by the attacker, is shown below:
Note: In this example, the attacker called /SessionCreate after the legitimate user. However, the attacker could also have called /SessionCreate before the legitimate user. This would have caused Zenly (on the side of the legitimate user) to obtain the same session token that the attacker obtained.
At this moment, the legitimate user will receive an SMS message containing a verification code. The authentication flow is finished (meaning the session token will become valid) once the user inputs this code in their Zenly application. However, once the user does this, the attacker will also end up with a valid session token in their hands (since it is the same token).
The attacker can then use this token to impersonate the legitimate user, executing any request to the Zenly API with it. The attacker can also, at any time, check if the session token is valid by launching a request to /Me, an endpoint that returns information about the current session. If the verification code has not yet been entered by the legitimate user, requests to /Me will return a 401 Unauthorized response. Once the code is entered, requests to /Me will return session information (such as phone number and user identifier), as shown below.
Once the attacker knows the session is valid, they can launch requests to /Notifications, /UserPlaces or /ChatMessagesLazy instead, thus gaining access to notifications, geolocation and conversations of the legitimate user and their friends.
In order to mitigate this issue, the following steps could be taken:
- Session tokens should be unique for each call to /SessionCreate.
- A new SMS code should be sent on every call to /SessionCreate.
- Previous SMS codes should be invalidated once a new one is sent.
- Apply rate limiting to both /SessionCreate and /SessionVerify endpoints.
Note that if the first suggestion is applied, then not implementing the fourth suggestion could lead to a new account takeover issue. This is explained by the fact that constantly providing new session tokens for the same mobile phone number to an attacker (on the response to /SessionCreate) could also allow them to keep sending requests to /SessionVerify with new session tokens and random verification codes until a correct attempt is made.
Given that a user currently receives two verification codes per call to /SessionCreate, and each code consists of four digits, an attacker would have a probability of approximately 0.02% of placing a correct attempt when picking a code at random (2 in 10000). The probability of achieving success after a given number of attempts can be calculated using the formula below.
By plotting this function, one can expect to achieve a 50% probability of success after approximately 3470 attempts. Meaning if no rate limiting is in place and an attacker can launch an attempt per second, this probability of success would be achieved in under an hour.
Please note that even though some kind of rate limiting should be in place, these suggestions should be applied with moderation, keeping in mind that these should not allow an attacker to prevent a legitimate user from accessing their own account.
Summary of Disclosure and Events
The Checkmarx Security Research Team responsibly notified Zenly, providing detailed information of our research findings, instructions to reproduce the issues, and recommendations. Zenly has now fixed the issues we discovered.
- 27-Jun-2021 – Full vulnerabilities report shared with Zenly through their bug bounty program
- 29-Jun-2021 – Zenly acknowledged receipt and asked for more details
- 02-Jul-2021 – Full detailed shared with Zenly
- 13-Jul-2021 – Zenly released a fix for the “Account Takeover via SMS Authentication Flow” issue
- 15-Jul-2021 – Checkmarx approved the fix
- 20-Sep-2021 – Zenly released a fix for the “Friend Request Flow Exposes User Data” issue
- 25-Sep-2021 – Checkmarx approved the fix
It was a pleasure working with Zenly’s security team. Their professionalism and cooperation, as well as the prompt ownership they took, are what we hope for when we engage with software companies. Kudos!
This type of research activity is part of the Checkmarx Security Research Team’s ongoing efforts to drive the necessary changes in software security practices among organizations who offer online services in an effort to improve security for everyone overall.