Description:
A. sql/sql_parce.cc:877:
-----
uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
*passwd++ : strlen(passwd);
-----
B. sql/sql_acl.cc:650:
-----
/* check password: it should be empty or valid */
if (passwd_len == acl_user_tmp->salt_len)
{
if (acl_user_tmp->salt_len == 0 ||
(acl_user_tmp->salt_len == SCRAMBLE_LENGTH ?
check_scramble(passwd, thd->scramble, acl_user_tmp->salt) :
check_scramble_323(passwd, thd->scramble,
(ulong *) acl_user_tmp->salt)) == 0)
{
-----
C. sql/password.c:208 (check_scramble_323):
-----
[skip]
char buff[16],*to,extra; /* Big enough for check */
const char *pos;
[skip]
to=buff;
for (pos=scrambled ; *pos ; pos++)
*to++=(char) (floor(my_rnd(&rand_st)*31)+64);
-----
In A, if CLIENT_SECURE_CONNECTION is enabled and passwd[0]==SCRAMBLE_LENGTH_323, then check_scramble_323() will be called. But the password string may not be null-terminated in this case. Therefore, there will be buffer overflow in C.
Note that this affects only users which have old-hashed passwords. Attacker should know only a name of that user.
Sorry for my English, please.
How to repeat:
Create a user with old-style password. Apply this patch to libmysqlclient and run 'mysql --user=username --password=anyword':
diff -r mysql-4.1.7.orig/sql-common/client.c mysql-4.1.7/sql-common/client.c
1517c1517
< char buff[NAME_LEN+USERNAME_LENGTH+100];
---
> char buff[NAME_LEN+USERNAME_LENGTH+100+512];
2002,2004c2002,2007
< *end++= SCRAMBLE_LENGTH;
< scramble(end, mysql->scramble, passwd);
< end+= SCRAMBLE_LENGTH;
---
> *end++= SCRAMBLE_LENGTH_323;
> memset(end,0xff,512);
> end+=512;
> /* *end++= SCRAMBLE_LENGTH; */
> /* scramble(end, mysql->scramble, passwd); */
> /* end+= SCRAMBLE_LENGTH; */
Suggested fix:
More strong checking of a client data is needed.