今天跟sys_mount系统调用发现在2.6.36内核下没有直接定义sys_mount而是用SYSCALL_DEFINE5来定义,于是在网上搜了一下找到了下面的资料(红字部分为网上资料):
CVE-2010-3301是其中一个。这个漏洞的成因是,在64位的内核上执行32位的系统调用时,作为传递系统调用号的%rax高32位未被清零处理,而且在进行比较的时候直接使用的%eax,导致高32位被忽略:
cmpl $(IA32_NR_syscalls-1),%eax
ja ia32_badsys
ia32_do_call:
IA32_ARG_FIXUP
call *ia32_sys_call_table(,%rax,8)
这样以来,通过静心构造的%rax就可以跳转到它想要的位置去!在这个exploit中,它就利用ptrace()来跟踪系统调用,并把计算好的想要跳转地址的偏移传递到%rax中,然后执行事先放置好的代码来提升权限!
修复方法很简单,要么把%rax的高位清零,要么比较的时候使用%rax。修复这个问题的commit是:
http://git.kernel.org/linus/36d001c70d8a0144ac1d038f6876c484849a74de
http://git.kernel.org/linus/eefdca043e8391dcd719711716492063030b55ac
和这个问题类似的问题之前也曾出现过,CVE-2009-0029,问题更严重,涉及很多的系统调用。不同的是,这个涉及64位的内核和64位的用户空间,来自用户空间的传递系统调用参数的寄存器的高32位同样没被清零,而带32位参数(比如int)的系统调用就会有问题,内核代码只会检查对它有意义的低32位,高32位就被忽略而直接传递到后面去了,这就会带来问题了。
问题的解决方法也很简单,就是要把这些寄存器高位清零。说起来简单,做起来难。要是和上面一样直接用汇编处理的话,参数的类型的信息就丢失了,因为你汇编里分不清它到底是32位还是64位;而如果用C处理的话,有那么多系统调用,一个一个处理