On Thu, Aug 21, 2025 at 22:56:20 +0000, Kyle Steere wrote:

> In BusyBox netstat, local users can launch a network application with an
> argv[0] containing ANSI terminal escape sequences, leading to a denial of
> service (terminal locked up) when netstat is used by a victim.
> 
> This patch sanitizes the process name before storing it in the cache,
> replacing any non-printable characters (including escape sequences) with
> '?'.
> 
> CVE-2024-58251: https://nvd.nist.gov/vuln/detail/CVE-2024-58251

This might, pedantically speaking, tick off the CVE as reported, but
the very same process will trigger the exact same problem with ps(1)
&c that sanitizes C0 controls, but doesn't sanitize C1 controls (and
doesn't sanitize the {comm} part at all, btw, even for C0).  I
recently posted about that, but got no feedback:

  https://lists.busybox.net/pipermail/busybox/2025-August/091682.html
  https://lists.busybox.net/pipermail/busybox/2025-August/091683.html

With ps &c you don't even need the argv[0] trickery that you need with
netstat (as neststat only shows argv[0]), just passing the offending
string as an argument is enough.

The attached patch tries to address that issue too.  It's probably
better done with some refactoring, but for now I did it defadvice
style.  It's not very thoroughly tested.

As explained in the second patch description, you cannot make this
completely fool-proof, b/c C1 bytes may be part of UTF-8 encoding for
normal characters.

-uwe
>From 81b33c6975cd9059624837c73fb591150988b316 Mon Sep 17 00:00:00 2001
From: Valery Ushakov <[email protected]>
Date: Thu, 21 Aug 2025 12:31:53 +0000
Subject: [PATCH 1/2] netstat: CVE-2024-58251 - sanitize argv0 for -p

Signed-off-by: Valery Ushakov <[email protected]>
---
 networking/netstat.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/networking/netstat.c b/networking/netstat.c
index 807800a62..d979f6079 100644
--- a/networking/netstat.c
+++ b/networking/netstat.c
@@ -41,6 +41,7 @@
 
 #include "libbb.h"
 #include "inet_common.h"
+#include "unicode.h"
 
 //usage:#define netstat_trivial_usage
 //usage:       "[-"IF_ROUTE("r")"al] [-tuwx] [-en"IF_FEATURE_NETSTAT_WIDE("W")IF_FEATURE_NETSTAT_PRG("p")"]"
@@ -314,9 +315,12 @@ static int FAST_FUNC dir_act(struct recursive_state *state,
 		return FALSE;
 	cmdline_buf[n] = '\0';
 
+	/* don't write process-controlled argv[0] to the user's terminal as-is */
+	const char *argv0base = printable_string(bb_basename(cmdline_buf));
+
 	/* go through all files in /proc/PID/fd and check whether they are sockets */
 	strcpy(proc_pid_fname + len - (sizeof("cmdline")-1), "fd");
-	pid_slash_progname = concat_path_file(pid, bb_basename(cmdline_buf)); /* "PID/argv0" */
+	pid_slash_progname = concat_path_file(pid, argv0base); /* "PID/argv0" */
 	n = recursive_action(proc_pid_fname,
 			ACTION_RECURSE | ACTION_QUIET,
 			add_to_prg_cache_if_socket,
@@ -686,6 +690,7 @@ int netstat_main(int argc UNUSED_PARAM, char **argv)
 	unsigned opt;
 
 	INIT_G();
+	init_unicode();
 
 	/* Option string must match NETSTAT_xxx constants */
 	opt = getopt32(argv, NETSTAT_OPTS);
-- 
2.34.1

>From 683f1ad81823800271d6f000e3e8b2155a3e386a Mon Sep 17 00:00:00 2001
From: Valery Ushakov <[email protected]>
Date: Fri, 22 Aug 2025 10:52:30 +0000
Subject: [PATCH 2/2] read_cmdline: wrap in printable_string

This is the same issue as CVE-2024-58251 for netstat -p vs. control
characters in argv0.  read_cmdline() had rudimentary sanitation code
that masked C0 controls, but there are C1 controls too and gnome
terminal seems to always interpret them, and xterm interprets them in
non-utf-8 mode.  So wrap read_cmdline() in a piece of advice that
applies printable_string to the buffer and add init_unicode() calls to
ps(1), top(1) and pmap(1) that call read_cmdline().

Note that this is not _completely_ fool-proof as you may have C1 bytes
as part of valid UTF-8 sequences for characters outside C1, e.g.
\u3add0;Gotcha\u3adc is a valid UTF-8 string, but it also a valid OSC
sequence _if_ interpreted as a single-byte 8-bit string.  procps-ng
will pass that kind of sequence through as well.  But this is,
strictly speaking, a misconfiguration, and you are supposed to use
Ubik's self-winding Swiss chromium never-ending blade only as
directed, and with caution.

Signed-off-by: Valery Ushakov <[email protected]>
---
 libbb/procps.c | 16 ++++++++++++++--
 procps/pmap.c  |  3 +++
 procps/ps.c    |  2 ++
 procps/top.c   |  2 ++
 4 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/libbb/procps.c b/libbb/procps.c
index f56b71b21..f8aae9af0 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -566,7 +566,7 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags)
 	return sp;
 }
 
-void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
+static void FAST_FUNC read_cmdline2(char *buf, int col, unsigned pid, const char *comm)
 {
 	int sz;
 	char filename[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
@@ -584,7 +584,8 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
 		strchrnul(buf, ' ')[0] = '\0';
 		base = bb_basename(buf); /* before we replace argv0's NUL with space */
 		while (sz >= 0) {
-			if ((unsigned char)(buf[sz]) < ' ')
+			/* controls will be dealt with by printable_string */
+			if (buf[sz] == '\0')
 				buf[sz] = ' ';
 			sz--;
 		}
@@ -618,6 +619,17 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
 	}
 }
 
+// defadvice (read_cmdline :after)
+void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
+{
+	read_cmdline2(buf, col, pid, comm);
+	char *printable = printable_string(buf);
+	if (printable == buf)
+		return;
+
+	snprintf(buf, col, "%s", printable);
+}
+
 /* from kernel:
 	//             pid comm S ppid pgid sid tty_nr tty_pgrp flg
 	sprintf(buffer,"%d (%s) %c %d  %d   %d  %d     %d       %lu %lu \
diff --git a/procps/pmap.c b/procps/pmap.c
index 49f7688d9..3b367d34c 100644
--- a/procps/pmap.c
+++ b/procps/pmap.c
@@ -26,6 +26,7 @@
 //usage:     "\n	-q	Quiet"
 
 #include "libbb.h"
+#include "unicode.h"
 
 #if ULLONG_MAX == 0xffffffff
 # define TABS "\t"
@@ -96,6 +97,8 @@ int pmap_main(int argc UNUSED_PARAM, char **argv)
 	unsigned opts;
 	int ret;
 
+	init_unicode();
+
 	opts = getopt32(argv, "^" "xq" "\0" "-1"); /* min one arg */
 	argv += optind;
 
diff --git a/procps/ps.c b/procps/ps.c
index 5b521aebd..f184f25ac 100644
--- a/procps/ps.c
+++ b/procps/ps.c
@@ -109,6 +109,7 @@
 //usage:       " 2990 andersen andersen R ps\n"
 
 #include "libbb.h"
+#include "unicode.h"
 #include "common_bufsiz.h"
 #ifdef __linux__
 # include <sys/sysinfo.h>
@@ -576,6 +577,7 @@ int ps_main(int argc UNUSED_PARAM, char **argv)
 	G.kernel_HZ = bb_clk_tck(); /* this is sysconf(_SC_CLK_TCK) */
 # endif
 #endif
+	init_unicode();
 
 	// POSIX:
 	// -a  Write information for all processes associated with terminals
diff --git a/procps/top.c b/procps/top.c
index 09d31c673..64acbafcb 100644
--- a/procps/top.c
+++ b/procps/top.c
@@ -116,6 +116,7 @@
 //kbuild:lib-$(CONFIG_TOP) += top.o
 
 #include "libbb.h"
+#include "unicode.h"
 
 #define ESC "\033"
 
@@ -1160,6 +1161,7 @@ int top_main(int argc UNUSED_PARAM, char **argv)
 	unsigned scan_mask = TOP_MASK;
 
 	INIT_G();
+	init_unicode();
 
 	interval = 5; /* default update interval is 5 seconds */
 	iterations = 0; /* infinite */
-- 
2.34.1

_______________________________________________
busybox mailing list
[email protected]
https://lists.busybox.net/mailman/listinfo/busybox

Reply via email to