openwrt-rpcd服务ACL配置错误风险分析

Author Avatar
hac425 5月 18, 2018
  • 在其它设备中阅读本文章

前言

openwrt 是一个用于的 路由器 的开源系统。

其他类似的路由器系统相比它的更新速度非常的快,可以看看 github 的更新速度

1
https://github.com/openwrt/openwrt

感觉以后用到 openwrt 的路由器会越来越多,而且 openwrt 可以直接用 vmware 来运行,也减少了学习的成本。

本文介绍一下 openwrtubus 机制 以及怎么利用 rpcd 通过 http 来访问 openwrt 里面的 ubus. 最后以一个 cve 为例介绍 rpcd 配置过程失误导致的安全问题。

Openwrt Helloworld

编译

首先安装好需要的包

1
2
3
sudo apt-get update
sudo apt-get install subversion git-core
sudo apt-get install gcc g++ binutils patch bzip2 flex bison make autoconf gettext textinfo unzip sharutils gawk ncurses-term zliblg-dev libncurses5-dev

然后去 github 下载源码包 (比较大,建议挂 ss 下载)

1
git clone git://git.openwrt.org/openwrt.git

更新编译依赖的包

1
2
scripts/feeds update -a
scripts/feeds install –a

然后通过

1
make menuconfig

配置一下编译参数,使他生成 vmdk 镜像文件

1
2
3
Tartget Images
[*] Build VMware image files (VMDK)
[*] ext4 --->

然后编译即可

1
make -j4 V=99

最后会在 bin/targets/x86/generic/ 目录下生成编译好的文件

paste image

我们使用 openwrt-x86-generic-combined-ext4.vmdk 即可。

挂载到vmware

然后新建一个 虚拟机, 选择目标操作系统为 其他 linux 64 位 的即可,然后后面选择硬盘时,使用刚刚生成的硬盘。

paste image

然后开机启动即可。

添加了那么多个网卡的原因是我这里前面几个网卡都是不能正常联网的,后面的就可以正常了~~~

这时不出意外,应该可以进入 shell 了。

基本配置

首先修改一下 root 的密码,方便 ssh 访问

1
passwd root

然后安装一个 web 界面,用来方便的管理路由器

1
2
opkg update
opkg install luci

然后启动 uhttpd

1
2
/etc/init.d/uhttpd start
/etc/init.d/uhttpd enable

这时由于防火墙的原因可能无法访问,由于是虚拟机,这里直接清空了 防火墙的规则

1
iptables -F

然后在浏览器上访问

1
http://server_ip/

paste image

ubus

介绍

ubusopenwrt 引入的一个消息总线,主要作用是实现不同应用程序之间的信息交互。

ubus 启动后会在后台运行 ubusd 进程,该进程监听一个 unix 套接字用于与其他应用程序通信。

其他应用程序可基于 libubox 提供的接口(或自己实现)与其通信。

paste image

用户可以使用 ubus 的命令行工具和 ubus 总线进行通信, 也可以使用 libubus.so 里面的 api 来和 ubus总线交互, 利用 uhttpd 的模块 和 rpcd 还可以实现通过 http 来访问 ubus 总线

如下图所示:

paste image

使用命令行工具与ubus交互

命令行工具的命令就是 ubus , 列举一下常用的选项

list

不加参数的话会列举出总线上所有的可用的命名空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@OpenWrt:~# ubus list
dhcp
dnsmasq
foo
log
network
network.device
network.interface
network.interface.lan
network.interface.loopback
network.interface.wan
network.interface.wan6
network.rrdns
network.wireless
service
session
system
uci

使用 -v 选项可以列举出 命名空间内方法的详细信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
root@OpenWrt:~# ubus -v list
'dhcp' @f236b80a
"ipv4leases":{}
"ipv6leases":{}
'dnsmasq' @a7e0fefe
'foo' @224fa516
"bar":{"arg1":"Boolean","arg2":"Integer","arg3":"String"}
"toto":{}
'log' @fb3f988f
"read":{"lines":"Integer","stream":"Boolean","oneshot":"Boolean"}
"write":{"event":"String"}
'network' @58aebe07
"restart":{}
"reload":{}
"add_host_route":{"target":"String","v6":"Boolean","interface":"String"}
"get_proto_handlers":{}
"add_dynamic":{"name":"String"}
...............................................................
...............................................................
...............................................................

后面还可以加上命名空间的名字,使得只打印该命名空间内部的方法信息

1
2
3
4
5
root@OpenWrt:~# ubus -v list network.device
'network.device' @61cffe58
"status":{"name":"String"}
"set_alias":{"alias":"Array","device":"String"}
"set_state":{"name":"String","defer":"Boolean"}

这里打印了 network.device 命名空间内的方法的信息。可以看到它有三个方法可以被调用。

call

使用 call 选项就可以调用指定命名空间的方法了。

1
2
3
4
5
6
7
8
root@OpenWrt:~# ubus call network.device status '{"name":"eth1"}'
{
"external": false,
"present": true,
"type": "Network device",
"up": true,
"carrier": true,
"link-advertising": [

调用 network.devicestatus 方法,参数通过 json 数据格式传递,name 指定设备名 ,这里的作用就是获取 eth1 的设备信息。

paste image

一些 openwrt 内置的命名空间以及他们的参数说明信息 , 可以看 openwrt 的官方 wiki, 其他更多的命令信息也可以查看这个 wiki

1
https://wiki.openwrt.org/zh-cn/doc/techref/ubus?s[]=ubus

通过http与ubus交互

配置 uhttpd

首先需要修改 uhttpd 的配置文件, 使得 uhttpd 支持 ubus_prefix

修改 /etc/init.d/uhttpd

1
2
3
4
5
start_instance() {
...
append_arg "$cfg" ubus_prefix "-u"
...
}

修改 /etc/config/uhttpd

1
2
3
...
option ubus_prefix /ubus
...

然后重启一下 uhttpd

1
/etc/init.d/uhttpd restart

之后可以通过

1
http://server_ip/ubus

ubus 交互(使用 post 请求)

rpcd配置

通过 http 来与 ubus 总线进行交互是通过 rpcd 进行的。

rpcd 是有访问控制措施的。

其中访问控制配置文件位于 /usr/share/rpcd/acl.d 目录下

1
2
root@OpenWrt:/usr/share/rpcd/acl.d# ls
hac425.json luci-base.json unauthenticated.json

hac425.json 是我后面新建的,内容为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
> {
> "hac425": {
> "description": "acl for hac425",
> "read": {
> "ubus": {
> "file": [ "*" ],
> "log": [ "*" ],
> "service": [ "*" ],
> },
> },
> "write": {
> "ubus": {
> "file": [ "*" ],
> "log": [ "*" ],
> "service": [ "*" ],
> },
> }
> }
> }
>

>

这个定义就是允许 hac425 使用 file, log, service 命名空间下的所有方法。

需要为其他用户增加 ACL ,可以根据官方提供的配置文件 demo 进行修改。

这个目录下的文件名是没有作用的,起作用的是 json 文件里面的顶层元素,用于定义 访问控制针对的用户的用户名

unauthenticated.json 为例

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"unauthenticated": {
"description": "Access controls for unauthenticated requests",
"read": {
"ubus": {
"session": [
"access",
"login"
]
}
}
}
}

这个文件定义了没有登录的用户(就是所有人)都可以使用的方法是

1
session 命名空间里面的 access, login 方法

调用 login 进行登录后会获得一个 ubus_rpc_session, 用于后面的 ubus 调用

1
curl -d '{ "jsonrpc": "2.0", "id": 1, "method": "call", "params": [ "00000000000000000000000000000000", "session", "login", { "username": "hac425", "password": "123" } ] }' http://192.168.31.111/ubus

如果用户名密码正确的话会返回 (默认的用户名和密码是 root 的用户名和密码, 后面介绍怎么修改)

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
{
"jsonrpc":"2.0",
"id":1,
"result":[
0,
{
"ubus_rpc_session":"749e6006d3be1149e8ef23c7dd6cb7ac",
"timeout":300,
"expires":300,
"acls":{
"access-group":{
"hac425":[
"read",
"write"
],
"uci-access":[
"read",
"write"
],
"unauthenticated":[
"read"
]
},
"ubus":{
"file":[
"*"
],
"log":[
"*"
],
"service":[
"*"
],
"session":[
"access",
"login"
]
},
"uci":{
"*":[
"read",
"write"
]
}
},
"data":{
"username":"hac425"
}
}
]
}

里面会有一个 ubus_rpc_session 用于调用那些需要登录后才能调用的方法, 同时会显示所有可以通过 http 调用的方法。

用于 rpcd 认证的用户名和密码在 /etc/config/rpcd 设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> root@OpenWrt:~# cat /etc/config/rpcd
>
> config login
> option username 'hac425'
> option password '$p$hac425'
> list read '*'
> list write '*'
> config login
> option username 'test'
> option password '$p$test'
> list read '*'
> list write '*'
>
>
>

>

$p$test 表示使用用户名为 test 的系统用户的密码作为 rpc 认证的密码

所以需要增加一个用户,只需新建一个系统用户,然后增加一个 表项即可

1
2
3
4
5
6
> config login
> option username 'xxxxx'
> option password '$p$xxxxx'
> list read '*'
> list write '*'
>

获取到 ubus_rpc_session 后,就可以调用在 /usr/share/rpcd/acl.d 目录下设置好的允许调用的方法了。调用方法时用到的 json 数据的格式如下

1
2
3
4
5
6
7
8
9
{
"jsonrpc": "2.0",
"id": <unique-id-to-identify-request>,
"method": "call",
"params": [
<ubus_rpc_session>, <ubus_object>, <ubus_method>,
{ <ubus_arguments> }
]
}

首先使用 list 显示方法的详细信息

1
curl -d '{"jsonrpc":"2.0","method":"list","params":["749e6006d3be1149e8ef23c7dd6cb7ac","*"],"id":1}' http://192.168.31.111/ubus

返回结果比较多,截个图看看

paste image

打印了方法的参数信息。

接下来可以调用需要的方法,比如调用 file 命名空间内的 read 方法 读取 /etc/passwd 的文件内容

1
2
07:36 haclh@ubuntu:~ $ curl -d '{ "jsonrpc": "2.0", "id": 1, "method": "call", "params": [ "749e6006d3be1149e8ef23c7dd6cb7ac", "file", "read", { "path": "/etc/passwd" } ] }' http://192.168.31.111/ubus
{"jsonrpc":"2.0","id":1,"result":[0,{"data":"root:x:0:0:root:\/root:\/bin\/ash\ndaemon:*:1:1:daemon:\/var:\/bin\/false\nftp:*:55:55:ftp:\/home\/ftp:\/bin\/false\nnetwork:*:101:101:network:\/var:\/bin\/false\nnobody:*:65534:65534:nobody:\/var:\/bin\/false\ndnsmasq:x:453:453:dnsmasq:\/var\/run\/dnsmasq:\/bin\/false\nhac425:x:1000:1000::\/home\/hac425:\ntest:x:1001:1001::\/home\/test:\n"}]}

安全风险

ACL配置问题简述

rpcd 用于 rpc 调用,那么访问控制就非常重要。

rpcd 的配置就有个很大的坑,使用 官网 wiki 上的配置,来配置用户的 ACL ,就会发现 ACL 不成功的情况,而且也没找到可以正常 配置 ACL 的方法 ~_~….

以上面的例子为例, hac425 设置了 ACL ,允许其调用 很多方法

test 用户没有配置 ACL 那么他应该只能调用非常有限的方法比如(session 中的 login

但是实际测试发现 , test 也可以调用 允许 hac425 调用的方法,比如 file 命名空间内的 read 方法。

paste image

由于一些错误配置,就会导致很多不该提供给用户调用的 方法被暴露出来,可能会造成一些问题(比如暴露 file 命名空间,就可能造成文件泄露等)。

CVE-2017-11361分析

这个漏洞是由于 对 rpcd 的错误配置(感觉是使用了官方的配置导致配置失效),导致管理 web 界面的普通用户可以通过 rpcd 暴露的大量方法,实现代码执行。(由于最新版的 juci 已经不使用 rpcd ,所以没有搭建复现环境)

下面根据漏洞作者的博客,简述下漏洞发现流程。

首先通过抓包,发现有 rpc 调用的登录包

1
{"jsonrpc":"2.0","method":"call","params":["00000000000000000000000000000000","session","login",{"username":"user","password":"testing"}],"id":0}

然后返回了大量的可以被调用的方法。

1
<{"jsonrpc":"2.0","id":0,"result":[0,{"ubus_rpc_session":"eb3bd8e7beb01338b21533a89b678971","timeout":300,"expires":299,"acls":{"access-group":{"core":["read"],"juci-broadcom-dsl":["read"],"juci-broadcom-dsl-admin":["write"],"juci-broadcom-ethernet":["read","write"],"juci-broadcom-iptv":["read","write"],"juci-broadcom-vlan":["read"],"juci-broadcom-vlan-admin":["write"],"juci-ddns":["read","write"],"juci-diagnostics":["read","write"],"juci-dnsmasq-dhcp":["read","write"],"juci-dropbear":["read","write"],"juci-ethernet":["read"],"juci-event":["read"],"juci-firewall-fw3":["read","write"],"juci-igmpinfo":["read"],"juci-inteno-backup":["read","write"],"juci-inteno-provisioning":["read","write"],"juci-inteno-qos":["read","write"],"juci-inteno-voice-client":["read","write"],"juci-minidlna":["read","write"],"juci-mod-status":["read"],"juci-mod-system":["read","write"],"juci-natalie-dect":["read","write"],"juci-netmode":["read","write"],"juci-network-netifd":["read","write"],"juci-owsd":["read","write"],"juci-printer":["read","write"],"juci-realtime-graphs":["read"],"juci-samba":["read","write"],"juci-snmpd":["rea...............................

其中有可以用于文件读写的 readwrite 方法,以及可以增加 ssh_keyrouter.dropbear:add_ssh_key 方法,组合起来就可以写 ssh_key , 然后 ssh 连过去。

CVE-2018-10123 分析

这个漏洞利用的是 p910nd 打印服务对配置文件信息没做校验,利用 rpcd 暴露的 uci 命名空间内的方法来写配置文件 get root shell

首先看看 p910nd 打印机服务,官网 wiki

安装

1
2
opkg update
opkg install p910nd

此时默认配置文件内容为

1
2
3
4
5
6
7
root@OpenWrt:/usr/share/rpcd/acl.d# uci show p910nd
p910nd.@p910nd[0]=p910nd
p910nd.@p910nd[0].device='/dev/usb/lp0'
p910nd.@p910nd[0].port='0'
p910nd.@p910nd[0].bidirectional='1'
p910nd.@p910nd[0].enabled='0'
root@OpenWrt:/usr/share/rpcd/acl.d#

然后设置 enabled 选项为 1, 开启 p910nd 服务

1
2
3
4
5
6
7
8
9
10
root@OpenWrt:/usr/share/rpcd/acl.d# uci set p910nd.@p910nd[0].enabled=1
root@OpenWrt:/usr/share/rpcd/acl.d# uci show p910nd
p910nd.@p910nd[0]=p910nd
p910nd.@p910nd[0].device='/dev/usb/lp0'
p910nd.@p910nd[0].port='0'
p910nd.@p910nd[0].bidirectional='1'
p910nd.@p910nd[0].enabled='1'
root@OpenWrt:/usr/share/rpcd/acl.d# /etc/init.d/p910nd restart
root@OpenWrt:/usr/share/rpcd/acl.d# netstat -an| grep 9100
tcp 0 0 0.0.0.0:9100 0.0.0.0:* LISTEN

此时可以用 nc 连接 9100 端口和 打印机通信。

如果我们修改 p910nd.@p910nd[0].device 为 一个文件的路径,我们在 nc 连过去的时候就可以获取文件的内容,同时在文件末尾追加内容。

openwrt 新建一个文件做测试,同时设置 device 指向它,并重启服务,使修改生效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@OpenWrt:/usr/share/rpcd/acl.d# echo this_line_1 >/tmp/xx
root@OpenWrt:/usr/share/rpcd/acl.d# cat /tmp/xx
this_line_1
root@OpenWrt:/usr/share/rpcd/acl.d# uci set p910nd.@p910nd[0].device=/tmp/xx
root@OpenWrt:/usr/share/rpcd/acl.d# uci show p910nd
p910nd.@p910nd[0]=p910nd
p910nd.@p910nd[0].port='0'
p910nd.@p910nd[0].bidirectional='1'
p910nd.@p910nd[0].enabled='1'
p910nd.@p910nd[0].device='/tmp/xx'
root@OpenWrt:/usr/share/rpcd/acl.d# /etc/init.d/p910nd restart
root@OpenWrt:/usr/share/rpcd/acl.d# netstat -an| grep 9100
tcp 0 0 0.0.0.0:9100 0.0.0.0:* LISTEN
root@OpenWrt:/usr/share/rpcd/acl.d#

然后 nc 过去就可以获取 /tmp/xx 文件的内容,此时再输入内容,然后终止连接,输入内容就会写入输入的数据到文件末尾.

输入内容

1
2
3
4
5
05:36 haclh@ubuntu:~ $ nc 192.168.31.111 9100
this_line_1
line2_writed_by_nc
^C
06:25 haclh@ubuntu:~ $

查看修改,可以看到内容成功被修改

1
2
3
root@OpenWrt:/usr/share/rpcd/acl.d# cat /tmp/xx
this_line_1
line2_writed_by_nc

这个 cve 是由于在用户登录后可以开启 p910nd 服务, 同时 我们可以访问 uci 命名空间中的一些方法,然后利用 rpcd 服务修改配置文件,就可以实现任意文件读写。

最后的利用方式是在 /etc/init.d/p910nd 追加了一句写 ssh key 的代码,然后在以后执行 /etc/init.d/p910nd 时就可以写入 ssh_key.

总结

对于 rpcd 要严格限制暴露出来的方法, 避免出现严重的问题

总结

对于 rpcd 要限制暴露出来的方法, 避免出现严重的问题

参考

http://www.cnblogs.com/nicephil/p/6768381.html#e4bdbfe794a8e696b9e6b395_2

https://neonsea.uk/blog/2017/07/17/cve-2017-11361.html

https://neonsea.uk/blog/2018/04/15/pwn910nd.html

本站文章均原创, 转载注明来源
本文链接:http://blog.hac425.top/2018/05/18/openwrt_rpcd_misconfig.html