描述
之前以为对路由器进行研究,需要先买一个路由器。之后对路由器的固件进行了解之后,才知道直接从官网下载对应的固件文件,然后用Linux系统即可搭建环境。
准备
待研究的路由器一个带有binwalk
命令的系统。去想研究的路由器官网下载带有漏洞的历史固件:
源码获取
binwalk -Me filename.bin
源码在squashfs-root
目录下,打包,然后拖到本机tar -cvf squashfs-root.tar squashfs-root
,其实,这就是一个小型的Linux系统。
目录结构
前端
lua写的路由器主要分两块,一块是web前端代码,在/www/
目录下边。
前端文件访问均不需要权限,主要包括js、css、json和图片等文件。可以翻翻js文件、json文件,可能会包含当前路由器的固件和硬件版本信息,针对性的分析。
后端
另一块是后端逻辑代码,在usrliblualuci
下面。
后端代码主要通过控制器来访问,控制器文件放在usrliblualucicontroller
目录下,admin
内的都是需要管理员权限才能访问的功能,其他文件不需要权限即可访问。
url和文件对应关系
stok=
后面对应的值,对应usrliblualucicontroller
下面相应位置的lua
文件。比如路径:/cgi-bin/luci/;stok=/locale?form=lang
对应usrliblualucicontrollerlocale.lua
文件。
分析思路
全局搜索危险函数
相对于路由器,主要关注权限控制、命令执行、文件操作、XSS等漏洞。危险函数有如下,其他更多的自己探索。
popen()
exec()
open()
` `
execute()
拿popen来举例
可以看到,找到很多。不过也是有选择的分析,后边参数写死的就不用看了。选择第一个深入分析。没有对e
参数进行过滤,但是也没有e
参数的来源,还需要进一步跟踪。
function exec(e)
local e = r.popen(e)
local n = e:read("*a")
e:close()
return n
end
继续跟进,全局搜索exec
的调用。注意这里的exec
不是原生库的exec
函数。而是sys
文件内的exec
,后面可能的调用s.exec
这个命令执行的命令行拼接了变量a
,跟进a
,a
的值来自n.file_name
,n
在第六行被重新赋值。
function unknown_ap_list_get()
local e = {}
local a
local _
local e = {}
local n, l = n.jcsUI2uac("unsupport_ap_get", n.JCS_OP_TYPE_GET, true, {})
if n == nil or n.ret ~= 0 or n.file_name == nil or n.unrecognize_ap == nil then
return t.ENONE, e, #e
else
a = n.file_name
end
local l, o = io.open(a, "r")
if l == nil then
r(o)
return t.ENONE, e, #e
else
_ = l:read("*a")
l:close()
end
e = i.decode(_)
if n.unrecognize_ap > 0 then
local n = {}
n.model_name = "unknown"
n.hardware_ver = "unknown"
e[#e + 1] = n
end
e = s.format_tbl_arry_with_secname_cnt(e, "unknown_ap_list")
luci.sys.exec("rm -f " .. a)
return t.ENONE, e, #e
end
跟进函数jcsUI2uac
function jcsUI2uac(c, n, e, a)
return t(c, n, e, a, '/tmp/jcs.ui.srv.uac')
end
继续跟进函数t
,大致意思就是读取一个pid文件,最后返回文件内容和消息文件内容不可控这条路算是断了下面分析其他地方。
local function t(_, t, f, p, u)
local a = u .. i.getpid() .. '.luaclt'
local n = i.open(a)
·······
d, e = i.rcve(n, 180)
······
s = o.decode(d)
if not s then
e = c .. 'Decode pkt failed'
end
end r(n, a)
return s, e
end
另一个sys.exec
的调用
if N == true then
l = "ping -c 1 -W %d -s %d %q 2>&1" % { e, i, t }
n = luci.sys.exec(l)
else
l = "ping -I %s -c 1 -W %d -s %d %q 2>&1" % { h, e, i, t }
n = luci.sys.exec(l)
end
可以看到变量h
、t
直接拼接进命令行,跟进变量t
,t
从参数C
传入。
local function V(c)
local t = c.addr or ""
local r = c.num or ""
local i = c.size or ""
local e = c.timeout or ""
local h = c.interface or ""
跟进V()
调用,当diag_type
为ping时,调用V()
。
function start_diagnose(e)
local o = e.diag_type
local n
if o == "ping" then
n = V(e)
elseif o == "tracert" then
n = u(e)
else
继续跟进start_diagnose(e)
调用,下面就是构造请求了。
register_keyword_action("diagnose", "start", "start_diagnose")
总结
当然,也可能会存在一些逻辑漏洞,比如绕过登陆限制,直接获取管理员权限。或者登陆模块比较脆弱,比如没有二次验证,密码未加密,密码简单等等。
原文始发于微信公众号(云智信安云窟实验室):路由器研究
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论