Version in base suite: 3.6-4+deb12u1 Base version: needrestart_3.6-4+deb12u1 Target version: needrestart_3.6-4+deb12u2 Base file: /srv/ftp-master.debian.org/ftp/pool/main/n/needrestart/needrestart_3.6-4+deb12u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/n/needrestart/needrestart_3.6-4+deb12u2.dsc changelog | 16 + control | 1 patches/core-prevent-race-condition-on-proc-PID-exec-evaluat.patch | 34 ++ patches/interp-chdir-into-empty-directory-to-prevent-python-.patch | 63 ++++ patches/interp-do-not-set-PYTHONPATH-environment-variable-to.patch | 54 +++ patches/interp-do-not-set-RUBYLIB-environment-variable-to-pr.patch | 76 +++++ patches/interp-drop-usage-of-Module-ScanDeps-to-prevent-LPE.patch | 149 ++++++++++ patches/series | 5 8 files changed, 397 insertions(+), 1 deletion(-) Unrecognised file line in .dsc: -----BEGIN PGP SIGNATURE----- diff -Nru needrestart-3.6/debian/changelog needrestart-3.6/debian/changelog --- needrestart-3.6/debian/changelog 2023-11-15 20:05:37.000000000 +0000 +++ needrestart-3.6/debian/changelog 2024-11-12 19:51:08.000000000 +0000 @@ -1,3 +1,19 @@ +needrestart (3.6-4+deb12u2) bookworm-security; urgency=high + + * Non-maintainer upload by the Security Team. + * Address local privilege escalation vulnerabilities from any unprivileged + user to root (CVE-2024-48990, CVE-2024-48992, CVE-2024-48991, + CVE-2024-11003): + - core: prevent race condition on /proc/$PID/exec evaluation + - interp: do not set PYTHONPATH environment variable to prevent a LPE + - interp: do not set RUBYLIB environment variable to prevent a LPE + - interp: chdir into empty directory to prevent python parsing arbitrary + files + - interp: drop usage of Module::ScanDeps to prevent LPE + * debian/control: Drop Depends on libmodule-scandeps-perl + + -- Salvatore Bonaccorso Tue, 12 Nov 2024 20:51:08 +0100 + needrestart (3.6-4+deb12u1) bookworm; urgency=medium * fix microcode check regression on AMD CPUs (Closes: #1013285) diff -Nru needrestart-3.6/debian/control needrestart-3.6/debian/control --- needrestart-3.6/debian/control 2023-05-31 14:47:03.000000000 +0000 +++ needrestart-3.6/debian/control 2024-11-12 19:50:36.000000000 +0000 @@ -18,7 +18,6 @@ libintl-perl, libproc-processtable-perl, libsort-naturally-perl, - libmodule-scandeps-perl, libterm-readkey-perl, libmodule-find-perl, binutils, diff -Nru needrestart-3.6/debian/patches/core-prevent-race-condition-on-proc-PID-exec-evaluat.patch needrestart-3.6/debian/patches/core-prevent-race-condition-on-proc-PID-exec-evaluat.patch --- needrestart-3.6/debian/patches/core-prevent-race-condition-on-proc-PID-exec-evaluat.patch 1970-01-01 00:00:00.000000000 +0000 +++ needrestart-3.6/debian/patches/core-prevent-race-condition-on-proc-PID-exec-evaluat.patch 2024-11-12 19:40:20.000000000 +0000 @@ -0,0 +1,34 @@ +From 99d954da8122122fa18bce061fcf2fb307135b64 Mon Sep 17 00:00:00 2001 +From: Thomas Liske +Date: Sat, 12 Oct 2024 16:05:30 +0200 +Subject: [PATCH 1/5] core: prevent race condition on /proc/$PID/exec + evaluation + +--- + needrestart | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/needrestart b/needrestart +index cad58c3..d8cb9a5 100755 +--- a/needrestart ++++ b/needrestart +@@ -530,11 +530,16 @@ if(defined($opt_l)) { + # orphaned binary + $restart++ if (defined($exe) && $exe =~ s/ \(deleted\)$//); # Linux + $restart++ if (defined($exe) && $exe =~ s/^\(deleted\)//); # Linux VServer ++ $restart++ unless(defined($ptable->{$pid}->{exec})); + print STDERR "$LOGPREF #$pid uses obsolete binary $exe\n" if($restart && $nrconf{verbosity} > 1); + + # ignore blacklisted binaries + next if(grep { $exe =~ /$_/; } @{$nrconf{blacklist}}); + ++ # Sync $exe with the initial value from Proc:ProcessTable to prevent race ++ # conditions in later checks. ++ $exe = $ptable->{$pid}->{exec} if(defined($ptable->{$pid}->{exec})); ++ + # read file mappings (Linux 2.0+) + unless($restart) { + if(open(HMAP, '<', "/proc/$pid/maps")) { +-- +2.39.5 + diff -Nru needrestart-3.6/debian/patches/interp-chdir-into-empty-directory-to-prevent-python-.patch needrestart-3.6/debian/patches/interp-chdir-into-empty-directory-to-prevent-python-.patch --- needrestart-3.6/debian/patches/interp-chdir-into-empty-directory-to-prevent-python-.patch 1970-01-01 00:00:00.000000000 +0000 +++ needrestart-3.6/debian/patches/interp-chdir-into-empty-directory-to-prevent-python-.patch 2024-11-12 19:43:14.000000000 +0000 @@ -0,0 +1,63 @@ +From 4f78b080b4cb51b3d3ea4453333ef83ebdc3590e Mon Sep 17 00:00:00 2001 +From: Thomas Liske +Date: Sun, 3 Nov 2024 19:50:31 +0100 +Subject: [PATCH 4/5] interp: chdir into empty directory to prevent python + parsing arbitrary files + +--- + perl/lib/NeedRestart/Interp/Python.pm | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/perl/lib/NeedRestart/Interp/Python.pm ++++ b/perl/lib/NeedRestart/Interp/Python.pm +@@ -29,11 +29,13 @@ use warnings; + + use parent qw(NeedRestart::Interp); + use Cwd qw(abs_path getcwd); ++use File::Temp qw(tempdir); + use Getopt::Std; + use NeedRestart qw(:interp); + use NeedRestart::Utils; + + my $LOGPREF = '[Python]'; ++my $empty_dir; + + needrestart_interp_register(__PACKAGE__); + +@@ -79,6 +81,14 @@ sub _scan($$$$$) { + } + } + ++# chdir into empty directory to prevent python parsing arbitrary files ++sub chdir_empty() { ++ unless(defined($empty_dir)) { ++ $empty_dir = tempdir(CLEANUP => 1); ++ } ++ chdir($empty_dir); ++} ++ + sub source { + my $self = shift; + my $pid = shift; +@@ -185,6 +195,7 @@ sub files { + + # use cached data if avail + if(exists($cache->{files}->{(__PACKAGE__)}->{$src})) { ++ chdir($cwd); + print STDERR "$LOGPREF #$pid: use cached file list\n" if($self->{debug}); + return %{ $cache->{files}->{(__PACKAGE__)}->{$src} }; + } +@@ -200,11 +211,13 @@ sub files { + } + + # get include path from sys.path ++ chdir_empty(); + my ($pyread, $pywrite) = nr_fork_pipe2($self->{debug}, $ptable->{exec}, '-'); + print $pywrite "import sys\nprint(sys.path)\n"; + close($pywrite); + my ($path) = <$pyread>; + close($pyread); ++ chdir("/proc/$pid/root/$ptable->{cwd}"); + + # look for module source files + if(defined($path)) { diff -Nru needrestart-3.6/debian/patches/interp-do-not-set-PYTHONPATH-environment-variable-to.patch needrestart-3.6/debian/patches/interp-do-not-set-PYTHONPATH-environment-variable-to.patch --- needrestart-3.6/debian/patches/interp-do-not-set-PYTHONPATH-environment-variable-to.patch 1970-01-01 00:00:00.000000000 +0000 +++ needrestart-3.6/debian/patches/interp-do-not-set-PYTHONPATH-environment-variable-to.patch 2024-11-12 19:41:11.000000000 +0000 @@ -0,0 +1,54 @@ +From 4c1fa7037c54224cef1e077c5ebdc7f2cfc83799 Mon Sep 17 00:00:00 2001 +From: Thomas Liske +Date: Sat, 12 Oct 2024 18:57:39 +0200 +Subject: [PATCH 2/5] interp: do not set PYTHONPATH environment variable to + prevent a LPE + +--- + perl/lib/NeedRestart/Interp/Python.pm | 15 +++++++-------- + 1 file changed, 7 insertions(+), 8 deletions(-) + +diff --git a/perl/lib/NeedRestart/Interp/Python.pm b/perl/lib/NeedRestart/Interp/Python.pm +index 097ac05..7a7002e 100644 +--- a/perl/lib/NeedRestart/Interp/Python.pm ++++ b/perl/lib/NeedRestart/Interp/Python.pm +@@ -190,16 +190,16 @@ sub files { + } + + # prepare include path environment variable +- my %e = nr_parse_env($pid); ++ my @path; + local %ENV; ++ ++ # get include path from env ++ my %e = nr_parse_env($pid); + if(exists($e{PYTHONPATH})) { +- $ENV{PYTHONPATH} = $e{PYTHONPATH}; +- } +- elsif(exists($ENV{PYTHONPATH})) { +- delete($ENV{PYTHONPATH}); ++ @path = map { "/proc/$pid/root/$_"; } split(':', $e{PYTHONPATH}); + } + +- # get include path ++ # get include path from sys.path + my ($pyread, $pywrite) = nr_fork_pipe2($self->{debug}, $ptable->{exec}, '-'); + print $pywrite "import sys\nprint(sys.path)\n"; + close($pywrite); +@@ -207,12 +207,11 @@ sub files { + close($pyread); + + # look for module source files +- my @path; + if(defined($path)) { + chomp($path); + $path =~ s/^\['//; + $path =~ s/'\$//; +- @path = map { "/proc/$pid/root/$_"; } split("', '", $path); ++ push(@path, map { "/proc/$pid/root/$_"; } split("', '", $path)); + } + else { + print STDERR "$LOGPREF #$pid: failed to retrieve include path\n" if($self->{debug}); +-- +2.39.5 + diff -Nru needrestart-3.6/debian/patches/interp-do-not-set-RUBYLIB-environment-variable-to-pr.patch needrestart-3.6/debian/patches/interp-do-not-set-RUBYLIB-environment-variable-to-pr.patch --- needrestart-3.6/debian/patches/interp-do-not-set-RUBYLIB-environment-variable-to-pr.patch 1970-01-01 00:00:00.000000000 +0000 +++ needrestart-3.6/debian/patches/interp-do-not-set-RUBYLIB-environment-variable-to-pr.patch 2024-11-12 19:42:34.000000000 +0000 @@ -0,0 +1,76 @@ +From 840b750f77ff67f950528e87e87045cfa724d1eb Mon Sep 17 00:00:00 2001 +From: Thomas Liske +Date: Sun, 3 Nov 2024 20:00:03 +0100 +Subject: [PATCH 3/5] interp: do not set RUBYLIB environment variable to + prevent a LPE + +--- + perl/lib/NeedRestart/Interp/Ruby.pm | 25 +++++++++++++++++++------ + 1 file changed, 19 insertions(+), 6 deletions(-) + +--- a/perl/lib/NeedRestart/Interp/Ruby.pm ++++ b/perl/lib/NeedRestart/Interp/Ruby.pm +@@ -29,11 +29,13 @@ use warnings; + + use parent qw(NeedRestart::Interp); + use Cwd qw(abs_path getcwd); ++use File::Temp qw(tempdir); + use Getopt::Std; + use NeedRestart qw(:interp); + use NeedRestart::Utils; + + my $LOGPREF = '[Ruby]'; ++my $empty_dir; + + needrestart_interp_register(__PACKAGE__); + +@@ -76,6 +78,14 @@ sub _scan($$$$$) { + } + } + ++# chdir into empty directory to prevent ruby parsing arbitrary files ++sub chdir_empty() { ++ unless(defined($empty_dir)) { ++ $empty_dir = tempdir(CLEANUP => 1); ++ } ++ chdir($empty_dir); ++} ++ + sub source { + my $self = shift; + my $pid = shift; +@@ -182,25 +192,28 @@ sub files { + + # use cached data if avail + if(exists($cache->{files}->{(__PACKAGE__)}->{$src})) { ++ chdir($cwd); + print STDERR "$LOGPREF #$pid: use cached file list\n" if($self->{debug}); + return %{ $cache->{files}->{(__PACKAGE__)}->{$src} }; + } + + # prepare include path environment variable +- my %e = nr_parse_env($pid); ++ my @path; + local %ENV; ++ ++ # get include path from env ++ my %e = nr_parse_env($pid); + if(exists($e{RUBYLIB})) { +- $ENV{RUBYLIB} = $e{RUBYLIB}; +- } +- elsif(exists($ENV{RUBYLIB})) { +- delete($ENV{RUBYLIB}); ++ @path = map { "/proc/$pid/root/$_"; } split(':', $e{RUBYLIB}); + } + + # get include path ++ chdir_empty(); + my $rbread = nr_fork_pipe($self->{debug}, $ptable->{exec}, '-e', 'puts $:'); +- my @path = map { "/proc/$pid/root/$_"; } <$rbread>; ++ push(@path, map { "/proc/$pid/root/$_"; } <$rbread>); + close($rbread); + chomp(@path); ++ chdir("/proc/$pid/root/$ptable->{cwd}"); + + my %files; + _scan($self->{debug}, $pid, $src, \%files, \@path); diff -Nru needrestart-3.6/debian/patches/interp-drop-usage-of-Module-ScanDeps-to-prevent-LPE.patch needrestart-3.6/debian/patches/interp-drop-usage-of-Module-ScanDeps-to-prevent-LPE.patch --- needrestart-3.6/debian/patches/interp-drop-usage-of-Module-ScanDeps-to-prevent-LPE.patch 1970-01-01 00:00:00.000000000 +0000 +++ needrestart-3.6/debian/patches/interp-drop-usage-of-Module-ScanDeps-to-prevent-LPE.patch 2024-11-12 19:49:30.000000000 +0000 @@ -0,0 +1,149 @@ +From d136fce9bba7730b031bff59c609830f3e072866 Mon Sep 17 00:00:00 2001 +From: Thomas Liske +Date: Mon, 4 Nov 2024 22:29:51 +0100 +Subject: [PATCH 5/5] interp: drop usage of Module::ScanDeps to prevent LPE + +--- + INSTALL.md | 1 - + README.Interp.md | 9 +++- + perl/Makefile.PL | 1 - + perl/lib/NeedRestart/Interp/Perl.pm | 70 ++++++++++++++++++++--------- + 4 files changed, 57 insertions(+), 24 deletions(-) + +--- a/INSTALL.md ++++ b/INSTALL.md +@@ -5,7 +5,6 @@ Perl + ---- + + - Module::Find +-- Module::ScanDeps + - Locale::TextDomain + - Proc::ProcessTable + - Sort::Naturally +--- a/README.Interp.md ++++ b/README.Interp.md +@@ -35,8 +35,13 @@ NeedRestart::Interp::Perl + Recognized binaries: /usr/(local/)?bin/perl + Find source file by: command line interpretation + +-We are using `Module::ScanDeps` to find used packages. This should work on +-any static loaded packages, dynamic stuff will fail. ++The source file is scanned only for 'use' lines, other module loading ++mechanisms will not be recognized. ++ ++*This function used the Module::ScanDeps package to get the used Perl packages ++until needrestart 3.7. Module::ScanDeps is not used any more as it seems not ++to be designed to work with untrustworthy perl sources which would allow an ++attacker to use needrestart for local privilege escalation.* + + + NeedRestart::Interp::Python +--- a/perl/Makefile.PL ++++ b/perl/Makefile.PL +@@ -6,7 +6,6 @@ WriteMakefile( + 'NAME' => 'NeedRestart', + 'PREREQ_PM' => { + Module::Find => 0, +- Module::ScanDeps => 0, + Proc::ProcessTable => 0, + Sort::Naturally => 0, + Term::ReadKey => 0. +--- a/perl/lib/NeedRestart/Interp/Perl.pm ++++ b/perl/lib/NeedRestart/Interp/Perl.pm +@@ -32,7 +32,6 @@ use Cwd qw(abs_path getcwd); + use Getopt::Std; + use NeedRestart qw(:interp); + use NeedRestart::Utils; +-use Module::ScanDeps; + + my $LOGPREF = '[Perl]'; + +@@ -48,6 +47,41 @@ sub isa { + return 0; + } + ++sub _scan($$$$$) { ++ my $debug = shift; ++ my $pid = shift; ++ my $src = shift; ++ my $files = shift; ++ my $path = shift; ++ ++ my $fh; ++ open($fh, '<', $src) || return; ++ # find used modules ++ my %modules = map { ++ (/^\s*use\s+([a-zA-Z][\w:]+)/ ? ($1 => 1) : ()) ++ } <$fh>; ++ close($fh); ++ ++ # track file ++ $files->{$src}++; ++ ++ # scan module files ++ if(scalar keys %modules) { ++ foreach my $module (keys %modules) { ++ # skip some well-known Perl pragmas ++ next if ($module =~ /^(constant|strict|vars|v5(\.\d+)?|warnings)$/); ++ ++ $module =~ s@::@/@g; ++ $module .= '.pm'; ++ ++ foreach my $p (@$path) { ++ my $fn = ($p ne '' ? "$p/" : '').$module; ++ &_scan($debug, $pid, $fn, $files, $path) if(!exists($files->{$fn}) && -r $fn && -f $fn); ++ } ++ } ++ } ++} ++ + sub source { + my $self = shift; + my $pid = shift; +@@ -160,31 +194,28 @@ sub files { + } + + # prepare include path environment variable +- my %e = nr_parse_env($pid); ++ my @path; + local %ENV; ++ ++ # get include path from env ++ my %e = nr_parse_env($pid); + if(exists($e{PERL5LIB})) { +- $ENV{PERL5LIB} = $e{PERL5LIB}; +- } +- elsif(exists($ENV{PERL5LIB})) { +- delete($ENV{PERL5LIB}); ++ @path = map { "/proc/$pid/root/$_"; } split(':', $e{PERL5LIB}); + } + +- @Module::ScanDeps::IncludeLibs = (exists($opts{I}) ? ($opts{I}) : ()); +- my $href; +- { +- # Silence warnings of Module::ScanDeps for dynamic loaded modules (github issue #41) +- local $SIG{__WARN__} = sub { }; ++ # get include path from @INC ++ my $plread = nr_fork_pipe($self->{debug}, $ptable->{exec}, '-e', 'print(join("\n", @INC));'); ++ push(@path, map { "/proc/$pid/root/$_"; } <$plread>); ++ close($plread); ++ chomp(@path); + +- $href = scan_deps( +- files => [$src], +- recurse => 1, +- ); +- } ++ my %files; ++ _scan($self->{debug}, $pid, $src, \%files, \@path); + + my %ret = map { +- my $stat = nr_stat("/proc/$pid/root/$href->{$_}->{file}"); +- $href->{$_}->{file} => ( defined($stat) ? $stat->{ctime} : undef ); +- } keys %$href; ++ my $stat = nr_stat("/proc/$pid/root/$_"); ++ $_ => ( defined($stat) ? $stat->{ctime} : undef ); ++ } keys %files; + + chdir($cwd); + diff -Nru needrestart-3.6/debian/patches/series needrestart-3.6/debian/patches/series --- needrestart-3.6/debian/patches/series 2023-11-15 20:05:37.000000000 +0000 +++ needrestart-3.6/debian/patches/series 2024-11-12 19:44:01.000000000 +0000 @@ -5,3 +5,8 @@ 05-fix-AMD-ucode-checking-in-non-debug-mode.patch 06-uCode-fix-uninitialized-value-in-logging-of-processo.patch 07-mark-unavailable-firmware-as-CURRENT.patch +core-prevent-race-condition-on-proc-PID-exec-evaluat.patch +interp-do-not-set-PYTHONPATH-environment-variable-to.patch +interp-do-not-set-RUBYLIB-environment-variable-to-pr.patch +interp-chdir-into-empty-directory-to-prevent-python-.patch +interp-drop-usage-of-Module-ScanDeps-to-prevent-LPE.patch