1. 程式人生 > >openwrt-rpcd服務ACL配置錯誤風險分析

openwrt-rpcd服務ACL配置錯誤風險分析

接下來 param 路由 消息 qos 如果 usb restart blog

前言

openwrt 是一個用於的 路由器 的開源系統。

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

https://github.com/openwrt/openwrt

感覺以後用到 openwrt 的路由器會越來越多,而且 openwrt 可以直接用 vmware 來運行,也減少了學習的成本。

本文介紹一下 openwrtubus 機制 以及怎麽利用 rpcd 通過 http 來訪問 openwrt 裏面的 ubus. 最後以一個 cve 為例介紹 rpcd 配置過程失誤導致的安全問題。

Openwrt Helloworld

編譯

首先安裝好需要的包

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 下載)

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

更新編譯依賴的包

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

然後通過

make menuconfig

配置一下編譯參數,使他生成 vmdk 鏡像文件

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

然後編譯即可

make -j4 V=99

最後會在 bin/targets/x86/generic/

目錄下生成編譯好的文件

技術分享圖片

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

掛載到vmware

然後新建一個 虛擬機, 選擇目標操作系統為 其他 linux 64 位 的即可,然後後面選擇硬盤時,使用剛剛生成的硬盤。

技術分享圖片

然後開機啟動即可。

添加了那麽多個網卡的原因是我這裏前面幾個網卡都是不能正常聯網的,後面的就可以正常了~~~

這時不出意外,應該可以進入 shell 了。

基本配置

首先修改一下 root 的密碼,方便 ssh 訪問

passwd root

然後安裝一個 web 界面,用來方便的管理路由器

opkg update
opkg install luci

然後啟動 uhttpd

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

這時由於防火墻的原因可能無法訪問,由於是虛擬機,這裏直接清空了 防火墻的規則

iptables -F

然後在瀏覽器上訪問

http://server_ip/

技術分享圖片

ubus

介紹

ubusopenwrt 引入的一個消息總線,主要作用是實現不同應用程序之間的信息交互。

ubus 啟動後會在後臺運行 ubusd 進程,該進程監聽一個 unix 套接字用於與其他應用程序通信。

其他應用程序可基於 libubox 提供的接口(或自己實現)與其通信。

技術分享圖片

用戶可以使用 ubus 的命令行工具和 ubus 總線進行通信, 也可以使用 libubus.so 裏面的 api 來和 ubus總線交互, 利用 uhttpd 的模塊 和 rpcd 還可以實現通過 http 來訪問 ubus 總線

如下圖所示:

技術分享圖片

使用命令行工具與ubus交互

命令行工具的命令就是 ubus , 列舉一下常用的選項

list

不加參數的話會列舉出總線上所有的可用的命名空間。

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 選項可以列舉出 命名空間內方法的詳細信息

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"}
...............................................................
...............................................................
...............................................................

後面還可以加上命名空間的名字,使得只打印該命名空間內部的方法信息

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 選項就可以調用指定命名空間的方法了。

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 的設備信息。

技術分享圖片

一些 openwrt 內置的命名空間以及他們的參數說明信息 , 可以看 openwrt 的官方 wiki, 其他更多的命令信息也可以查看這個 wiki

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

通過http與ubus交互

配置 uhttpd

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

修改 /etc/init.d/uhttpd

start_instance() { 
... 
 append_arg "$cfg" ubus_prefix "-u" 
... 
} 

修改 /etc/config/uhttpd

... 
    option ubus_prefix /ubus 
...

然後重啟一下 uhttpd

/etc/init.d/uhttpd restart

之後可以通過

http://server_ip/ubus 

ubus 交互(使用 post 請求)

rpcd配置

通過 http 來與 ubus 總線進行交互是通過 rpcd 進行的。

rpcd 是有訪問控制措施的。

其中訪問控制配置文件位於 /usr/share/rpcd/acl.d 目錄下

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

hac425.json 是我後面新建的,內容為

{
        "hac425": {
                "description": "acl for hac425",
                "read": {
                        "ubus": {
                                "file": [ "*" ],
                                "log": [ "*" ],
                                "service": [ "*" ],
                        },
                },
                "write": {
                        "ubus": {
                                "file": [ "*" ],
                                "log": [ "*" ],
                                "service": [ "*" ],
                        },
                }
        }
}

這個定義就是允許 hac425 使用 file, log, service 命名空間下的所有方法。

需要為其他用戶增加 ACL ,可以根據官方提供的配置文件 demo 進行修改。

這個目錄下的文件名是沒有作用的,起作用的是 json 文件裏面的頂層元素,用於定義 訪問控制針對的用戶的用戶名

unauthenticated.json 為例

{
    "unauthenticated": {
        "description": "Access controls for unauthenticated requests",
        "read": {
            "ubus": {
                "session": [
                    "access",
                    "login"
                ]
            }
        }
    }
}

這個文件定義了沒有登錄的用戶(就是所有人)都可以使用的方法是

session 命名空間裏面的 access, login 方法

調用 login 進行登錄後會獲得一個 ubus_rpc_session, 用於後面的 ubus 調用

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

如果用戶名密碼正確的話會返回 (默認的用戶名和密碼是 root 的用戶名和密碼, 後面介紹怎麽修改)

{
    "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 設置

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 認證的密碼

所以需要增加一個用戶,只需新建一個系統用戶,然後增加一個 表項即可

config login
        option username ‘xxxxx‘
        option password ‘$p$xxxxx‘
        list read ‘*‘
        list write ‘*‘

獲取到 ubus_rpc_session 後,就可以調用在 /usr/share/rpcd/acl.d 目錄下設置好的允許調用的方法了。調用方法時用到的 json 數據的格式如下

{ 
  "jsonrpc": "2.0",
  "id": <unique-id-to-identify-request>, 
  "method": "call",
  "params": [
             <ubus_rpc_session>, <ubus_object>, <ubus_method>, 
             { <ubus_arguments> }
            ]
}

首先使用 list 顯示方法的詳細信息

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

返回結果比較多,截個圖看看

技術分享圖片

打印了方法的參數信息。

接下來可以調用需要的方法,比如調用 file 命名空間內的 read 方法 讀取 /etc/passwd 的文件內容

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 方法。

技術分享圖片

由於一些錯誤配置,就會導致很多不該提供給用戶調用的 方法被暴露出來,可能會造成一些問題(比如暴露 file 命名空間,就可能造成文件泄露等)。

CVE-2017-11361分析

這個漏洞是由於 對 rpcd 的錯誤配置(感覺是使用了官方的配置導致配置失效),導致管理 web 界面的普通用戶可以通過 rpcd 暴露的大量方法,實現代碼執行。(由於最新版的 juci 已經不使用 rpcd ,所以沒有搭建復現環境)

下面根據漏洞作者的博客,簡述下漏洞發現流程。

首先通過抓包,發現有 rpc 調用的登錄包

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

然後返回了大量的可以被調用的方法。

<{"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

安裝

opkg update
opkg install p910nd

此時默認配置文件內容為

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 服務

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 指向它,並重啟服務,使修改生效

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 文件的內容,此時再輸入內容,然後終止連接,輸入內容就會寫入輸入的數據到文件末尾.

輸入內容

05:36 haclh@ubuntu:~ $ nc 192.168.31.111 9100
this_line_1
line2_writed_by_nc
^C
06:25 haclh@ubuntu:~ $ 

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

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

openwrt-rpcd服務ACL配置錯誤風險分析