MiRouter漏洞挖掘

起因

和丁佬他们一起整了个小队去打了强网杯,然后就玩到了线下,有实物的rw赛制还是好玩

题目

赛题都放一个u盘里,然后还有个路由器的实体,有个说明文档,大致就是介绍题目的内容和上台演示的成功条件,由于比较菜,搞不来v8和VMware,就来搞这台路由器了

演示环境是不给后台密码的,所以我们需要挖的是一个远程利用的未授权RCE,这里拿到的路由器开了ssh,连上去可以知道这是小米路由器pro,然后把实机的固件dump下来确定版本后,去官网下原始固件来diff下看看是不是出题人塞东西或改的洞。
GoAhead漏洞分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
diff --git a/etc/mihttpd/mihttpd.conf b/etc/mihttpd/mihttpd.conf
index 9fbe742..89d058f 100644
--- a/etc/mihttpd/mihttpd.conf
+++ b/etc/mihttpd/mihttpd.conf
@@ -153,13 +153,13 @@ http {
upload_pass_form_field "^(?!nginx_file)";
upload_pass_args on;
}
- location /api-third-party/download/public {
+ location /api-third-party/download/public/ {
alias /userdisk/data/;
}
- location /api-third-party/download/private {
+ location /api-third-party/download/private/ {
alias /userdisk/appdata/;
}
- location /api-third-party/download/extdisks {
+ location /api-third-party/download/extdisks/ {
alias /extdisks/;
}

diff --git a/etc/sysapihttpd/sysapihttpd.conf b/etc/sysapihttpd/sysapihttpd.conf
index ab90d6c..16bf88c 100644
--- a/etc/sysapihttpd/sysapihttpd.conf
+++ b/etc/sysapihttpd/sysapihttpd.conf
@@ -129,19 +129,19 @@ http {
proxy_pass http://127.0.0.1:5081;
#
}
- location /backup/log {
+ location /backup/log/ {
alias /tmp/syslogbackup/;
}
location /api/service/plugin {
rewrite ^/api/service/plugin/control /api-third-party/download/private/$arg_appid/control.html? permanent;
}
- location /api-third-party/download/public {
+ location /api-third-party/download/public/ {
alias /userdisk/data/;
}
- location /api-third-party/download/private {
+ location /api-third-party/download/private/ {
alias /userdisk/appdata/;
}
- location /api-third-party/download/extdisks {
+ location /api-third-party/download/extdisks/ {
alias /extdisks/;
}
location /api-third-party/service {
diff --git a/usr/lib/lua/traffic.lua b/usr/lib/lua/traffic.lua
index 8bf1ad7..5c2569a 100644
--- a/usr/lib/lua/traffic.lua
+++ b/usr/lib/lua/traffic.lua
@@ -8,7 +8,7 @@ local dbDict
local dhcpDict

function cmdfmt(str)
- return str:gsub("\\", "\\\\"):gsub("`", "\\`"):gsub("\"", "\\\""):gsub("%$", "\\$")
+ return str:gsub("\\", "\\\\"):gsub("`", "\\`"):gsub("\"", "\\\"")
end

function get_hostname_init()
diff --git a/usr/lib/lua/xiaoqiang/module/XQBackup.lua b/usr/lib/lua/xiaoqiang/module/XQBackup.lua
index 9330fb2..e8e3b2c 100644
--- a/usr/lib/lua/xiaoqiang/module/XQBackup.lua
+++ b/usr/lib/lua/xiaoqiang/module/XQBackup.lua
@@ -1,7 +1,7 @@
module ("xiaoqiang.module.XQBackup", package.seeall)

-local DESFILE = "/tmp/cfg_backup.des"
-local MBUFILE = "/tmp/cfg_backup.mbu"
+local DESFILE = "/tmp/extmp/cfg_backup.des"
+local MBUFILE = "/tmp/extmp/cfg_backup.mbu"
local TARMBUFILE = "/tmp/cfgbackup.tar.gz"

-- backup functions
@@ -210,9 +210,10 @@ function save_info(keys, info)
local dstr = json.encode(keys)
local data = aes.encrypt(key, jstr)
local filename = os.date("%Y-%m-%d--%X",os.time())..".tar.gz"
+ os.execute("mkdir -p /tmp/extmp >/dev/null 2>/dev/null")
fs.writefile(MBUFILE, data)
fs.writefile(DESFILE, dstr)
- os.execute("cd /tmp; tar -czf "..backuppath..filename.." cfg_backup.des cfg_backup.mbu >/dev/null 2>/dev/null")
+ os.execute("cd /tmp/extmp; tar -czf "..backuppath..filename.." cfg_backup.des cfg_backup.mbu >/dev/null 2>/dev/null")
os.execute("rm "..MBUFILE.." >/dev/null 2>/dev/null")
os.execute("rm "..DESFILE.." >/dev/null 2>/dev/null")
local url = lanip.."/backup/log/"..filename
@@ -267,7 +268,8 @@ function extract(filepath)
if not fs.access(tarpath) then
return 1
end
- os.execute("cd /tmp; tar -xzf "..tarpath.." >/dev/null 2>/dev/null")
+ os.execute("mkdir -p /tmp/extmp >/dev/null 2>/dev/null")
+ os.execute("cd /tmp/extmp; tar -xzf "..tarpath.." >/dev/null 2>/dev/null")
os.execute("rm "..tarpath.." >/dev/null 2>/dev/null")
if not fs.access(DESFILE) then
return 2

发现把路径穿越还有一些其他的洞修了,然后删去了cmdfmt函数里防止$命令执行的过滤,那么重点就应该是在这个地方了,看一下traffic里的相关逻辑,能发现在trafficd_lua_ecos_pair_verify调用了cmdfmt

接着找一下哪有调用这个文件的这个函数的文件

1
2
3
iot@toi ~/C/r/d/rootfs> grep -r "trafficd_lua_ecos_pair_verify"
usr/lib/lua/traffic.lua:function trafficd_lua_ecos_pair_verify(repeater_token)
匹配到二进制文件 usr/sbin/netapi

那么该去逆一波netapi里trafficd_lua_ecos_pair_verify的逻辑了
简单的逆完再结合到路由器里其他使用netapi的脚本后可以知道这是小米魔改dbus的tbus的一个服务
然后根据路由器里的脚本我们可以得到命令行版命令注入的poc

1
tbus call netapi init {"data":"$(touch /tmp/nepire)"}

但我们需要的是远程的命令执行,我感觉这个好像用不了,同步了下进度就去看其他有监听端口的进程了
后来有人提醒我,这个进程有网络通信,直接重放命令可以执行,于是我又回来审这流量了

直接重放流量,发现可以成功执行,但重启设备后发现不行
重新抓流量对比发现有8个字节的不同,并且两个疑似相同的key不过一个是大端一个小端,在查看前面的流量中发现在返回包里存在该key,并且请求包的内容是固定的,那么就直接两步获取了,先获取key然后构造命令执行的请求包,写入authorized_keys,这样就能getshell了

由于路由器的www目录不能直接改,所以用mount把利用成功页面挂上去,至此完成整个利用