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
留言评论(旧系统):