http://weibo.com/u/1619411912 分析的结果,我稍加补充,形成如下文章,感觉蛮清晰的

这个问题是出现在password.c文件的check_scramble 函数中的标红位置,具体说明如下:

my_bool check_scramble(const uchar *scramble_arg, const char *message,
              const uint8 *hash_stage2)
{
 SHA1_CONTEXT sha1_context;
 uint8 buf[SHA1_HASH_SIZE];
 uint8 hash_stage2_reassured[SHA1_HASH_SIZE];

mysql_sha1_reset(&sha1_context);
 /* create key to encrypt scramble */
 mysql_sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH);
 mysql_sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE);
 mysql_sha1_result(&sha1_context, buf);
 /* encrypt scramble */
   my_crypt((char *) buf, buf, scramble_arg, SCRAMBLE_LENGTH);
 /* now buf supposedly contains hash_stage1: so we can get hash_stage2 */
 mysql_sha1_reset(&sha1_context);
 mysql_sha1_input(&sha1_context, buf, SHA1_HASH_SIZE);
 mysql_sha1_result(&sha1_context, hash_stage2_reassured);
 return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE);
}
memcmp(void *buf1, void *buf2, unsigned int count)

比较内存区域buf1和buf2的前count个字节

当buf1<buf2时,返回值<0

当buf1=buf2时,返回值=0

当buf1>buf2时,返回值>0

返回值为int型,计算时是buf1的第一个字符减buf2的第一个字符,若差为0再继续比较下个字符,差不为0返回差值

一般该值应该在[127,-128]之间的

但是现在的linxu glibc是经过sse or sse2优化的,为了提高内存访问速度做了对内存对齐操作,浪费了一些空间但是提高了速度,这样一来,读取的是4的整数倍地址

这样的话,返回值就不一定在[127,-128]之间了,

再看该函数的返回值,mysql 中my_bool是被定义为char型

typedef char my_bool

这样的话,就会出现文章中提到的举例,比如memcmp返回为0x200的时候,做int to char转型的时候,就会被截断为0x0,这样就导致check_scramble()返回值为0,误返回密码为匹配,致使登录成功!

触发成功一方面是概率事件,另一方面看有说在编译mysql时加入-fno-builtin参数也有关,具体有时间验证一下,所以说所谓的256次能命中一次也是需要先决条件的

补充:

由于要确保最后一个字节差值为0,所以就是要确保密码做完sha1散列后最后一字节的值相等,可能取值为(00-ff),256种可能。

mysql密码对比过程中会生成随机码来生成sha1散列,由于生成散列的函数保证是平均分布的,所以,发生末尾相等的概率也是1/256。

只要对mysql用同一帐户同一密码不停尝试登陆,在一定次数下就达到利用条件,成功利用。

转自:http://zone.wooyun.org/content/385

留言评论(旧系统):

晴天小铸 @ 2012-06-13 18:27:37

大家溢出吧 msf更新了 https://community.rapid7.com/community/metasploit/blog/2012/06/11/cve-2012-2122-a-tragically-comedic-security-flaw-in-mysql

本站回复:

╮(╯_╰)╭