-
Notifications
You must be signed in to change notification settings - Fork 14k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ubuntu needrestart LPE (CVE-2024-48990) #19676
base: master
Are you sure you want to change the base?
Conversation
version = cmd_exec('cat /etc/issue | cut -d " " -f 2').strip | ||
version = version.slice(0, 5) # take off any extra version info | ||
return CheckCode::Safe("Ubuntu version #{version} is not vulnerable") unless fixed_versions.key? version |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why couldn't those versions of Ubuntu run a vulnerable version of needrestart
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could get it running on every version of ubuntu by installing via source code. I'm not bothering with detecting the self-reported version number from the binary due to backporting. Plus its pre-installed on Ubuntu so someone installing a newer version via source code seems unlikely. I think this is good enough for the time being, but am open to PRs if theres a better way
package = cmd_exec('dpkg -l needrestart | grep \'^ii\'') | ||
package = package.split(' ')[2] | ||
package = package.gsub('+', '.') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't there a mixin to check if a package is installed and get its version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not that i'm aware of. took a quick look and didn't see any, but seems like a good idea. I coded out an Ubuntu based one. I'll throw it up in a few days once its more tested
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got one roughly built, coming in a PR shortly
vprint_status("Uploading payload: #{payload_path}") | ||
register_files_for_cleanup(payload_path) | ||
|
||
# our c stub file does our chmod/chown/suid for the payload |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be better to use metasploit's options/features to prepend the setuid call to the payload, instead of having it in the stub?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it may be possible, this was a copy of the PoC with no additions.
However, I'm not sure if it would work since the c_stub
is originally called by the python script itself. It fails to do the chmod
etc. The python stub then waits watching for our payload to get modified.
needrestart
is run by sudo/root/etc, which then runs our c_stub
, changes the permissions. It may be possible to modify c_stub
so that it executes the payload directly only if it detects itself running as root. That would take out some system complexity, but i may need some @zeroSteiner (or other r7) on updating the code to work in metasm (updated code coming soon).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like this can be refined some more, further testing will happen this week
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we may be able to launch the payload from the .so
file directly, but even w/ threading (prepend_thread
, and with &
) , it freezes needrestart
. Its a delicate tradeoff between the python script and .so
file, so I think this is a good strategy for now. We've already improved on the original PoC by cutting out the build file, and using metasm to avoid the need for gcc
/build-essential
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @h00die ! I just left a couple of comments for you to review when you get a chance.
package = cmd_exec('dpkg -l needrestart | grep \'^ii\'') | ||
package = package.split(' ')[2] | ||
package = package.gsub('+', '.') | ||
package = package.gsub('needrestart-', '') # fedora specific |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering, since the package version is retrieved by calling dpkg
, which is only available on Debian-like distributions, is this line relevant? I might be missing something though.
package = Rex::Version.new(package) | ||
return CheckCode::Safe('needrestart not install, or not detected.') if package.nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like Rex::Version.new(package)
doesn't return nil
in case of failure. Instead it raises an exception or returns Gem::Version.new("0")
if package
is an empty string or nil
.
Fixes #19675
Exploits needrestart on Ubuntu. Debian and Fedora put out patches, but after putting minor effort into testing them and a bunch of PoCs on github, I gave up trying to make it work. If someone wants to expand this module, be my guest. Happily working on Ubuntu though!
Verification
use exploit/linux/local/ubuntu_needrestart_lpe
set lhost <ip>
set lport <port>
set session <session>
run