{"id":60444,"date":"2024-11-22T21:51:35","date_gmt":"2024-11-22T18:51:35","guid":{"rendered":"https:\/\/packetstormsecurity.com\/files\/182765\/QSA-needrestart.txt"},"modified":"2024-11-22T21:51:35","modified_gmt":"2024-11-22T18:51:35","slug":"needrestart-local-privilege-escalation","status":"publish","type":"post","link":"https:\/\/afaghhosting.net\/blog\/needrestart-local-privilege-escalation\/","title":{"rendered":"needrestart Local Privilege Escalation"},"content":{"rendered":"<p>Qualys Security Advisory<\/p>\n<p>LPEs in needrestart (CVE-2024-48990, CVE-2024-48991, CVE-2024-48992,<br \/>CVE-2024-10224, and CVE-2024-11003)<\/p>\n<p>========================================================================<br \/>Contents<br \/>========================================================================<\/p>\n<p>Summary<br \/>Background<br \/>CVE-2024-48990 (and CVE-2024-48992)<br \/>CVE-2024-48991<br \/>CVE-2024-10224 (and CVE-2024-11003)<br \/>Mitigation<br \/>Acknowledgments<br \/>Timeline<\/p>\n<p>I got bugs<br \/>I got bugs in my room<br \/>Bugs in my bed<br \/>Bugs in my ears<br \/>Their eggs in my head<br \/>&#8212; Pearl Jam, &#8220;Bugs&#8221;<\/p>\n<p>========================================================================<br \/>Summary<br \/>========================================================================<\/p>\n<p>needrestart (from https:\/\/github.com\/liske\/needrestart) is a Perl tool<br \/>that is installed by default on Ubuntu Server since version 21.04. From<br \/>https:\/\/discourse.ubuntu.com\/t\/needrestart-changes-in-ubuntu-24-04-service-restarts:<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>What is needrestart, exactly?<\/p>\n<p>needrestart is a tool that probes your system to see if either the<br \/>system itself or some of its services should be restarted. That last<br \/>part is the one of interest in this document. Notably, a service is<br \/>considered as needing to be restarted if one of its processes is using<br \/>a shared library whose initial file isn&#8217;t on the system anymore (for<br \/>instance, if it has been overwritten by a new version as part of a<br \/>package update).<\/p>\n<p>We ship this tool in our server images, and it is configured by<br \/>default to run at the end of APT transactions, e.g. when doing apt<br \/>install\/upgrade\/remove or during unattended-upgrades.<br \/>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<\/p>\n<p>We discovered three fundamental vulnerabilities in needrestart (three<br \/>LPEs, Local Privilege Escalations, from any unprivileged user to full<br \/>root), which are exploitable without user interaction on Ubuntu Server<br \/>(through unattended-upgrades):<\/p>\n<p>&#8211; CVE-2024-48990: local attackers can execute arbitrary code as root by<br \/>tricking needrestart into running the Python interpreter with an<br \/>attacker-controlled PYTHONPATH environment variable.<\/p>\n<p>Last-minute update: an additional CVE, CVE-2024-48992, has been<br \/>assigned to needrestart because local attackers can also execute<br \/>arbitrary code as root by tricking needrestart into running the Ruby<br \/>interpreter with an attacker-controlled RUBYLIB environment variable.<\/p>\n<p>&#8211; CVE-2024-48991: local attackers can execute arbitrary code as root by<br \/>winning a race condition and tricking needrestart into running their<br \/>own, fake Python interpreter (instead of the system&#8217;s real Python<br \/>interpreter).<\/p>\n<p>&#8211; CVE-2024-10224: local attackers can execute arbitrary shell commands<br \/>as root by tricking needrestart into open()ing a filename of the form<br \/>&#8220;commands|&#8221; (technically, this vulnerability is in Perl&#8217;s ScanDeps<br \/>module, but it is unclear whether this module was ever meant to<br \/>operate on attacker-controlled files or not).<\/p>\n<p>Last-minute update: in the end, an additional CVE, CVE-2024-11003, has<br \/>been assigned to needrestart for calling Perl&#8217;s ScanDeps module with<br \/>attacker-controlled files.<\/p>\n<p>To the best of our knowledge, these vulnerabilities have existed since<br \/>the introduction of interpreter support in needrestart 0.8 (April 2014).<br \/>&gt;From https:\/\/github.com\/liske\/needrestart#interpreters:<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>needrestart 0.8 brings an interpreter scanning feature. Interpreters<br \/>not only map binary (shared) objects but also use plaintext source<br \/>files. The interpreter detection tries to check for outdated source<br \/>files since they may contain security issues, too. This is only a<br \/>heuristic and might fail to detect all relevant source files. The<br \/>following interpreter scanners are shipped:<\/p>\n<p>&#8211; NeedRestart::Interp::Java<br \/>&#8211; NeedRestart::Interp::Perl<br \/>&#8211; NeedRestart::Interp::Python<br \/>&#8211; NeedRestart::Interp::Ruby<br \/>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<\/p>\n<p>We will not publish our exploits for now; however, please note that<br \/>these vulnerabilities are trivially exploitable, and other researchers<br \/>might publish working exploits shortly after this coordinated release.<\/p>\n<p>========================================================================<br \/>Background<br \/>========================================================================<\/p>\n<p>And now the questions:<br \/>Do I kill them?<br \/>Become their friend?<br \/>Do I eat them?<br \/>&#8212; Pearl Jam, &#8220;Bugs&#8221;<\/p>\n<p>While idly watching an &#8220;apt-get upgrade&#8221; of one of our Ubuntu Servers,<br \/>we noticed a message that we had never noticed before: &#8220;Scanning<br \/>processes&#8230;&#8221;<\/p>\n<p>We immediately wondered: What is printing this message? Is it scanning<br \/>userland processes? As root? Even processes that do not belong to root?<\/p>\n<p>We quickly found out that this message is printed by needrestart, a tool<br \/>that scans the userland for processes that need to be restarted after a<br \/>package installation, upgrade, or removal. Naturally, needrestart scans<br \/>all userland processes as root, including unprivileged user processes;<br \/>i.e., possibly attacker-controlled processes.<\/p>\n<p>========================================================================<br \/>CVE-2024-48990 (and CVE-2024-48992)<br \/>========================================================================<\/p>\n<p>To determine whether a Python process (a process that is running the<br \/>Python interpreter) needs to be restarted, needrestart extracts the<br \/>PYTHONPATH environment variable from this process&#8217;s \/proc\/pid\/environ<br \/>(at line 193), sets this environment variable if it exists (at line<br \/>196), and executes Python (&#8220;$ptable-&gt;{exec}&#8221; at line 203) with a &#8220;-&#8220;<br \/>argument to read a short, hard-coded script from stdin (at line 204):<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>135 sub files {<br \/>136 my $self = shift;<br \/>137 my $pid = shift;<br \/>138 my $cache = shift;<br \/>139 my $ptable = nr_ptable_pid($pid);<br \/>&#8230;<br \/>193 my %e = nr_parse_env($pid);<br \/>194 local %ENV;<br \/>195 if(exists($e{PYTHONPATH})) {<br \/>196 $ENV{PYTHONPATH} = $e{PYTHONPATH};<br \/>197 }<br \/>&#8230;<br \/>203 my ($pyread, $pywrite) = nr_fork_pipe2($self-&gt;{debug}, $ptable-&gt;{exec}, &#8216;-&#8216;);<br \/>204 print $pywrite &#8220;import sys\\nprint(sys.path)\\n&#8221;;<br \/>205 close($pywrite);<br \/>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<\/p>\n<p>Unfortunately, if a Python process belongs to a local attacker, then<br \/>needrestart executes Python (at line 203) with an attacker-controlled<br \/>PYTHONPATH environment variable, which allows the attacker to execute<br \/>arbitrary code as root (even though needrestart&#8217;s hard-coded Python<br \/>script at line 204 is not attacker-controlled at all). This is<br \/>CVE-2024-48990.<\/p>\n<p>For example, in our exploit we run a simple Python process (which<br \/>sleep()s forever) with a &#8220;PYTHONPATH=\/home\/jane&#8221; environment variable,<br \/>and plant a shared library &#8220;importlib\/__init__.so&#8221; in our \/home\/jane.<br \/>As soon as needrestart executes Python with our PYTHONPATH environment<br \/>variable (at line 203), our shared library is executed (by Python&#8217;s<br \/>initialization code) and creates a SUID-root shell in \/home\/jane.<\/p>\n<p>Note: needrestart&#8217;s support code for the Ruby interpreter seems equally<br \/>vulnerable, but we have not investigated this any further, because<br \/>(unlike Python) Ruby is not installed by default on Ubuntu Server.<\/p>\n<p>Last-minute update: we have now confirmed that needrestart&#8217;s support<br \/>code for the Ruby interpreter is indeed vulnerable and exploitable,<br \/>through an attacker-controlled RUBYLIB environment variable and an<br \/>&#8220;enc\/encdb.so&#8221; shared library. This is CVE-2024-48992.<\/p>\n<p>========================================================================<br \/>CVE-2024-48991<br \/>========================================================================<\/p>\n<p>To determine whether a process is indeed a Python process (a process<br \/>that is running the Python interpreter, for example \/usr\/bin\/python3),<br \/>needrestart reads this process&#8217;s \/proc\/pid\/exe (at line 520), and then<br \/>matches it against the regular expression at line 45:<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>520 my $exe = nr_readlink($pid);<br \/>&#8230;<br \/>606 $restart++ if(needrestart_interp_check($nrconf{verbosity} &gt; 1, $pid, $exe, $nrconf{blacklist_interp}, $opt_t));<br \/>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>166 sub needrestart_interp_check($$$$$) {<br \/>167 my $debug = shift;<br \/>168 my $pid = shift;<br \/>169 my $bin = shift;<br \/>170 my $blacklist = shift;<br \/>171 my $tolerance = shift;<br \/>&#8230;<br \/>176 if($interp-&gt;isa($pid, $bin)) {<br \/>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>40 sub isa {<br \/>41 my $self = shift;<br \/>42 my $pid = shift;<br \/>43 my $bin = shift;<br \/>44 <br \/>45 return 1 if($bin =~ m@^\/usr\/(local\/)?bin\/python([23][.\\d]*)?$@);<br \/>46 <br \/>47 return 0;<br \/>48 }<br \/>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<\/p>\n<p>In fact, this code used to be vulnerable to CVE-2022-30688, a Local<br \/>Privilege Escalation reported by Jakub Wilk: the regular expression at<br \/>line 45 used to be unanchored (i.e., &#8220;\/usr\/(local\/)?bin\/python&#8221; instead<br \/>of &#8220;^\/usr\/(local\/)?bin\/python([23][.\\d]*)?$&#8221;), so local attackers could<br \/>simply run their own, fake &#8220;\/home\/jane\/usr\/bin\/python&#8221; (for example) and<br \/>needrestart would later execute this fake Python interpreter as root (as<br \/>if it were the system&#8217;s real Python interpreter, at line 203).<\/p>\n<p>We tried to bypass the fixed, anchored regular expression at line 45,<br \/>but we failed. However, we eventually realized that the filename that is<br \/>checked at line 45 is not necessarily the same filename that is executed<br \/>at line 203: the filename that is checked is read from \/proc\/pid\/exe in<br \/>the middle of needrestart&#8217;s main loop (at line 520), but the filename<br \/>that is executed (&#8220;$ptable-&gt;{exec}&#8221; at line 203) was first read from<br \/>\/proc\/pid\/exe long before needrestart entered its main loop.<\/p>\n<p>In other words, needrestart is vulnerable to a TOCTOU race condition<br \/>(time-of-check, time-of-use). For example, our exploit \/home\/jane\/race<br \/>waits for needrestart to read our \/proc\/pid\/exe for the first time (we<br \/>use inotify to reliably win this race), and then quickly execve()s the<br \/>system&#8217;s real Python interpreter with a script that simply sleep()s for<br \/>some time. As a result, needrestart does its checks on the real Python<br \/>interpreter, but executes our own \/home\/jane\/race instead, as root.<\/p>\n<p>Note: needrestart&#8217;s support code for the Ruby interpreter seems equally<br \/>vulnerable, but we have not investigated this any further.<\/p>\n<p>========================================================================<br \/>CVE-2024-10224 (and CVE-2024-11003)<br \/>========================================================================<\/p>\n<p>After we had discovered CVE-2024-48990 and CVE-2024-48991 in<br \/>needrestart&#8217;s support code for the Python interpreter (and Ruby), we<br \/>began to wonder whether the support code for the Perl interpreter might<br \/>also be vulnerable to a Local Privilege Escalation.<\/p>\n<p>Unlike needrestart&#8217;s support code for Python and Ruby, the support code<br \/>for Perl does not execute the Perl interpreter itself: instead, it calls<br \/>the scan_deps() function from Perl&#8217;s ScanDeps module, which analyzes a<br \/>Perl script by recursively reading its source files.<\/p>\n<p>We therefore grepped the ScanDeps module for one of the oldest pitfalls<br \/>of the Perl programming language: the two-argument form of open(), which<br \/>allows attackers to execute arbitrary shell commands if they control the<br \/>name of the file to be open()ed (for example, &#8220;commands|&#8221;). For more<br \/>information, please refer to rain.forest.puppy&#8217;s 1999 Phrack article<br \/>(&#8220;That pesky pipe&#8221; section) and the SEI CERT Perl Coding Standard:<\/p>\n<p>https:\/\/phrack.org\/issues\/55\/7.html#article<br \/>https:\/\/wiki.sei.cmu.edu\/confluence\/pages\/viewpage.action?pageId=88890543<\/p>\n<p>Incredibly, we found a match, at line 871 in ScanDeps.pm:<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>868 sub scan_file{<br \/>869 my $file = shift;<br \/>870 my %found;<br \/>871 open my $fh, $file or die &#8220;Cannot open $file: $!&#8221;;<br \/>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<\/p>\n<p>In our exploit, we simply run a Perl script named &#8220;\/home\/jane\/perl|&#8221;<br \/>(which sleep()s forever), and as soon as needrestart calls scan_deps()<br \/>to analyze our script, &#8220;\/home\/jane\/perl|&#8221; is open()ed (at line 871), but<br \/>because this filename ends with a &#8220;|&#8221; it is treated as a shell command,<br \/>and our own &#8220;\/home\/jane\/perl&#8221; is executed instead, as root.<\/p>\n<p>Last-minute update: while reviewing needrestart&#8217;s patches for these<br \/>vulnerabilities, we have discovered that Perl&#8217;s ScanDeps module is also<br \/>trivially exploitable through various calls to eval() (&#8220;string&#8221; eval()s,<br \/>https:\/\/perldoc.perl.org\/functions\/eval). Consequently and impressively,<br \/>in response to our advisory:<\/p>\n<p>&#8211; all of ScanDeps&#8217;s vulnerable calls to open() and eval() have been<br \/>patched, thus fixing CVE-2024-10224;<\/p>\n<p>&#8211; needrestart&#8217;s dependence on ScanDeps has been completely removed (it<br \/>uses a simple regex-based approach now), thus fixing CVE-2024-11003.<\/p>\n<p>========================================================================<br \/>Mitigation<br \/>========================================================================<\/p>\n<p>As already recommended by needrestart&#8217;s advisory for CVE-2022-30688<br \/>(from https:\/\/www.openwall.com\/lists\/oss-security\/2022\/05\/17\/9):<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br \/>Disabling the interpreter heuristic in needrestart&#8217;s config prevents<br \/>this attack:<\/p>\n<p># Disable interpreter scanners.<br \/>$nrconf{interpscan} = 0;<br \/>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<\/p>\n<p>========================================================================<br \/>Acknowledgments<br \/>========================================================================<\/p>\n<p>We thank needrestart&#8217;s maintainer (Thomas Liske), Module::ScanDeps&#8217;s<br \/>maintainers (Roderich Schupp in particular), the Ubuntu Security Team<br \/>(Mark Esler in particular), and distros@openwall (Salvatore Bonaccorso<br \/>from the Debian Security Team in particular) for their outstanding work;<br \/>it has been a real pleasure to collaborate on this coordinated release.<\/p>\n<p>We also thank Adam Boileau (@metlstorm) and Rodrigo Branco (@bsdaemon)<br \/>for their very kind words about our work; they mean the world to us:<\/p>\n<p>https:\/\/risky.biz\/RB755\/<br \/>https:\/\/phrack.org\/issues\/71\/2.html#article<\/p>\n<p>========================================================================<br \/>Timeline<br \/>========================================================================<\/p>\n<p>2024-10-04: We sent our advisory and exploits to the Ubuntu Security<br \/>Team, and asked them if they could help us to coordinate this disclosure<br \/>with the upstream projects and distros@openwall; they gladly accepted.<\/p>\n<p>2024-10-08: The Ubuntu Security Team sent our advisory and exploits to<br \/>needrestart&#8217;s maintainer; we then started a very constructive exchange<br \/>of patches and patch reviews.<\/p>\n<p>2024-10-18: The Ubuntu Security Team opened GHSA-g597-359q-v529, a<br \/>private GitHub repository to collaborate on this disclosure with<br \/>Module::ScanDeps&#8217;s maintainers.<\/p>\n<p>2024-11-11: The Ubuntu Security Team sent our advisory and all of<br \/>needrestart&#8217;s and Module::ScanDeps&#8217;s patches to distros@openwall.<\/p>\n<p>2024-11-19: Coordinated Release Date (16:00 UTC).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Qualys Security Advisory LPEs in needrestart (CVE-2024-48990, CVE-2024-48991, CVE-2024-48992,CVE-2024-10224, and CVE-2024-11003) ========================================================================Contents======================================================================== SummaryBackgroundCVE-2024-48990 (and CVE-2024-48992)CVE-2024-48991CVE-2024-10224 (and CVE-2024-11003)MitigationAcknowledgmentsTimeline I got bugsI got bugs in my roomBugs in my bedBugs in my earsTheir eggs in my head&#8212; Pearl Jam, &#8220;Bugs&#8221; ========================================================================Summary======================================================================== needrestart (from https:\/\/github.com\/liske\/needrestart) is a Perl toolthat is installed by default on Ubuntu Server since version &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[],"class_list":["post-60444","post","type-post","status-publish","format-standard","hentry","category-vulnerability"],"_links":{"self":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/60444","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/comments?post=60444"}],"version-history":[{"count":0,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/posts\/60444\/revisions"}],"wp:attachment":[{"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/media?parent=60444"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/categories?post=60444"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/afaghhosting.net\/blog\/wp-json\/wp\/v2\/tags?post=60444"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}