作者:XiWang
原文地址:http://kqueue.org/blog/2012/06/25/more-randomness-or-less/
CVE-2006-0166是一个将未初始化内存用于随机数生成的名声狼藉的例子。一个Debian维护者注掉了两行代码来使Valgrind闭嘴,它抱怨未初始化内存的使用是熵的一个额外来源,这个改变使得OpenSSL在基于Debian的系统上生成伪键值(boguskey)。
事实上,使用未初始化内存总是一个非常坏的主意,不仅因为它迷惑了开发者以及像Valgrind的工具,而且因为一个睿智的C编译器将公然反对你。下面是FreeBSD与Mac OSX libc中的一个例子。
函数srandomdev()用于对random()播种,根据其manpage,“适合密码用途”。它将首先尝试/dev/random,在FreeBSD与Mac OSX上它是非阻塞的;如果失败,它退而求次,以某些令人惊奇的额外比特,使用当前时间与pid。
libc/stdlib/random.c link
struct timeval tv; unsigned long junk;
gettimeofday(&tv, NULL); srandom((getpid() << 16) ^ tv.tv_sec ^ tv.tv_usec ^ junk); |
你可以看到使用与未初始化栈变量junk混合的gettimeofday与getpid()的结果来计算种子。下面是来自MacOS X 10.6(SnowLeopard)的对应汇编代码。
/usr/lib/libSystem.B.dylib (10.6)
leaq 0xe0(%rbp),%rdi xorl %esi,%esi callq 0x001422ca ; symbol stub for: _gettimeofday callq 0x00142270 ; symbol stub for: _getpid movq 0xe0(%rbp),%rdx movl 0xe8(%rbp),%edi xorl %edx,%edi shll $0x10,%eax xorl %eax,%edi xorl %ebx,%edi callq 0x00142d68 ; symbol stub for: _srandom |
目前为止一切正常。让我们看一下来自10.7(Lion)与10.8(MountainLion)同样的代码。
/usr/lib/system/libsystem_c.dylib (10.7/10.8)
leaq 0xd8(%rbp),%rdi xorl %esi,%esi callq 0x000a427e ; symbol stub for: _gettimeofday callq 0x000a3882 ; symbol stub for: _getpid callq 0x000a4752 ; symbol stub for: _srandom |
等一下,整个种子计算没了!gettimeofday()与getpid()的结果完全没有使用;使用了某些“垃圾”值来调用srandom()。
我猜Apple从GCC转到LLVM来编译新的MacOS X中的libc。因为这个C代码包含未定义行为,未初始化变量junk的使用,LLVM更为进取地优化掉了这个计算。如果使用LLVM编译FreeBSD,你应该看到相同的汇编。下面是几个要探索的有趣的问题。
· 在FreeBSD及Mac OSX上/dev/random以某种方式失败,有可能触发这吗?
· 现在使用LLVM编译的srandom()种子到底有多随机,它只是%edi的值?看起来它是栈变量tv地址的低32比特。
· GCC会产生“正确的”代码吗?最后的xorl指令使用%ebx,它不太可能对应junk的值,而是/dev/random的文件描述符。
movl %ebx,%edi callq 0x00141d48 ; symbol stub for: _close$NOCANCEL |
总之,不要因为更多的随机性而使用未初始化的内存。
Junk的使用在1997年引入。Google显示了一组类似的使用与修改。
· Sranddev()共享相同的后备代码。
· DragonFly BSD开发者对junk添加了注释“XXXleft uninitialized on purpose”。
· 在2005年,OpenBDS开发者删除了这个代码。
· Varnish开发者修正了一个类似的使用,虽然这个代码仍然被其他人使用。顺便说一下,在那里你可以找到一个有趣的比较fd>= 0,其中fd是一个指针!