Metasploit Module: Xorg X11 Server SUID logfile Privilege Escalation
Type: exploit
Path: modules/exploits/multi/local/xorg_x11_suid_server.rb
Platform: unix
Disclosure date: 2018-10-25
References: CVE-2018-14665, BID-105741, EDB-45697, EDB-45742, EDB-45832, URL-https://www.securepatterns.com/2018/10/cve-2018-14665-xorg-x-server.html, URL-https://github.com/0xdea/exploits/blob/master/openbsd/raptor_xorgasm
Targets: OpenBSD, Linux x64, Linux x86

Description:
This module attempts to gain root privileges with SUID Xorg X11 server
          versions 1.19.0 < 1.20.3.

          A permission check flaw exists for -modulepath and -logfile options when
          starting Xorg. This allows unprivileged users that can start the server
          the ability to elevate privileges and run arbitrary code under root
          privileges.

          This module has been tested with OpenBSD 6.3, 6.4, CentOS 7.4.1708, and
          CentOS 7.5.1804, and RHEL 7.5. The default PAM configuration for CentOS
          and RHEL systems requires console auth for the user's session to start
          the Xorg server.

          Cron launches the payload, so if SELinux is enforcing, exploitation
          may still be possible, but the module will bail.

          Xorg must have SUID permissions and may not start if already running.

          On exploitation a crontab.old backup file will be created by Xorg.
          This module will remove the .old file and restore crontab after
          successful exploitation. Failed exploitation may result in a corrupted
          crontab. On successful exploitation artifacts will be created consistant
          with starting Xorg and running a cron.

Check method (fingerprinting):
# linux checks
    uname = cmd_exec('uname')
    if uname =~ /linux/i
      vprint_status 'Running additional check for Linux'
      if datastore['ConsoleLock']
        user = cmd_exec('id -un')
        unless exist?("/var/run/console/#{user}")
          vprint_error("No console lock for #{user}")
          return CheckCode::Safe("No console lock for #{user}")
        end
        vprint_good("Console lock for #{user}")
      end

      return CheckCode::Safe('SELinux is enforcing') if selinux_installed? && selinux_enforcing?
    end

    # suid program check
    xorg_path = cmd_exec('command -v Xorg')
    unless xorg_path.include?('Xorg')
      return CheckCode::Safe('Could not find Xorg executable')
    end

    vprint_good("Xorg path found at #{xorg_path}")

    unless setuid?(xorg_path)
      return CheckCode::Safe("Xorg binary #{xorg_path} is not SUID")
    end

    vprint_good("Xorg binary #{xorg_path} is SUID")

    # version check
    x_version = cmd_exec 'Xorg -version'
    if x_version.include?('Release Date')
      v = Rex::Version.new(x_version.scan(/\d\.\d+\.\d+/).first)
      unless v.between?(Rex::Version.new('1.19.0'), Rex::Version.new('1.20.2'))
        vprint_error "Xorg version #{v} not supported"
        return CheckCode::Safe("Xorg version #{v} is not in the vulnerable range")
      end
    elsif x_version.include?('Fatal server error')
      vprint_error 'User probably does not have console auth'
      vprint_error 'Below is Xorg -version output'
      vp