From 71f9f67970bde09f73812d59cbeafb7093e31f24 Mon Sep 17 00:00:00 2001 From: Alex Burmashev Date: Tue, 27 May 2025 10:47:46 +0000 Subject: [PATCH 2/2] coredump: use %d in kernel core pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kernel provides %d which is documented as "dump mode—same as value returned by prctl(2) PR_GET_DUMPABLE". We already query /proc/pid/auxv for this information, but unfortunately this check is subject to a race, because the crashed process may be replaced by an attacker before we read this data, for example replacing a SUID process that was killed by a signal with another process that is not SUID, tricking us into making the coredump of the original process readable by the attacker. With this patch, we effectively add one more check to the list of conditions that need be satisfied if we are to make the coredump accessible to the user. Reportedy-by: Qualys Security Advisory CVE-2025-4598 Modified-by: Alex Burmashev Signed-off-by: Alex Burmashev --- src/coredump/coredump.c | 20 +++++++++++++++++--- sysctl.d/50-coredump.conf.in | 2 +- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 539bc83..f0b13a5 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -95,6 +95,7 @@ typedef enum { META_ARGV_TIMESTAMP, /* %t: time of dump, expressed as seconds since the Epoch (we expand this to µs granularity) */ META_ARGV_RLIMIT, /* %c: core file size soft resource limit */ META_ARGV_HOSTNAME, /* %h: hostname */ + META_ARGV_DUMPABLE, /* %d: as set by the kernel */ _META_ARGV_MAX, /* The following indexes are cached for a couple of special fields we use (and @@ -120,6 +121,7 @@ static const char * const meta_field_names[_META_MAX] = { [META_ARGV_TIMESTAMP] = "COREDUMP_TIMESTAMP=", [META_ARGV_RLIMIT] = "COREDUMP_RLIMIT=", [META_ARGV_HOSTNAME] = "COREDUMP_HOSTNAME=", + [META_ARGV_DUMPABLE] = "COREDUMP_DUMPABLE=", [META_COMM] = "COREDUMP_COMM=", [META_EXE] = "COREDUMP_EXE=", [META_UNIT] = "COREDUMP_UNIT=", @@ -132,6 +134,7 @@ typedef struct Context { pid_t pid; uid_t uid; gid_t gid; + unsigned dumpable; bool is_pid1; bool is_journald; } Context; @@ -447,14 +450,16 @@ static int grant_user_access(int core_fd, const Context *context) { if (r < 0) return r; - /* We allow access if we got all the data and at_secure is not set and - * the uid/gid matches euid/egid. */ + /* We allow access if dumpable on the command line was exactly 1, we got all the data, + * at_secure is not set, and the uid/gid match euid/egid. */ bool ret = + context->dumpable == 1 && at_secure == 0 && uid != UID_INVALID && euid != UID_INVALID && uid == euid && gid != GID_INVALID && egid != GID_INVALID && gid == egid; - log_debug("Will %s access (uid="UID_FMT " euid="UID_FMT " gid="GID_FMT " egid="GID_FMT " at_secure=%s)", + log_debug("Will %s access (dumpable=%u uid="UID_FMT " euid="UID_FMT " gid="GID_FMT " egid="GID_FMT " at_secure=%s)", ret ? "permit" : "restrict", + context->dumpable, uid, euid, gid, egid, yes_no(at_secure)); return ret; } @@ -1082,6 +1087,15 @@ static int save_context(Context *context, const struct iovec_wrapper *iovw) { return log_error_errno(r, "Failed to parse GID \"%s\": %m", context->meta[META_ARGV_GID]); + /* The value is set to contents of /proc/sys/fs/suid_dumpable, which we set to 2, + * if the process is marked as not dumpable, see PR_SET_DUMPABLE(2const). */ + if (context->meta[META_ARGV_DUMPABLE]) { + r = safe_atou_bounded(context->meta[META_ARGV_DUMPABLE], 0, 2, &context->dumpable); + if (r < 0) + return log_error_errno(r, "Failed to parse dumpable field \"%s\": %m", context->meta[META_ARGV_DUMPABLE]); + assert(context->dumpable <= 2); + } + unit = context->meta[META_UNIT]; context->is_pid1 = streq(context->meta[META_ARGV_PID], "1") || streq_ptr(unit, SPECIAL_INIT_SCOPE); context->is_journald = streq_ptr(unit, SPECIAL_JOURNALD_SERVICE); diff --git a/sysctl.d/50-coredump.conf.in b/sysctl.d/50-coredump.conf.in index 5fb551a..9c10a89 100644 --- a/sysctl.d/50-coredump.conf.in +++ b/sysctl.d/50-coredump.conf.in @@ -13,7 +13,7 @@ # the core dump. # # See systemd-coredump(8) and core(5). -kernel.core_pattern=|{{ROOTLIBEXECDIR}}/systemd-coredump %P %u %g %s %t %c %h +kernel.core_pattern=|{{ROOTLIBEXECDIR}}/systemd-coredump %P %u %g %s %t %c %h %d # Allow 16 coredumps to be dispatched in parallel by the kernel. # We collect metadata from /proc/%P/, and thus need to make sure the crashed -- 2.47.1