Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2977417
  • 博文数量: 401
  • 博客积分: 12926
  • 博客等级: 上将
  • 技术积分: 4588
  • 用 户 组: 普通用户
  • 注册时间: 2009-02-22 14:51
文章分类

全部博文(401)

文章存档

2015年(16)

2014年(4)

2013年(12)

2012年(82)

2011年(98)

2010年(112)

2009年(77)

分类: LINUX

2011-04-26 15:01:37

从我出去旅行到现在这段时间内,Linux内核似乎是漏洞频频啊,光我看到的安全漏洞就已经若干个了,有机会真想八一八这些安全漏洞。

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就可以跳转到它想要的位置去!在中,它就利用ptrace()来跟踪系统调用,并把计算好的想要跳转地址的偏移传递到%rax中,然后执行事先放置好的代码来提升权限!

修复方法很简单,要么把%rax的高位清零,要么比较的时候使用%rax。修复这个问题的commit是:

和这个问题类似的问题之前也曾出现过,CVE-2009-0029,问题更严重,涉及很多的系统调用。不同的是,这个涉及64位的内核和64位的用户空间,来自用户空间的传递系统调用参数的寄存器的高32位同样没被清零,而带32位参数(比如int)的系统调用就会有问题,内核代码只会检查对它有意义的低32位,高32位就被忽略而直接传递到后面去了,这就会带来问题了。

问题的解决方法也很简单,就是要把这些寄存器高位清零。说起来简单,做起来难。要是和上面一样直接用汇编处理的话,参数的类型的信息就丢失了,因为你汇编里分不清它到底是32位还是64位;而如果用C处理的话,有那么多系统调用,一个一个处理?那不符合Linus的作风!他是怎么做的呢?用宏!而且用强制转化,把所有的32位参数声明为long,然后再强制转化成实际的类型,比如int。去看看__SC_CASTx()和__SC_LONGx()的定义就知道了:

C:
  1. #define __SC_CAST1(t1, a1)      (t1) a1
  2. #define __SC_LONG1(t1, a1)      long a1
  3.  
  4. #define __SYSCALL_DEFINEx(x, name, ...)                                 \
  5.         asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__));           \
  6.         static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__));       \
  7.         asmlinkage long SyS##name(__SC_LONG##x(__VA_ARGS__))            \
  8.         {                                                               \
  9.                 __SC_TEST##x(__VA_ARGS__);                              \
  10.                 return (long) SYSC##name(__SC_CAST##x(__VA_ARGS__));    \
  11.         }                                                               \
  12.         SYSCALL_ALIAS(sys##name, SyS##name);                            \
  13.         static inline long SYSC##name(__SC_DECL##x(__VA_ARGS__))

可见Linus大神把宏用到了何等出神入化的地步。:-) 这也是为什么你在内核中看到系统调用都是用SYSCALL_DEFINEx()来定义了。

阅读(4867) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~